在上一篇咱们曾经领有了一个简略lisp 解释器 (JLispInter)它反对:
- 变量:a
- 绑定:(define a 5)
- 四则运算: + - * /
- 函数 (lambda (x) (exp))
- 调用 (g exp)
过后咱们还留下了一些问题:
如解析器还不反对字符串,高阶函数中四则运算 define 等还不能作为入参,不反对匿名函数 ,分支判断 等
在这篇文章中咱们先疏忽语法树解析器,专一于解释器,花了1个多小时的工夫写了一个更加欠缺的解释器,为了不便解说我会将代码一段一段的放出,这样能够让咱们更聚焦一些,在开始前,画了一个表达式语法元素的形成图,这是形象之后(疏忽了如 Characters,Strings,Vectors , Dotted pairs, lists 等)前面如Dotted pairs 咱们能够通过函数的模式实现。
其中 var expression 是援用类型也就是能够对其进行化简,var在此处咱们用symbols进行代指;number, characters, boolean 是根底类型或原子类型(atom)不能对其进行化简,其就是最简模式;functional则是介于两者之前,在作为参数时它是一个箱子,在对其进行apply(调用)时则是关上的箱子,箱子里关上后能够是atom,也能够是functional,如果是后者则是高阶函数,也就是能够再次对其进行apply(调用)直到关上后为atom为止。
在前面咱们会看到 :“道生一 , 毕生二, 二生三 ,三生万物。”
准备阶段 咱们还须要对 Env 进行一些革新,对解析器进行的革新咱们在这里先疏忽掉, Cons 对应的是前文 ExpNode (此处 Cons是ExpNode 的父类)
Env
public class Env { private final Map<String, Object> env = new HashMap<>(); private Env parent; public static Env newInstance(Env parent) { Env env1 = new Env(); env1.parent = parent; return env1; } public void setEnv(Symbols symbols, Object val) { setEnv(symbols.getName(),val); } public void setEnv(String key, Object val) { env.put(key, val); } public Optional<Object> env(Symbols symbols) { String symbolsName = symbols.getName(); return Optional.ofNullable(env.containsKey(symbolsName) ? env.get(symbolsName) : (parent != null ? parent.env(symbols).orElse(null) : null)); } public boolean contains(Symbols symbols){ return env.containsKey(symbols.getName()); } public Env parent(){ return parent; }}
道生一
JLispInter2
@Slf4jpublic class JLispInter2 { private static final Predicate<Object> IS_EXP = o -> o instanceof Cons; private static final Predicate<Object> IS_SYMBOLS = o -> o instanceof Symbols; private static final Predicate<Object> IS_FUN = o -> o instanceof Function; private static final Predicate<Object> IS_ATOM = o -> o instanceof Number || o instanceof String || o instanceof Boolean; private static final BiPredicate<Cons, Object> CAN_APPLY = (exp, v) -> IS_FUN.test(v) && exp.isExp(); public static Object eval(String exp) { Env root = Env.newInstance(null); return eval(Parse.parseTree(exp), Env.newInstance(root)); } private static Object eval(Cons exp, Env env) { Object car = exp.car(); Cons cdr = exp.cdr(); if (IS_EXP.test(car)) { Object v = eval(exp.carCons(), env); if (CAN_APPLY.test(exp, v)) { return apply(v, cdr, env); } return cdr.isEmpty() ? v : eval(cdr, env); } else if (IS_SYMBOLS.test(car)) { Object v = env.env(exp.carSymbols()).orElseThrow(() -> new IllegalArgumentException(exp.parent() + ": " + exp.carSymbols() + " not define")); if (CAN_APPLY.test(exp, v)) { return apply(v, cdr, env); } return cdr.isEmpty() ? v : eval(cdr, env); } else if (IS_ATOM.test(car)) { return cdr.isEmpty() ? car : eval(cdr, env); } else { return cdr.isEmpty() ? car : eval(cdr, env); } } private static Object apply(Object v, Cons cdr, Env env) { return ((Function<ApplyArgs, Object>) v).apply(ApplyArgs.of(cdr, env, () -> cdr.data().stream().map(o -> getAtom(o, cdr, env)).toArray(), JLispInter2::eval)); } private static Object getAtom(Object o, Cons exp, Env env) { if (IS_EXP.test(o)) { return eval((Cons) o, env); } else if (IS_SYMBOLS.test(o)) { return eval(toCons(o, exp), env); } else { return o; } } private static Cons toCons(Object obj, Cons parent) { return ExpNode.one(parent, obj); } @Value(staticConstructor = "of") public static class ApplyArgs { Cons exp; Env env; Supplier<Object[]> lazyArgs; BiFunction<Cons, Env, Object> eval; }}
ApplyArgs 最开始的时候只有exp,和env 两个参数;前面发现有的函数会须要args,而后就又定义了一个懒加载的lazyArgs;至于eval函数则是为了当自定义函数应用时可能有一个门(这个门只对自定义函数凋谢),它的作用是:“为了获取一些货色要去应用某些函数或取值,而这些函数或取值能够不须要本人再去实现一遍,间接应用eval便可获取。”
此时JLispInter2还没有任何内置的函数,不过咱们还是能够只用atom应用JLispInter2.eval办法。
输出
(1 2 3 4 5 6)(1 2 3)
输入
63
尽管还没有任何内置函数,但从输入后果来看这是一个好的开始,接下来让咱们为其减少如四则运算函数,而后再看一下它的成果,让咱们开始吧。
毕生二
咱们先定义一个用来治理内置函数的类,咱们就叫它FunManager吧
FunManager
static class FunManager{ private static final Map<String, Function<ApplyArgs, Object>> FUNCTIONAL = new ConcurrentHashMap<>(); static { reg("+", applyArgs -> toIntStream(applyArgs.getLazyArgs()).reduce(Integer::sum).orElse(null)); reg("-", applyArgs -> toIntStream(applyArgs.getLazyArgs()).reduce((a, b) -> a - b).orElse(null)); reg("*", applyArgs -> toIntStream(applyArgs.getLazyArgs()).reduce((a, b) -> a * b).orElse(null)); reg("/", applyArgs -> toIntStream(applyArgs.getLazyArgs()).reduce((a, b) -> a / b).orElse(null)); } private static void reg(String optKey, Function<ApplyArgs, Object> opt) { FUNCTIONAL.put(optKey, opt); } private static Stream<Integer> toIntStream(Supplier<Object[]> supplierObjs) { return Stream.of(supplierObjs.get()).map(Object::toString).map(Integer::valueOf).collect(Collectors.toList()).stream(); }}
而后让咱们对JLispInter2施加一些魔法,哈哈哈
JLispInter2
public class JLispInter2 { public static Object eval(String exp) { Env root = Env.newInstance(null); FunManager.FUNCTIONAL.forEach(root::setEnv); return eval(Parse.parseTree(exp), Env.newInstance(root)); }}
尽管只减少了一行
FunManager.FUNCTIONAL.forEach(root::setEnv);
但当初JLispInter2曾经反对四则运算了,让咱们测试一下。
输出
(+ 1 2 3 4 (+ 5 6) (+ 7 8 (+ 9 0)))(+ 1 2 3 4 (- 9 3))
输入
4516
从输入后果来看它合乎咱们的预期,这阐明 var (env), expression, function, apply, atom 这些性能曾经能够失常工作了,接下来让咱们为其减少更多的内置函数,让其能够更弱小一些吧。
二生三
既然是内置函数,那用户自定义额函数要怎么办呢?这很简略,咱们只须要减少一个能够返回函数的高阶函数即可,让咱们开始吧。
首先在FunManager外面减少一个高阶函数
FunManager
static class FunManager{ static { ... reg("lambda", applyArgs -> lambda(applyArgs.getExp(), applyArgs.getEnv())); ... } private static Function<ApplyArgs, Object> lambda(Cons cdr, Env env) { return (applyArgs) -> { Object[] x = applyArgs.getLazyArgs().get(); Cons args = cdr.carCons(); Cons body = cdr.cdr(); validateTrue(args.data().size() == x.length, "参数不统一"); Env env0 = Env.newInstance(env); int i = 0; for (Object argName : args) { env0.setEnv(((Symbols) argName), x[i]); i++; } return applyArgs.getEval().apply(body, env0); }; } private static void validateTrue(boolean flag, String err) { if (!flag) { throw new IllegalArgumentException(err); } }}
就这么简略,只须要15行代码即可,让咱们来好好的看一下它。
return (applyArgs) -> {...}
通过下面这行代码咱们返回一个函数,这对应下面咱们刚提到过的高阶函数。
Object[] x = applyArgs.getLazyArgs().get();
通过下面这行代码咱们能够取得入参的值。
Env env0 = Env.newInstance(env);
通过下面这行代码咱们为以后办法在调用时创立一个新的环境。
env0.setEnv(((Symbols) argName), x[i]);
通过下面这行代码咱们为以后办法入参进行了变量绑定。
return applyArgs.getEval().apply(body, env0);
通过下面这行代码咱们让eval函数帮咱们解释以后办法体并失去返回值。
让咱们测试一下解释器是否失常工作。
输出
((lambda (x) (+ 5 x)) 5)
输入
10
让咱们再测试一下是否反对高阶函数
((lambda (x) (+ 5 x)) ((lambda (y) (* y y)) 5))(((lambda (x) (lambda (o) (o 5 x))) ((lambda (y) (* y y)) 5)) +)
输入
3030
成果还是很不错的,接下来让咱们再减少 let cond set! apply 内置函数。
FunManager.let
static class FunManager{... private static Object let(Cons cdr, Env env, BiFunction<Cons, Env, Object> eval) { Object car0 = cdr.car(); validateTrue(car0 instanceof Cons && cdr.data().size() == 2, "please check" + car0); Env env0 = Env.newInstance(env); Cons car = cdr.carCons(); Cons body = cdr.cdr().carCons(); for (Object con : car) { validateTrue(IS_EXP.test(con), "please check" + con); Cons item = (Cons) con; Symbols var = item.carSymbols(); validateTrue(!env0.contains(var), "Do not repeat the let " + var); env0.setEnv(var, eval.apply(item.cdr(), env)); } return eval.apply(body, env0); }...}
FunManager.cond
static class FunManager{... private static Object cond(Cons cdr, Env env, BiFunction<Cons, Env, Object> eval){ Cons car = cdr.carCons(); Cons predicateExp = car.carCons(); Cons body = car.cdr(); if(isaBoolean(eval.apply(predicateExp, env))){ return eval.apply(body, env); }else{ Cons elseCdr = cdr.cdr(); if(elseCdr.data().size()==1){ // 去掉括號 while (IS_EXP.test(elseCdr.car())&&elseCdr.data().size()==1){ elseCdr = elseCdr.carCons(); } validateTrue(IS_SYMBOLS.test(elseCdr.car())&&elseCdr.carSymbols().getName().equals("else"),"cond last item not else key"); return eval.apply(elseCdr.cdr(), env); } return cond(elseCdr, env, eval); } }...}
FunManager.set!
static class FunManager{... private static Object set(Cons cdr,Env env, BiFunction<Cons, Env, Object> eval) { Symbols var = cdr.carSymbols(); Object val = eval.apply(cdr.cdr(),env); validateTrue(env.env(var).isPresent(), " not definition set! error " + var); Env envParent = env; while (!envParent.contains(var)) { envParent = envParent.parent(); } envParent.setEnv(var, val); return null; }...}
FunManager.apply0
static class FunManager{... private static Object apply0(Cons cdr, Env env) { Object f = cdr.car(); f = getAtom(f, cdr, env); if (IS_FUN.test(f)) { return apply(f, cdr.cdr(), env); } else { throw new IllegalArgumentException("apply " + cdr); } }...}
接下来让咱们将他们注册进去
FunManager
static class FunManager{... static { ... reg("let", applyArgs -> let(applyArgs.getExp(), applyArgs.getEnv(), applyArgs.getEval())); reg("set!", applyArgs -> set(applyArgs.getExp(), applyArgs.getEnv(), applyArgs.getEval())); reg("apply", applyArgs -> apply0(applyArgs.getExp(), applyArgs.getEnv())); reg("cond", applyArgs -> cond(applyArgs.getExp(), applyArgs.getEnv(), applyArgs.getEval())); }...}
貌似还少点什么?是的 boolean逻辑
FunManager.regBooleanFun
static class FunManager{... private static void regBooleanFun() { reg("and", applyArgs -> { Object[] ts = applyArgs.getLazyArgs().get(); return Stream.of(ts).allMatch(FunManager::isaBoolean) ? ts[ts.length - 1] : false; }); reg("or", applyArgs -> Stream.of(applyArgs.getLazyArgs().get()).filter(FunManager::isaBoolean).findFirst().orElse(false)); reg("not", applyArgs -> { Object[] ts = applyArgs.getLazyArgs().get(); validateTrue(ts.length == 1, applyArgs.getExp() + "not args only one"); return !isaBoolean(ts[0]); }); reg("<", applyArgs -> predicate(applyArgs.getExp(), applyArgs.getLazyArgs(), (x, y) -> (x instanceof Integer && y instanceof Integer) ? (Integer) x < (Integer) y : x.toString().length() < y.toString().length())); reg("<=", applyArgs -> predicate(applyArgs.getExp(), applyArgs.getLazyArgs(), (x, y) -> (x instanceof Integer && y instanceof Integer) ? (Integer) x <= (Integer) y : x.toString().length() <= y.toString().length())); reg("=", applyArgs -> predicate(applyArgs.getExp(), applyArgs.getLazyArgs(), Object::equals)); reg("!=", applyArgs -> predicate(applyArgs.getExp(), applyArgs.getLazyArgs(), (x, y) -> !x.equals(y))); reg(">", applyArgs -> predicate(applyArgs.getExp(), applyArgs.getLazyArgs(), (x, y) -> (x instanceof Integer && y instanceof Integer) ? (Integer) x > (Integer) y : x.toString().length() > y.toString().length())); reg(">=", applyArgs -> predicate(applyArgs.getExp(), applyArgs.getLazyArgs(), (x, y) -> (x instanceof Integer && y instanceof Integer) ? (Integer) x >= (Integer) y : x.toString().length() >= y.toString().length())); } private static Object predicate(Cons exp, Supplier<Object[]> lazyArgs, BiPredicate<Object, Object> predicates) { Object[] objs = lazyArgs.get(); validateTrue(objs.length > 1, exp + " args qty > 1 "); Object o = objs[0]; for (int i = 1; i < objs.length; i++) { Object o1 = objs[i]; boolean b = predicates.test(o, o1); if (!b) { return b; } o = o1; } return true; }...}
这里也要记得注册哦。
FunManager
static class FunManager{... static { ... regBooleanFun(); }...}
持续咱们的测试
为了不便这里不再分两行
// let test<= (let ((a 5) (b 5) (g (lambda (x y) (+ x y)))) (g a b))=> 10// cond test<= ((lambda (x) (cond ((< 5 x) 1) ((> 5 x) -1) (else 0) )) 6)=> 1// let and cond test <= (let ((a 5) (g (lambda (x) (cond ((< 5 x) 1) ((> 5 x) -1) (else 0) )))) (g a))=> 0// set! test <= (let ((a 5) (b 6)) ((set! a 6) (+ a b)))=> 12// apply test<= (let ((a 5) (g (lambda (x) (cond ((< 5 x) 1) ((> 5 x) -1) (else 0) )))) (apply g a))=> 0