概述
上一篇实现了使用变量,这一篇只简单加一个功能,打印功能。之前的实现都是解释完一个表达式就打印结果,这样当然不利于后续扩展,这一篇只简单加个打印语句。在完善语言过程中,遇到问题时思考如何拆解问题并解决它,同时又希望每一步不要跨太大,想到什么就加什么,所以篇幅时长时短。
动手
词法分析(nl.l)加一个关键字表示要调用打印方法,完整代码在这里
.....
%%
"print" return PRINT;
.....
%%
.....
语法分析(nl.y)加上新token(PRINT)定义
%token SEMICOLON ADD SUB MUL DIV LP RP MOD ASSIGN PRINT
语法分析识别到一个表达式语句(expression SEMICOLON)后去掉打印逻辑,语句(statement)的产生式加上打印语句(PRINT LP expression RP SEMICOLON),形式上类似于调用print方法,打印语句括号间放表达式,逻辑上需要先计算表达式值,再打印出结果。
statement
: expression SEMICOLON
{
nl_eval_expression($1);
}
| IDENTIFIER ASSIGN expression SEMICOLON
{
King *king = nl_get_current_king();
nl_execute_assign_statement(king, $1, $3);
}
| PRINT LP expression RP SEMICOLON
{
NL_Value v = nl_eval_expression($3);
nl_print_value(&v);
}
;
这样逻辑就改完了,然后我们可以在代码中加上打印方法的调用
结果
比如我们可以这样写代码
abc = 34;
4 + 10 * abc;
print(abc);
_abc = 1+ 1.1;
10 * _abc + abc;
print(10 * abc);
abc = abc * 2;
abc * 2;
print(abc);
录屏看看运行结果,可以看到现在不会打印所有语句,只会打印我们指定的表达式。
结束
当前编译器解释执行逻辑有个大问题,它一边做语法分析,一边执行其中的语句和表达式,这样没办法往后扩展。打个比方,后续如果要支持定义方法和调用方法,按照目前的解析逻辑,在解析到方法定义中的打印语句时,马上执行打印。而实际上这只是方法定义而已,方法还没被调用。整个程序其实也是一个大方法,正确的逻辑顺序应该是区分开解析和执行这两个阶段。
下一篇,我们会改造代码逻辑,做到先解释后执行,这种做法未来还能扩展成先编译成另一个中语言单独存放,需要时可以调用执行。