1 2 * 3 +
、o.k.f1(x) o (get k) (call f1 (var x))
总:解释=执行、编译=执行缓存(pyc,zend-opcache,JIT,AOT..)、解析=提供语言模型(=Sexp=AST=PSI)、提取文本所构造的数据。这个数据可以是 class Node{class If(Expr,Blk):Node; For(Decl,cond:Expr,tail:Expr,Blk):Node}
也可以是 1+2=3
即 +(1,2):3,总之方便转化就行。
代码生成=构造任何程度上代码(文本、语言内)结构。这么看元编程、()=>闭包算是种代码生成,总之它能极大的简化编程
学习顺序:解释器-解析器(1) -虚拟机(2)
(1)无首 则你不知道语法结构是为什么动作而存在 (2)无首 则你不知道字节码文件只是一些栈代码、常量 组成的二进制结构,许多二元op 有何相似
(1)的俩项要先执行,然后(2),这叫(a-b)中(-)号后序遍历(tree walker)。 编译时也是一样深先、铺平
编译器上除了AST,基本块算赖图BB-SSA(a1=b; a2=a1+b.. =左边不重复出现) 这种IR(中间形式)更利于编译优化(如dce死代码消除,需要遍历表达式受用处)
也有种把操作独立于情况的 class Compile: Visitor {val ctx:Codgen; impl fun see(t:If)="若${+t.cond} ${+t.body.see}" }
其大体 class Visitor{ fun Node.unaryPlus()=vis.see(this)}
,这是OOP解,弱类型有更好的解(反正编译器执行频率不高,脚本一点多共享中间数据 也OK)
这个领域有C系和函数式两个流派,C系(LexYaCC,Bison “解析器代码生成”)什么综合语法啊继承语法(遍历前有没有作用域:名值表维护)、“容错”、左递归和 LL (Simp)LR 完全就是伪数学垃圾(好比SQL数据库“技术”和当今JavaEE ORM都懒得用的关系),改良它(ANTLR,jflex)和有限利用(NFA-DFA字符类转移图) 也就好一丢丢
为啥CC是编译器编译器呢,因为LL那一套就是从自顶向下(递归下降)的推广,给列表匹配弄了左递归转循环、无回退、首随表(无递归化) ,写出有问题的文法定义,自然需要奇奇怪怪的“编译器”解决随之而来的一大堆分析转化;所以说写解析器是编译原理也不错,只是这个讨论点就很莫名其妙,你想做编译器,却把重点放在如何自动修补文法规则里的历史包袱。或许你觉得无递归化更快,但语言构造AST 还不是需 $$=$1+$2
这种 栈。
LR,LALR 都是自底向上(词条到文法、先读最右) ,缺点是不能无分词器(当然如果有NF"B" 或许就行)。反正 Lookahead-(L/R) 都是给列表而非字符流做匹配的“虚拟机”,嗯…… [a,b,c].reverse 再匹配,难怪人手写不了
函数式的只会自顶向下-带回退,加上一点语言概念的“优雅”hack (以闭包组合子,CPS,Monad..),极少数人在Py,JS,甚至C 实现递归下降解析器框架,然后用它解决常见语言的文本匹配,更别说扩展算法了。
这下面还有个认真的C编译原理回答。 float n=1.0; int bits=*(int*)&n;
和 CAST(T,T1) union{T a;T1 b}){x}.b
是C99 的“组合值(compound literal)”,可在表达式里创建局部结构,像 std::initializer_list。
那个5行命令行心形的其实就是个函数图(看我B站),duangsuse:失败的初选赛 有类似方法但是整数精度足够
都在用成熟的“专业”生成器。 实际上手写递归下降一直是很通用的做法
然后专业知识可以看:
腾讯在国内没有实现的梦,在海外能成真吗? Tech 星球报道,虎牙整体大裁员,国际化业务裁员比例高达70%,出海产品 Nimo TV ...
妊娠油的功效是否有保障还真得靠专业人士分析,换句话说,一款妊娠油能否预防、淡化妊娠纹需要进行专业的测评。