在Swift中实现编程语言—第6部分:解析变量
注意:这是“用Swift编写编程语言”教程系列的第六部分。一定要检查 以前的内容 。
在之前的教程中,我们创建了第一个解释器。 现在是时候使用一些真正的编程语言功能来启动它,首先是对变量的支持。
为了提供可变支持,我们的语言需要两个附加功能:
- 支持解析变量名
- 支持声明变量
本教程全部关于第一(“支持解析变量名”)。
解析变量名。
此功能会创建两种错误情况,首先将它们添加到Parser.Error
枚举中:
枚举错误:Swift.Error {
预期情况
预期大小写
预期情况
预期情况
预期大小写(字符串)
case notDefined(String)
}
添加对解析变量的支持相当简单,只需执行几个简单的步骤。
首先,我们需要在Lexer中添加对变量名称的支持。 为此,我们要做的就是将一个案例identifier
添加到我们的Token
枚举以及一个生成器:
枚举令牌{
typealias Generator =(String)->令牌?
案例操作员(操作员)
案例号(浮点数)
案例解析
案例解析
案例标识符(字符串)
静态var生成器:[String:Generator] {
返回[
“ \\ * | \\ / | \\ + | \\-”:{.op(Operator(rawValue:$ 0)!)},
“ \\-?([0-9] * \\。[0-9] + | [0-9] +)”:{.number(Float($ 0)!)},
“ \\(”:{ _在.parensOpen}中,
“ \\)”:{ _在.parensClose}中,
“ [[a-zA-Z _ $] [a-zA-Z_ $ 0-9] *”:{.identifier($ 0)},
]
}
}
不要担心此正则表达式的复杂性,它实际上非常简单。
第一位[a-zA-Z_$]
指出标识符必须以字母,下划线或美元符号开头。
第二位[a-zA-Z_$0-9]*
指出第一位后面是( *
表示零个或多个)字母,下划线,美元符号或数字的任何序列。
太好了,现在是时候编辑我们的解析器了。 本质上,标识符代表值,因此我们将编辑Parser的parseValue
方法以支持标识符:
func parseValue()抛出->节点{
开关(peek()){
案件编号:
返回尝试parseNumber()
案例.parensOpen:
返回尝试parseParens()
大小写.identifier:
返回尝试parseIdentifier()
默认:
抛出Error.expected(“ ”)
}
}
当前没有称为“ parseIdentifier”的方法,因此我们也必须将其添加到解析器中。 它的实现将与parseNumber
非常相似:
func parseIdentifier()抛出->节点{
警卫队让.identifier(name)= popToken()else {
抛出Error.expectedIdentifier
}
返回名称
}
如您所见,我们仍然收到编译器错误。 这是因为我们希望返回一个Node
,但是返回的是name
String
。
为了解决这个问题,我们应该使字符串符合Node
协议。
Node协议需要我们实现返回Float
的interpret
方法。
对于变量名,此方法应检查是否定义了变量,如果未定义,则抛出错误,如果已定义,则应简单地返回其值。
为了获取变量声明,我们首先需要能够存储它们。 一个简单的字典应该可以解决这个问题:
var标识符:[String:Float] = [
“ PI”:Float.pi,
]
我只是为了测试而添加了变量“ PI”,因为我们还没有添加对变量声明的支持(下一个教程的主题)。
大! 现在我们准备使String
符合Node
:
扩展字符串:节点{
func interpret()throws-> Float {
保护警卫值=标识符[自己]否则{
抛出Parser.Error.notDefined()
}
返回值
}
}
而已! 现在,我们可以解析变量了:
让代码=“ 50 * PI”
let令牌= Lexer(代码:代码).tokens
让输出=尝试解析器(令牌:令牌).parse()。interpret()
print(输出==(50 * Float.pi))// true
接下来的变量声明!
敬请关注!