在Swift中实现编程语言—第10部分:If语句
注意:这是关于“在Swift中实现编程语言”的系列教程的第十部分。一定要检查 以前的内容 。
在上一教程中,我们添加了对功能的支持。 事实证明,语句是否非常相似。
与函数类似,如果语句由关键字和代码块组成。 两者之间的区别在于需要参数的函数,而if语句则需要表达式。
如果语句与else
和else if
一起增加了一些额外的复杂性,但是如果您一直在遵循以前的教程,则此过程应该很简单。
您可能已经问过自己“鉴于我们的语言仅支持一种类型,我们将如何实现if语句?”这是一个令人担忧的问题,但是现在我们将使用真实/虚假格式,解释大于或等于1表示真实,而所有值小于1则表示虚假。
添加关键字
像以前的教程一样,所有这些都始于添加适当的关键字。 这次是if
和else
(我们已经在方括号“ {,”,“}”,“(”和“)”中添加了括号):
枚举令牌{
typealias Generator =(String)->令牌?
...
大小写逗号
大小写如果
案例`else`
静态var生成器:[String:Generator] {
返回[
“ \\ * | \\ / | \\ + | \\-”:{.op(Operator(rawValue:$ 0)!)},
“ \\-?([0-9] * \\。[0-9] + | [0-9] +)”:{.number(Float($ 0)!)},
“ [[a-zA-Z _ $] [a-zA-Z_ $ 0-9] *”:{
保护$ 0!=“ var” else {
返回.var
}
保护$ 0!=“功能”其他{
返回功能
}
警卫$ 0!=“”如果“否则{
返回.if
}
警卫$ 0!=“ else” else {
返回.else
}
返回.identifier($ 0)
},
...
]
}
}
实施节点
下一步是为if语句添加Node结构。 这次涉及一些设计。 我们需要问自己“我们的IfStatement结构应包含哪些数据?”
“ if”部分包括一个表达式和要运行的块。 “其他if”还包含一个表达式和一个块,但“其他”不包含表达式。
我们可以将IfStatement
视为默认为(“ else”块)的代码块和元组列表,其中包括一个Node
(表达式)以及该代码块的开始和结束(就像我们存储的功能代码块)。
解释功能也相当简单:
struct IfStatement:节点{
让ifOrIfElse:[[表达式:节点,块:节点)]
让块:节点?
func interpret()throws-> Float {
for ifOrElseIf in ifAndIfElse {
守卫(尝试ifOrElseIf
.expression.interpret())> = 1其他{
继续
}
返回尝试ifOrElseIf.block.interpret()
}
守卫let elseBlock = self.elseBlock else {
返回-1
}
返回尝试elseBlock.interpret()
}
}
在我们的interpret
方法中,我们仅找到第一个真实表达式并返回其关联块的解释。 如果没有找到表达式,我们只返回值-1
。
解析IfStatement
在开始之前,我们应该在Parser
类中添加一个辅助方法来解析大括号内的代码块。 可以从我们的函数定义解析方法中复制实现,如下所示:
func parseCurlyCodeBlock()引发->节点{
后卫canPop,大小写为.curlyOpen = popToken()其他{
抛出Parser.Error.expected(“ {”)
}
var depth = 1
让startIndex =索引
而canPop {
警卫队.curlyClose = peek()else {
如果case .curlyOpen = peek(){
深度+ = 1
}
指数+ = 1
继续
}
深度-= 1
保护深度== 0其他{
指数+ = 1
继续
}
打破
}
让endIndex =索引
警卫canPop,大小写.curlyClose = popToken()其他{
抛出Error.expected(“}”)
}
让代币= Array(self.tokens [startIndex .. <endIndex])
返回try Parser(tokens:tokens).parse()
}
现在,我们还应该能够重构我们的函数定义解析器,但是为了不使本教程IfStatement
,让我们继续使用IfStatement
解析器。
这是我尝试变得聪明的地方,所以请忍受:
func parseIfStatement()引发->节点{
警卫canPop,大小写.if = popToken()else {
抛出Parser.Error.expected(“ if”)
}
让firstExpression =尝试parseExpression()
让codeBlock =试试parseCurlyCodeBlock()
让ifsAndElseIfs:[[(节点,节点)] = [(firstExpression,codeBlock)]
后卫canPop,大小写为.else = peek()else {
返回IfStatement(ifsAndElseIfs:ifsAndElseIfs,elseBlock:nil)
}
popToken()
警卫队.if = peek()else {
让elseBlock =尝试parseCurlyCodeBlock()
返回IfStatement(ifsAndElseIfs:ifsAndElseIfs,
elseBlock:elseBlock)
}
让ifStatement =试试parseIfStatement()作为! IfStatement
返回IfStatement(ifsAndElseIfs:ifsAndElseIfs + ifStatement.ifsAndElseIfs,elseBlock:ifStatement.elseBlock)
}
如果您很难遵循,这是非常有用的,尤其是递归,我建议您在回到本教程之前先阅读一下该主题。 这是正在发生的事情:
- 我们首先解析第一个“ If”及其表达式和块。
- 我们检查是否存在“ else”,否则,我们仅返回单个块
IfStatement
。 - 我们检查是否还有一个“ if”,否则,我们将先前找到的else解释为elseBlock。 如果确实找到了“ if”(这就是我试图变得更聪明的地方),我们将尝试递归解析
IfStatement
。
大!
现在剩下要做的就是将IfStatement
解析添加到解析器的主要parse
方法中:
func parse()抛出->节点{
var节点:[Node] = []
而canPop {
让令牌= peek()
切换令牌{
案例.var:
让声明=尝试parseVariableDeclaration()
nodes.append(声明)
大小写
让定义=尝试parseFunctionDefinition()
nodes.append(定义)
案例.if:
let语句=尝试parseIfStatement()
node.append(声明)
默认:
让expression =试试parseExpression()
nodes.append(表达式)
}
}
返回块(节点:节点)
}
现在我们可以使用if语句了!
var code =“”“
var false = 0
var true = 1
如果为假{
5
}否则,如果为{
6
}
“”
let令牌= Lexer(代码:代码).tokens
让节点=尝试解析器(令牌:令牌).parse()
做{
print(尝试node.interpret()== 6)//是
} {
打印(error.localizedDescription)
}
那是我们的语言! 它缺少许多必要的功能,例如正确设置变量,循环和递归等。
可能是狗屎,但这是我们的狗屎,因此我们不会让它的无能将我们拖下去。
下一步
从技术上讲,我们在本教程系列中仅创建了编程语言的(前端)前端。 如果您有兴趣创建一种“真实的”语言,建议您阅读以下材料:
- 手工翻译-一本免费的在线书籍,其中介绍了如何实现完整的编程语言,虚拟机以及其他所有内容,因为它很底层,因此您需要为此花一些时间。 我才刚刚开始研究它,到目前为止,这本书看起来绝对值得一试。
- 编译器:原理,技术和工具(《龙书》)[亚马逊的会员链接]-关于创建编译器的较著名的书之一,我不能说它是否是最好的,这是我真正看过的唯一一本书进入,但我当然觉得很有趣。