90分钟实现一门编程语言(极简解释器教程)

2019-12-30 14:54:22王冬梅

处理 begin

遍历语句,然后返回最后一条语句的值:


else if (first.Value == "begin") {
  SObject result = null;
  foreach (SExpression statement in this.Children.Skip(1)) {
    result = statement.Evaluate(scope);
  }
  return result;
}

处理 func

利用 SExpression 构建 SFunction ,然后返回:


else if (first.Value == "func") {
  SExpression body = this.Children[2];
  String[] parameters = this.Children[1].Children.Select(exp => exp.Value).ToArray();
  SScope newScope = new SScope(scope);
  return new SFunction(body, parameters, newScope);
}

处理 list

首先把 list 里的元素依次求值,然后创建 SList :


else if (first.Value == "list") {
  return new SList(this.Children.Skip(1).Select(exp => exp.Evaluate(scope)));
}

处理内置操作

首先得到参数的表达式,然后调用对应的内置函数:


else if (SScope.BuiltinFunctions.ContainsKey(first.Value)) {
  var arguments = this.Children.Skip(1).ToArray();
  return SScope.BuiltinFunctions[first.Value](arguments, scope);
}

处理自定义函数调用

自定义函数调用有两种情况:

    非具名函数调用:((func (x) (* x x)) 3) 具名函数调用:(square 3)

调用自定义函数时应使用新的作用域,所以为 SFunction 增加 Update 方法:


public SFunction Update(SObject[] arguments) {
  var existingArguments = this.Parameters.Select(p => this.Scope.FindInTop(p)).Where(obj => obj != null);
  var newArguments = existingArguments.Concat(arguments).ToArray();
  SScope newScope = this.Scope.Parent.SpawnScopeWith(this.Parameters, newArguments);
  return new SFunction(this.Body, this.Parameters, newScope);
}

为了便于创建自定义作用域,并判断函数的参数是否被赋值,为 SScope 增加 SpawnScopeWith 和 FindInTop 方法:


public SScope SpawnScopeWith(String[] names, SObject[] values) {
  (names.Length >= values.Length).OrThrows("Too many arguments.");
  SScope scope = new SScope(this);
  for (Int32 i = 0; i < values.Length; i++) {
    scope.variableTable.Add(names[i], values[i]);
  }
  return scope;
}
public SObject FindInTop(String name) {
  if (variableTable.ContainsKey(name)) {
    return variableTable[name];
  }
  return null;
}

下面是函数调用的实现:


else {
  SFunction function = first.Value == "(" ? (SFunction)first.Evaluate(scope) : (SFunction)scope.Find(first.Value);
  var arguments = this.Children.Skip(1).Select(s => s.Evaluate(scope)).ToArray();
  return function.Update(arguments).Evaluate();
}

完整的求值代码