这算是 <两周自制脚本语言> 这本书的练习项目, 不过我从 Java 实现换成了 Golang 实现, 并且把语言的名字从平淡的 Stone 换成了我很喜欢 Yrel(伊瑞尔).
-
不需要借助软件执行的设计语言, 也就是机器语言
-
解释器和编译器:
- 解释器(Interpreter): 根据程序中的算法执行运算, 这种软件也可以称为虚拟机.
- 编译器(Compiler): 将某种语言携程的程序转换成另一种语言的程序, 通常目标程序为机器语言程序, 这个过程称为编译.
程序执行的典型流程:
-
源代码, 通过
词法分析到2 -
单词排列( 简短字符串的排列 ), 通过
语法分析到3 -
抽象语法树, 到4具体执行
-
分为两种
- 编译器: 生成其他语言的程序
- 解释器: 执行程序, 获得结果
- 没有静态类型系统, 变量可以随时被赋给合法值
- 句末没有分号
- 必须使用花括号
{}来包裹语句块 - 没有
return关键字, 语句块的最后一个表达式作为返回值 - 使用双斜线
//作为注释 if和while中的条件表达式不需要括起来else关键字必须和前一个语句块的右括号以及下一个语句块的左括号一行
一段代码示例:
sum = 0
i = 1
while i < 10 {
sum = sum + i
i = i + 1
}
if i % 2 == 0 {
even = even + 1
} else {
odd = odd + 1
}
这个章节开始词法分析的内容, 这里的主要作用是把代码分割成若干的单词(token), 去掉没用的空格和注释.
Yrel 语言的单词只有三种类型: 标识符, 整型和字符串类型. 分别用IdToken, NumToken和StrToken实现.
每次逐行从文件中读取内容, 并且通过正则匹配的方式来获得token:
- IdToken:
([A-Z_a-z][A-Z_a-z0-9]*|=|!=|==|<=|>=|&&|\|\|) - numPattern:
([0-9]+) - strPattern:
(\"[\S\s]*\") - commentPattern:
(//[\S\s]*)
这三个结构体都实现了Token接口, 主要方法有:
IsNumber() boolIsIdentifier() boolIsString() boolGetText() string
可以使用GetTokenType()方法来获得一个token的类型: Identifier, Number, String或Undefined.
我们可以使用NewLexer()方法来创建一个词法分析器Lexer的实例, 这个实例的主要内容是一个Queue[]数组, 通过使用借口实现泛型, 用来保存分析得到的token序列.
Lexer有一个Read()方法, 可以用于从词法分析器中读取token, 当所有元素已经读取出来时, 将会返回EOF.
进行语法分析的时候, 语法元素有终结符和非终结符的却别, 这里我使用go tool原生的yacc库编写一个语法分析器.
语法的定义使用BNF范式, 并且约定终结符为全大写, 非终结符为全小写.
-
终结符
IDENTIFIER: 对应词法分析中的IdToken类型NUMBER: 对应词法分析中的NumToken类型STRING: 对应词法分析中的StrToken类型BOOL: 对应词法中的BoolToken类型
-
非终结符及其表达式
primary:"(" expr ")" | NUMBER | IDENTIFIER | STRING | BOOLfactor:"-" primary | primaryexpr:factor { OP factor }block:"{" [statement] "}"simple: exprstatement:"if" expr block [ "else" block ]"while" expr blocksimple
program:[statement]