学到Part6,用Java重写了解释器。
把这几个类拆开放了,共三个文件:
src
├── Interpreter.java
├── Lexer.java
└── Token.java
Token.java
public class Token {
private String type;
private String value;
public Token(String type, String value) {
this.type = type;
this.value = value;
}
public String getType() {
return type;
}
public String getValue() {
return value;
}
@Override
public String toString() {
return "Token{" +
"type='" + type + '\'' +
", value='" + value + '\'' +
'}';
}
}
Lexer.java (词法分析器)
import com.sun.xml.internal.ws.api.message.ExceptionHasMessage;
/**
* 词法解析器:输入字符串->token
*/
public class Lexer{
private String text;
private int pos=0;
private char current_char;
public Lexer(String text) {
this.text = text;
this.current_char = text.charAt(this.pos);
}
private void error() throws InvalidCharException{
throw new InvalidCharException("Invalid character");
}
/**
* 字符串的解析前进一个字符
*/
private void advance(){
this.pos += 1;
if(this.pos > this.text.length()-1){
this.current_char = '\0';
}else{
this.current_char = this.text.charAt(pos);
}
}
/**
* 跳过空格
*/
private void skip_whitespace(){
if(this.current_char!='\0' && Character.isSpaceChar(this.current_char)){
advance();
}
}
/**
* 一/多个字符->整数
* @return
*/
private String integer(){
String result = "";
while(this.current_char != '\0' && Character.isDigit(this.current_char)){
result += this.current_char;
advance();
}
return result;
}
/**
* 获取下一个token,并且使得pos加一
* @return
*/
public Token get_next_token() throws InvalidCharException {
while(this.current_char != '\0' ){
// 判断当前字符是空格
if(Character.isSpaceChar(this.current_char)){
//跳过空格
skip_whitespace();
//然后继续循环
continue;
}else if(Character.isDigit(this.current_char)){ // 如果当前字符是整数
return new Token(Interpreter.INTEGER,integer());
}else if(this.current_char == '+'){// 如果当前字符是+
// 前进一步
advance();
//然后返回当前字符对应的token
return new Token(Interpreter.PLUS,"+");
}else if(this.current_char == '-'){// 如果当前字符是-
// 前进一步
advance();
//然后返回当前字符对应的token
return new Token(Interpreter.MINUS,"-");
}else if(this.current_char == '*'){// 如果当前字符是*
// 前进一步
advance();
//然后返回当前字符对应的token
return new Token(Interpreter.MUL,"*");
}else if(this.current_char == '/'){// 如果当前字符是/
// 前进一步
advance();
//然后返回当前字符对应的token
return new Token(Interpreter.DIV,"/");
}else if(this.current_char == '('){// 如果当前字符是(
// 前进一步
advance();
//然后返回当前字符对应的token
return new Token(Interpreter.LPAREN,"(");
}else if(this.current_char == ')'){// 如果当前字符是)
// 前进一步
advance();
//然后返回当前字符对应的token
return new Token(Interpreter.RPAREN,")");
}else{
error();
}
}
return new Token(Interpreter.EOF,"EOF");
}
public class InvalidCharException extends Exception{
public InvalidCharException(String message) {
super(message);
}
}
}
Interpreter.java(语法分析器和解释器)
import java.util.Scanner;
public class Interpreter {
// expr : term ((PLUS | MINUS)term)*
// term : factor ((MUL|DIV)factor)*
// factor : INTEGER | LPAREN expr RPAREN
private Lexer lexer;
private Token current_token;
public final static String PLUS = "PLUS";
public final static String MINUS = "MINUS";
public final static String MUL = "MUL";
public final static String DIV = "DIV";
public final static String LPAREN = "(";
public final static String RPAREN = ")";
public final static String INTEGER = "INTEGER";
public final static String EOF = "EOF";
public Interpreter(Lexer lexer) throws Lexer.InvalidCharException {
this.lexer = lexer;
this.current_token = lexer.get_next_token();
}
/**
* 抛出语法异常
* @throws InvalidSyntaxException
*/
public void error() throws InvalidSyntaxException {
throw new InvalidSyntaxException("Invalid syntax");
}
public class InvalidSyntaxException extends Exception{
public InvalidSyntaxException(String message) {
super(message);
}
}
private void eat(String token_type) throws InvalidSyntaxException, Lexer.InvalidCharException {
// 如果当前token的type和传入的type确实一样,那么就获取下一个token
if(this.current_token.getType() == token_type){
this.current_token = this.lexer.get_next_token();
}else{
throw new InvalidSyntaxException("Invalid Syntax");
}
}
/**
* factor : INTEGER | LPAREN expr RPAREN
* @return 基本单元
*/
private int factor() throws InvalidSyntaxException, Lexer.InvalidCharException {
int result=0;
if(this.current_token.getType() == INTEGER){
// 需要先取出当前INTEGER类型的token,否则下面eat之后的return返回的就是INTEGER的下一个token了
Token token = this.current_token;
eat(INTEGER);
result = Integer.valueOf(token.getValue());
}else if(this.current_token.getType() == LPAREN){
eat(LPAREN);
result = expr();
eat(RPAREN);
}
return result;
}
/**
* term : factor ((MUL|DIV)factor)*
* @return 中间计算结果
*/
private int term() throws InvalidSyntaxException, Lexer.InvalidCharException {
// 获取第一个factor
int result = factor();
// ()* -> 循环
while(this.current_token.getType() == MUL || this.current_token.getType() == DIV){
// | -> if-else
if(this.current_token.getType() == MUL){
eat(MUL);
result *= factor();
}else if(this.current_token.getType() == DIV){
eat(DIV);
result /= factor();
}
}
return result;
}
//
/**
* expr : term ((PLUS | MINUS)term)*
*
* calc> 7 + 3 * (10 / (12 / (3 + 1) - 1))
* 22
*
* @return 最终计算结果
*/
private int expr() throws InvalidSyntaxException, Lexer.InvalidCharException {
// 获取第一个term
int result = term();
// ()* -> 循环
while(this.current_token.getType() == PLUS || this.current_token.getType() == MINUS){
// | -> if-else
if(this.current_token.getType() == PLUS){
// 吃掉当前token,设置this.current_token为下一个token
eat(PLUS);
// 加上第2个term
result += term();
}else if(this.current_token.getType() == MINUS){
// 吃掉当前token,设置this.current_token为下一个token
eat(MINUS);
// 减去第2个term
result -= term();
}
}
return result;
}
public static void main(String[] args) throws Lexer.InvalidCharException, InvalidSyntaxException {
Scanner sc = new Scanner(System.in);
while(true){
String text = "";
try{
System.out.print("calc> ");
text = sc.nextLine();
}catch (Exception e){
break;
}
if(text.equals("")){
continue;
}
if(text.equals("exit")){
break;
}
// 将输入字符串转换为token
Lexer lexer = new Lexer(text);
// 将lexer输入解释器,new一个解释器
Interpreter interpreter = new Interpreter(lexer);
//执行解释器的expr,得到结果
int result = interpreter.expr();
// 打印结果
System.out.println(result);
}
sc.close();
}
}
测试
calc> 7 + 3 * (10 / (12 / (3 + 1) - 1))
22
calc>
calc> 2
2
calc>