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

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

而不用


if (a < 3) {
  throw new Exception("Value must be less than 3.");
}

算术操作

iScheme 算术操作包含 + - * / % 五个操作,它们仅应用于数值类型(也就是 SNumber )。

从加减法开始:


.BuildIn("+", (args, scope) => {
  var numbers = args.Select(obj => obj.Evaluate(scope)).Cast<SNumber>();
  return numbers.Sum(n => n);
})
.BuildIn("-", (args, scope) => {
  var numbers = args.Select(obj => obj.Evaluate(scope)).Cast<SNumber>().ToArray();
  Int64 firstValue = numbers[0];
  if (numbers.Length == 1) {
    return -firstValue;
  }
  return firstValue - numbers.Skip(1).Sum(s => s);
})

注意到这里有一段重复逻辑负责转型求值(Cast then Evaluation),考虑到接下来还有不少地方要用这个逻辑,我把这段逻辑抽象成扩展方法:


public static IEnumerable<T> Evaluate<T>(this IEnumerable<SExpression> expressions, SScope scope)
where T : SObject {
  return expressions.Evaluate(scope).Cast<T>();
}
public static IEnumerable<SObject> Evaluate(this IEnumerable<SExpression> expressions, SScope scope) {
  return expressions.Select(exp => exp.Evaluate(scope));
}

然后加减法就可以如此定义:


.BuildIn("+", (args, scope) => (args.Evaluate<SNumber>(scope).Sum(s => s)))
.BuildIn("-", (args, scope) => {
  var numbers = args.Evaluate<SNumber>(scope).ToArray();
  Int64 firstValue = numbers[0];
  if (numbers.Length == 1) {
    return -firstValue;
  }
  return firstValue - numbers.Skip(1).Sum(s => s);
})

乘法,除法和求模定义如下:


.BuildIn("*", (args, scope) => args.Evaluate<SNumber>(scope).Aggregate((a, b) => a * b))
.BuildIn("/", (args, scope) => {
  var numbers = args.Evaluate<SNumber>(scope).ToArray();
  Int64 firstValue = numbers[0];
  return firstValue / numbers.Skip(1).Aggregate((a, b) => a * b);
})
.BuildIn("%", (args, scope) => {
  (args.Length == 2).OrThrows("Parameters count in mod should be 2");
  var numbers = args.Evaluate<SNumber>(scope).ToArray();
  return numbers[0] % numbers[1];
})

逻辑操作

iScheme 逻辑操作包括 and , or 和 not ,即与,或和非。

需要注意的是 iScheme 逻辑操作是 短路求值(Short-circuit evaluation),也就是说:

(and condA condB) ,如果 condA 为假,那么整个表达式为假,无需对 condB 求值。 (or condA condB) ,如果 condA 为真,那么整个表达式为真,无需对 condB 求值。

此外和 + - * / 一样, and 和 or 也可以接收任意数量的参数。