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

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

需要注意的几点

iScheme 支持部分求值(Partial Evaluation),这意味着:
部分求值


>> (def mul (func (a b) (* a b)))
>> (func (a b) (* a b))
>> (mul 3 4)
>> 12
>> (mul 3)
>> (func (a:3 b) (* a b))
>> ((mul 3) 4)
>> 12

也就是说,当 SFunction 的实际参数(Argument)数量小于其形式参数(Parameter)的数量时,它依然是一个函数,无法被求值。

这个功能有什么用呢?生成高阶函数。有了部分求值,我们就可以使用


(def mul (func (a b) (* a b)))
(def mul3 (mul 3))
>> (mul3 3)
>> 9

而不用专门定义一个生成函数:


(def times (func (n) (func (n x) (* n x)) ) )
(def mul3 (times 3))
>> (mul3 3)
>> 9

SFunction#ToString 可以将其自身还原为源代码——从而大大简化了 iScheme 的理解和测试。
内置操作

iScheme 的内置操作有四种:算术|逻辑|比较|列表操作。

我选择了表达力(Expressiveness)强的 lambda 方法表来定义内置操作:

首先在 SScope 中添加静态字段 builtinFunctions ,以及对应的访问属性 BuiltinFunctions 和操作方法 BuildIn 。


public class SScope {
  private static Dictionary<String, Func<SExpression[], SScope, SObject>> builtinFunctions =
    new Dictionary<String, Func<SExpression[], SScope, SObject>>();
  public static Dictionary<String, Func<SExpression[], SScope, SObject>> BuiltinFunctions {
    get { return builtinFunctions; }
  }
  // Dirty HACK for fluent API.
  public SScope BuildIn(String name, Func<SExpression[], SScope, SObject> builtinFuntion) {
    SScope.builtinFunctions.Add(name, builtinFuntion);
    return this;
  }
}

注意:

    Func<T1, T2, TRESULT> 是 C# 提供的委托类型,表示一个接受 T1 和 T2 ,返回 TRESULT 这里有一个小 HACK,使用实例方法(Instance Method)修改静态成员(Static Member),从而实现一套流畅的 API(参见Fluent Interface)。

接下来就可以这样定义内置操作:


new SScope(parent: null)
  .BuildIn("+", addMethod)
  .BuildIn("-", subMethod)
  .BuildIn("*", mulMethod)
  .BuildIn("/", divMethod);

一目了然。

断言(Assertion)扩展

为了便于进行断言,我对 Boolean 类型做了一点点扩展。


public static void OrThrows(this Boolean condition, String message = null) {
  if (!condition) { throw new Exception(message ?? "WTF"); }
}

从而可以写出流畅的断言:

(a < 3).OrThrows("Value must be less than 3.");