目录戳
要开发一个编程语言,首先得知道“我想开发的语言长什么样”。
这种语言可以Lisp式,可以是c式,可以像python,ruby,甚至由emoji组成...
总之先得把语言的模样大概设计一下当时刚起手的时候我的设计是这样的
class Foo(bar:Type) method():Unit
为啥会选择冒号这样的类型声明呢(话说由于这个设计好多做java/c#的第一反应是看不懂)。。纯粹是觉得这么写很好玩。。为什么用Unit而不是void呢?同理。。觉得有趣,就这么设计了(虽然后来还是把void加进去了)
毕竟这是自己的编程语言,再怎么奇特也不奇怪。
我目前开发了一个REPL
,可以用来试一试语法特性。在github上可以找到 ,语法规则在src/test/resources/lang-demo/
中可以找到
什么是REPL? Read Eval Print Loop, 简单来说就是一个程序,输入一串表达式或语句,输出获得的结果
在设计时,设计的语法不能出现歧义
平时口语交流,歧义有不少。例如“你看我头像牛逼吗”这类,就最好不要在编程语言的语法中出现。
(实际上,编程语言的“歧义”也会遇到,例如方法调用时,可能会遇到重载间的选择,这时候就可能会报错,表示不能在两个方法间做出决策。但是语法上的歧义千万不能出现)
在做完了设计后不一定急着做词法分析,倒是可以把AST一并设计了。
AST叫做 抽象语法树
(Abstract Syntax Tree)。它是解释器和编译器的重要中间结构。学校里编译原理作业甚至期末作业也只需要解析到AST。
大概可以这么描述 :有了AST,编译才刚刚开始
那么AST是什么呢?首先它是一棵树。它描述了整个语言的逻辑结构。
比方说,1+2*3。这到底是(1+2)*3还是1+(2*3)呢?用一维关系不可能描述清楚,那么树自然是最方便的选择了
上述关系可以描述为
+ / \ / \ 1 * / \ / \ 2 3
也许你会说,用前缀式或者后缀式不就好了?话是这么说,不过可读性哪有中缀式来的好啊。。话说熵永远是增的,如果编译器省力了,那写代码的就要受罪。况且这还是自己的语言,不就是坑自己吗(额。。喜欢LISP风格前缀式的自然可以创建自己喜欢的语法,我只是就我自己喜好说说。。)
图示结构很清楚,实际编码呢?
我用的是类来完成树的结构。通过一组合理的继承关系来构建一系列的类。然后用它们的实例构建一棵树。例如,所有语句都是Statement
。所有可能产生一个值的都是Expression
。然后整个结构体系都由Statement和Expression为主导
例如
array.length
我将其解析为
Access( Access(null, 'array'), 'length')
其中Access(Expression exp, String name)
,表示访问exp的name成员。
当然这只是一种解析思路,像ECJ就把点看成某种连接符,大概解析成这样((array), (length))。只能说各有各的好处。
为什么这里没有先看词法呢?因为有一种非常简便的方法进行词法分析,我个人认为扩展性极强。至少我在开发过程中调整过几次语法特性,词法方面只改一个地方,测试用例依旧全部跑通。所以词法的具体设计可以缓一缓。
下一章就要开始正戏了~编译器的第一步:词法分析
希望看官能关注下我的编程语言哦。。