只是打印数字还不够直观,这个时候我们需要字符串变量来展示,如 打印a的值为:1,所以需要添加字符串变量,同时字符串可以通过+连接,也可以将数字和字符串连接 , 用单引号‘hello world’来表示字符串。
回顾上一个语法图 , 现在用QUOTE表示单引号’,增加语法节点stringAst : QUOTE variableAst QUOTE 。
programAst : (assignAst | printCallAst)*
assignAst : variableAst ASSIGN arithAst
printCallAst : print LBRACKET arithAst RBRACKET
arithAst : termAst ((PLUS|MINUS) termAst)*
termAst : factorAst ((MUL|DIV) factorAst)*
factorAst: INTEGER | LBRACKET arithAst RBRACKET | variableAst
新的语法图
programAst : (assignAst | printCallAst)*
assignAst : variableAst ASSIGN arithAst
printCallAst : print LBRACKET arithAst RBRACKET
arithAst : termAst ((PLUS|MINUS) termAst)*
termAst : factorAst ((MUL|DIV) factorAst)*
factorAst: INTEGER | LBRACKET arithAst RBRACKET | variableAst | stringAst
修改词法解析器
// 词性枚举
public enum TokenType {
INTEGER // 数字
, PLUS // 加法运算符
, EOF // 程序结束
, MINUS // 减法运算符
, MUL // 乘法运算符
, DIV // 除法运算符
, LBRACKET // 左括号
, RBRACKET // 右括号
, ID // 变量
, ASSIGN // 赋值符号=
, PRINT // 内置打印函数
, QUOTE // 单引号 '
, STRING // 字符串
}
// 词法解析器
public class Lexer {
private String text; // 输入的程序
private Integer position; // 记录扫描的位置
private Character currentChar; // 记录当前扫描的字符
private Map<String , Token> keyWordMap = new HashMap<>(); // 关键字map
public Token getNextToken(){ // 获取词法单元
while(this.currentChar != null){
if(Character.isDigit(this.currentChar)){
return this.integer();
}else if(Character.isWhitespace(currentChar)){
this.skipWhiteSpace();
}else if(this.currentChar == '+'){
Token token = new Token(TokenType.PLUS , "+");
this.advance();
return token;
}else if(this.currentChar == '-'){
Token token = new Token(TokenType.MINUS , "-");
this.advance();
return token;
}else if(this.currentChar == '*'){
Token token = new Token(TokenType.MUL , "*");
this.advance();
return token;
}else if(this.currentChar == '/'){
Token token = new Token(TokenType.DIV , "/");
this.advance();
return token;
}else if(this.currentChar == '/'){
Token token = new Token(TokenType.DIV , "/");
this.advance();
return token;
}else if(this.currentChar == '('){
Token token = new Token(TokenType.LBRACKET , "(");
this.advance();
return token;
}else if(this.currentChar == ')'){
Token token = new Token(TokenType.RBRACKET , ")");
this.advance();
return token;
}else if(this.currentChar == '='){
Token token = new Token(TokenType.ASSIGN , "=");
this.advance();
return token;
}else if(this.currentChar == '\''){
return this.string();
}else if(Character.isAlphabetic(currentChar)){
return variable();
}else {
this.error("未知的词法");
}
}
return new Token(TokenType.EOF);
}
private Token string() {
String value = "";
this.advance();
while(currentChar != null && currentChar != '\''){
value += currentChar;
this.advance();
}
this.advance();
return new Token(TokenType.STRING , value);
}
private Token variable(){ // 识别变量
String value = "";
while(currentChar != null && Character.isAlphabetic(currentChar)){
value += currentChar;
this.advance();
}
Token token = keyWordMap.getOrDefault(value, new Token(TokenType.ID, value));
return token;
}
public Token integer(){ // 识别多个数字
String result = "";
while(this.currentChar != null && Character.isDigit(this.currentChar)){
result += this.currentChar;
this.advance();
}
return new Token(TokenType.INTEGER ,Integer.valueOf(result));
}
private void skipWhiteSpace(){ // 空格跳过
while(currentChar != null && Character.isWhitespace(currentChar)){
this.advance();
}
}
public void advance(){ // 往后走一步
this.position += 1;
if(this.position <= this.text.length() - 1){ // 扫描的位置有效
this.currentChar = text.charAt(this.position);
}else{ // 扫描完了
this.currentChar = null;
}
}
public void error(String msg){ // 报错函数
throw new RuntimeException(msg);
}
public Lexer(String text) {// 构造器
this.text = text;
this.position = 0;
this.currentChar = text.charAt(this.position);
keyWordMap.put("print", new Token(TokenType.PRINT , "print"));
}
}
修改语法解析器
@Data
public class StringAst extends Ast{
private String value;
public StringAst(Token token) {
this.value = (String) token.getValue();
}
}
// 语法解析器
public class Parser {
private Lexer lexer ; // 词法解析器
private Token currentToken; // 当前的词法单元
public Parser(Lexer lexer) {
this.lexer = lexer;
this.currentToken = this.lexer.getNextToken();
}
public Ast programAst(){ // 程序节点
// programAst : (assignAst | printCallAst)*
ProgramAst ast = new ProgramAst();
while (Arrays.asList(TokenType.ID,TokenType.PRINT).contains(this.currentToken.getType()) ) {
if(this.currentToken.getType() == TokenType.ID){
ast.add(this.assignAst());
}else if(this.currentToken.getType() == TokenType.PRINT){
ast.add(this.printCallAst());
}
}
return ast;
}
public Ast printCallAst(){
// printCallAst : print LBRACKET arithAst RBRACKET
this.eat(TokenType.PRINT);
this.eat(TokenType.LBRACKET);
Ast ast = this.arithAst();
this.eat(TokenType.RBRACKET);
return new PrintCallAst(ast);
}
public Ast assignAst(){
// assignAst : variableAst ASSIGN arithAst
Token id = this.currentToken;
this.eat(TokenType.ID);
this.eat(TokenType.ASSIGN);
Ast right = this.arithAst();
return new AssignAst((String)id.getValue() , right);
}
public Ast arithAst(){
// arithAst : termAst ((PLUS|MINUS) termAst)*
Ast node = this.termAst();
while(Arrays.asList(TokenType.PLUS,TokenType.MINUS).contains(this.currentToken.getType())){
Token op = this.currentToken;
if(op.getType() == TokenType.PLUS){
this.eat(TokenType.PLUS);
}else if(op.getType() == TokenType.MINUS){
this.eat(TokenType.MINUS);
}
node = new ArithAst(node ,op.getType(),this.termAst());
}
return node;
}
public Ast termAst(){
// termAst : factorAst ((MUL|DIV) factorAst)*
Ast node = this.factorAst();
while(Arrays.asList(TokenType.MUL,TokenType.DIV).contains(this.currentToken.getType())){
Token op = this.currentToken;
if(op.getType() == TokenType.MUL){
this.eat(TokenType.MUL);
}else if(op.getType() == TokenType.DIV){
this.eat(TokenType.DIV);
}
node = new TermAst(node ,op.getType(),this.factorAst());
}
return node;
}
public Ast factorAst(){
// factorAst: INTEGER | LBRACKET assignAst RBRACKET | variableAst
Token left = this.currentToken;
if(left.getType() == TokenType.INTEGER){
this.eat(TokenType.INTEGER);
return new FactorAst((Integer)left.getValue());
}else if(left.getType() == TokenType.LBRACKET){
this.eat(TokenType.LBRACKET);
Ast ast = this.arithAst();
this.eat(TokenType.RBRACKET);
return ast;
}else if(left.getType() == TokenType.ID){
this.eat(TokenType.ID);
return new VariableAst(left);
}else if(left.getType() == TokenType.STRING){
this.eat(TokenType.STRING);
return new StringAst(left);
}
this.error("语法错误");
return null;
}
public void eat(TokenType tokenType){ // 确认当前的词性是否正确
if(tokenType == this.currentToken.getType()){
this.currentToken = this.lexer.getNextToken();
}else{
this.error("语法错误");
}
}
public void error(String msg){ // 报错函数
throw new RuntimeException(msg);
}
public Ast parse(){ // 获取语法树
return this.programAst();
}
}
修改解释器
// 目标执行器
public class Interpreter {
private Parser parser; // 语法解析器
private Map<String , Object> symbolMap = new HashMap<>(); // 符号表
public Interpreter(Parser parser) {
this.parser = parser;
}
public void visitProgramAst(Ast ast){ // 访问programAst节点
ProgramAst programAst = (ProgramAst) ast;
List<Ast> statementList = programAst.getStatementList();
for(Ast statment : statementList){
this.visit(statment);
}
}
public void visitPrintCallAst(Ast ast){
PrintCallAst printCallAst = (PrintCallAst) ast;
System.out.println(this.visit(printCallAst.getArithAst()));
}
public void visitAssignAst(Ast ast){
AssignAst assignAst = (AssignAst) ast;
String name = assignAst.getLeftValue();
Object value = this.visit(assignAst.getRightValue());
symbolMap.put(name,value);
}
public Object visitArithAst(Ast ast){
ArithAst arithAst = (ArithAst) ast;
if(arithAst.getOp() == TokenType.PLUS){
Object leftObj = this.visit(arithAst.getLeftValue());
Object rightObj = this.visit(arithAst.getRightValue());
if(leftObj instanceof String || rightObj instanceof String){
String left = leftObj instanceof String ? (String) leftObj : String.valueOf((Integer)leftObj);
String right = rightObj instanceof String ? (String) rightObj : String.valueOf((Integer)rightObj);
return left + right;
}else {
return (Integer) leftObj + (Integer)rightObj;
}
}else if(arithAst.getOp() == TokenType.MINUS){
return (Integer)this.visit(arithAst.getLeftValue()) - (Integer)this.visit(arithAst.getRightValue()); // 减法计算
}
return null;
}
public Object visitTermAst(Ast ast){
TermAst termAst = (TermAst) ast;
if(termAst.getOp() == TokenType.MUL){
return (Integer)this.visit(termAst.getLeftValue()) * (Integer)this.visit(termAst.getRightValue()); // 乘法计算
}else if(termAst.getOp() == TokenType.DIV){
return (Integer)this.visit(termAst.getLeftValue()) / (Integer)this.visit(termAst.getRightValue()); // 除法计算
}
return null;
}
public Object visitStringAst(Ast ast){
StringAst stringAst = (StringAst) ast;
return stringAst.getValue();
}
public Object visitFactorAst(Ast ast){
FactorAst factorAst = (FactorAst) ast;
return factorAst.getValue();
}
public Object visitVariableAst(Ast ast){
VariableAst variableAst = (VariableAst) ast;
return symbolMap.get(variableAst.getValue()); // 从符号表获取对应的值
}
public Object visit(Ast ast){ // 使用反射通过类名调用对应的函数
String methodName = "visit" + ast.getClass().getSimpleName();
try {
Method method = this.getClass().getDeclaredMethod(methodName , Ast.class );
return method.invoke(this, ast);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public void expr() {
Ast ast = parser.parse(); // 获取语法树
this.visit(ast); // 遍历获取结果
System.out.println(symbolMap);
}
}
执行测试
LJS.txt
a = 1
b = 1 + 2
c = a + 1
d = a * b + c * 2
f = a * ( b + c ) * 2
e = 'hello world'
print('a的值为:' + a)
print('b的值为:' + b)
print(e + '_' + a)
Path resourcePath = Paths.get("study-python","demo","src", "main", "resources").toAbsolutePath();
Lexer lexer = new Lexer(FileUtil.readUtf8String(resourcePath + "\\LJS.txt"));
Parser parser = new Parser(lexer);
Interpreter interpreter = new Interpreter(parser);
interpreter.expr();
控制台打印
a的值为:1
b的值为:3
hello world_1
{a=1, b=3, c=2, d=7, e=hello world, f=10}