作者descent (「雄辩是银,沉默是金」)
看板C_and_CPP
标题[心得] interpreter 环境与变数
时间Fri Nov 25 11:09:20 2016
变数就是字面上的意思, 写程式的都知道, 但知道 interpreter 怎麽把这功能做出来的
程式员可能就不多了。
list 1
1 x=1;
2 x+5;
list 1 L2 怎麽把 x, 1 做个对应的呢? 就是靠《环境》, 他不是什麽神秘的东西, 用
c++ 来说明什麽是环境的话就是:
std::map<std::string, ASTNode *> env;
然後把 env["x"]=1 就完成 x=1 这个运算式。
不管你用什麽资料结构, 反正就是把 x 和 1 的关系存起来就对了。感觉很简单, 但真实
世界上可没这麽简单, 不过教学就是要化繁为简, 先知道这样就够了。
再来的 list 1 L2 在 x+5 要做 eval 时, eval 5 就回传 5 ASTNode 本身, x 就到
env 去把他对应的数字找出来, 也就是 1, 所以传回 1 的 ASTNode, 然後 + 就可以把
1, 5 作相加的运算, 6 就这麽算出来了。
那《环境》困难在哪里呢? 在 function call。
p1
1 int x;
2 int f1()
3 {
4 2*3;
5 12+13;
6 }
7
8 int f2(int i)
9 {
9.5 int cc;
10 5*i;
11 }
12 int main()
13 {
14 int z;
15 61+2;
16 z=5;
17 f1();
18 f2(z);
19 }
如果像 p1 这样, 扫描到 L1 时, 把 x 加入 global_env (table 1), 在 call main
时, 要产生 main_env (table 2), main_env 的上层 up_env 要指到 global_env, 当扫
描到 L 14 时, 把 z:0 加入到 main_env, 而扫描到 L 16 时, 把 5 的值至换到 z
(table 3), 在 call f2(z) 时 (L 18), 又要产生 f2_env (table 4), 再把 up_env 指
到 main_env, 并把 z/i 的对应存到 f2_env, cc 的对应也要存到 f2_env, 疑, cc 没对
应的值阿, 随便塞一个就好了, 这不就是 c 语言 auto 变数的行为吗? 而上层 env 要指
到 main_env, 这样层层下去, 让整个环境建立起所有的变数名称/变数值的对应关系。大
概像 env class 那样。
table 1. global_env
up_env 0
x 0
table 2. main_env
up_env global_env
z 0
table 3 main_env
up_env global_env
z 5
table 4. f2_env
up_env main_env
i z
cc 0
所以在 f2() 里头用到 cc, i 时, 就去查 f2_env, 把对应的值找出来, 就可以知道这个
变数的值, 而在这层找不到, 就要去 up_env 找, 都找不到就发出错误讯息。
env class
14 class Environment
15 {
16 public:
17 Environment(Environment *outer=0, const char *name=0): outer_(outer)
18 {
21 }
22 Environment *outer_;
31 int free_frame_index_;
32 private:
33 std::map<std::string, ASTNode *> frame_;
34 };
观念很简单, 但实作还是会困难一点, 可参考以下范例程式码。以下程式码只有实作最简
单的环境, 我还没完成 function call 那复杂的环境。目前的版本我已经完成了
function call 和 return, 真是有种觉得自己很不简单的感觉呢!
source code:
https://github.com/descent/simple_compiler (
https://goo.gl/FBON5s )
commit: 0452c23b770dad99b1d503e0f417cae45879ce72
除了加入变数, 函式的定义也一样要加入环境, 当呼叫一个函式时, 就到环境来找这个函
式, 找到後把 parameter, argument 配对後, 就去执行该函式了。
至於函式的传回值, 那又是另外一件事情了。
因为使用了「环境」来处理「变数」, 这便是「环境变数」名称的由来。
打通整个 interpreter 流程并不容易, 当我满怀好奇心将所有疑问抽丝剥茧, 最後接触
到本质的那一刻, 我知道这些努力与坚持是值得的, 纵使我无法因为这样而在工作上有立
即的帮助, 但满足自己的好奇心就是驱使我这麽做的最大动力。
// 本文使用 Blog2BBS 自动将Blog文章转成缩址的BBS纯文字
http://goo.gl/TZ4E17 //
blog 原文
http://descent-incoming.blogspot.tw/2016/07/compiler-4.html
--
纸上得来终觉浅,绝知此事要躬行。
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 113.196.174.254
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/C_and_CPP/M.1480043372.A.661.html
1F:嘘 Ommm5566: 排版 11/25 19:22
排版有什麽问题吗?
* 该有的标点符号都有
* 英文和中文字有留空白
* 半形逗号後面也留一个空白
* () 前後也留空白
* 程式码也有行号
如果 bbs 版本不好看, 也可以参考网页版本。
2F:推 wtchen: 推回来,我觉得排版没问题阿 11/25 20:22
3F:推 art1: 扫到 L14 那里有点看不懂,那一行是int z,但你的说明是把 11/26 08:31
4F:→ art1: x:0 加到 main环境 11/26 08:31
感谢, 笔误, 己修正
5F:→ MOONRAKER: ptt的排版就是 我看得顺眼才叫排版 我看不顺眼就嘘排版 11/26 13:06
6F:→ MOONRAKER: 一堆连报纸都没看过的○○满口排版 强! 11/26 13:06
7F:推 EdisonX: 请问有机会介绍 conditions 吗? 11/26 21:08
conditions 是指 if/else 吗?
8F:推 youtuuube000: 看不懂感觉很厉害先推再说XD 11/27 11:13
9F:推 EdisonX: 是的 我说的conditions 泛指条件判断 含if else elsif s 11/27 13:05
10F:→ EdisonX: witch case 11/27 13:05
会的, 目前的实作有
if/else
while
funcion call
当然还有 c 最具代表性的指标, 只不过是 interpreter 的指标版本。
11F:推 EdisonX: 期待您的 part 2 11/27 20:21
12F:推 Ommm5566: 那很抱歉 补回来推 11/27 22:06
13F:推 eye5002003: 这专案是用C++实现一个直译器采用C做为脚本语言吗? 11/28 09:55
是的, 本来打算写编译器的, 不过发现有点难, 只好先改成直译器的版本。
14F:推 eye5002003: 喔!我有做过类似的事,建议使用C++11的特性 11/28 19:02
15F:→ eye5002003: 匿名函式用来写parser很有帮助,个人意见 11/28 19:04
有没有什麽建议的写法, 我那程式码丑的要命。
16F:推 EdisonX: goo.gl/b70T6m 11/29 14:53
感谢分享, c++ 程度不太好, 看不懂用 匿名函式 的好处。
我的 AST 没有使用物件导向, 所以没有很多 node type,
但 eval() 里头就有一大堆的 switch/case, 这部份相当丑陋,
但用物件导向我也不觉得有比较好, 这是个困难的选择。
四则运算解析器那篇没把 ebnf 列出来, 要看懂那个程式会相当困难。
※ 编辑: descent (175.98.141.254), 11/29/2016 17:23:03
17F:推 CoNsTaR: 这种时候就会觉得有 pattern matching 的语言真好 XDD 11/29 17:53
18F:推 littleshan: functional language 拿来做这种事超愉悦的 XD 12/02 00:11
19F:→ littleshan: 推荐coursera上由UW提供的programming languages课程 12/02 00:12
20F:→ littleshan: 从此人生打开了另一扇窗 12/02 00:12