在Swift中实现编程语言—第10部分:If语句

注意:这是关于“在Swift中实现编程语言”的系列教程的第十部分。一定要检查 以前的内容

在上一教程中,我们添加了对功能的支持。 事实证明,语句是否非常相似。

与函数类似,如果语句由关键字和代码块组成。 两者之间的区别在于需要参数的函数,而if语句则需要表达式。

如果语句与elseelse if一起增加了一些额外的复杂性,但是如果您一直在遵循以前的教程,则此过程应该很简单。

您可能已经问过自己“鉴于我们的语言仅支持一种类型,我们将如何实现if语句?”这是一个令人担忧的问题,但是现在我们将使用真实/虚假格式,解释大于或等于1表示真实,而所有值小于1则表示虚假。

添加关键字

像以前的教程一样,所有这些都始于添加适当的关键字。 这次是ifelse (我们已经在方括号“ {,”,“}”,“(”和“)”中添加了括号):

 枚举令牌{ 
  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) 
  } 

如果您很难遵循,这是非常有用的,尤其是递归,我建议您在回到本教程之前先阅读一下该主题。 这是正在发生的事情:

  1. 我们首先解析第一个“ If”及其表达式和块。
  2. 我们检查是否存在“ else”,否则,我们仅返回单个块IfStatement
  3. 我们检查是否还有一个“ 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) 
  } 

那是我们的语言! 它缺少许多必要的功能,例如正确设置变量,循环和递归等。

可能是狗屎,但这是我们的狗屎,因此我们不会让它的无能将我们拖下去。

下一步

从技术上讲,我们在本教程系列中仅创建了编程语言的(前端)前端。 如果您有兴趣创建一种“真实的”语言,建议您阅读以下材料:

  • 手工翻译-一本免费的在线书籍,其中介绍了如何实现完整的编程语言,虚拟机以及其他所有内容,因为它很底层,因此您需要为此花一些时间。 我才刚刚开始研究它,到目前为止,这本书看起来绝对值得一试。
  • 编译器:原理,技术和工具(《龙书》)[亚马逊的会员链接]-关于创建编译器的较著名的书之一,我不能说它是否是最好的,这是我真正看过的唯一一本书进入,但我当然觉得很有趣。