在Swift中实现编程语言—第6部分:解析变量

注意:这是“用Swift编写编程语言”教程系列的第六部分。一定要检查 以前的内容

在之前的教程中,我们创建了第一个解释器。 现在是时候使用一些真正的编程语言功能来启动它,首先是对变量的支持。

为了提供可变支持,我们的语言需要两个附加功能:

  1. 支持解析变量名
  2. 支持声明变量

本教程全部关于第一(“支持解析变量名”)。

解析变量名。

此功能会创建两种错误情况,首先将它们添加到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}中, 
  “ \\)”:{ _在.parensClo​​se}中, 
  “ [[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协议需要我们实现返回Floatinterpret方法。

对于变量名,此方法应检查是否定义了变量,如果未定义,则抛出错误,如果已定义,则应简单地返回其值。

为了获取变量声明,我们首先需要能够存储它们。 一个简单的字典应该可以解决这个问题:

  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 

接下来的变量声明!

敬请关注!