Symbols
public interface Symbols { static Symbols of(String name) { return new SimpleSymbols(name); } String getName(); @Value class SimpleSymbols implements Symbols { String name; @Override public String toString() { return "`" + name; } }}
Strings
public interface Strings { static Strings of(String str) { return new SimpleStr(str); } String getString(); @Value class SimpleStr implements Strings { String string; @Override public String toString() { return "'" + string+"'"; } }}
Cons
@RequiredArgsConstructor(staticName = "of")public class Cons implements Iterable<Object> { private final List<Object> data; private final Cons parent; private final boolean exp; public static Cons newInstance(Cons parent) { return Cons.of(new ArrayList<>(), parent, true); } public void add(Object obj) { data.add(obj); } public Object car() { return iterator().next(); } public Cons carCons() { return (Cons) car(); } public Symbols carSymbols() { return (Symbols) car(); } public Cons cdr() { return Cons.of(data.subList(1, data.size() ), this,false); } public Collection<Object> data() { return Collections.unmodifiableList(data); } public Cons parent() { return parent; } public boolean isExp() { return exp; } public boolean isEmpty(){ return data.isEmpty(); } @Override public Iterator<Object> iterator() { return data.iterator(); } @Override public String toString() { Optional<String> reduce = data.stream().map(Object::toString).reduce((x, y) -> x + " " + y).map(o -> "(" + o + ")"); return reduce.orElse("()"); }}
Parse
@Slf4jpublic class Parse { private static final String PREFIX = "("; private static final String SUFFIX = ")"; public static Cons parse(String expStr) { return parse(format(replaceSpace(expStr)), Cons.newInstance(null)); } private static Cons parse(String expStr, Cons r) { String tempExpStr = expStr.trim(); boolean isPush = tempExpStr.indexOf(PREFIX) == 0; String nextStr = tempExpStr.substring(1); int prefixIndex2 = nextStr.indexOf(PREFIX); int suffixIndex2 = nextStr.indexOf(SUFFIX); int nextFixIndex = (prefixIndex2==-1||suffixIndex2==-1? Math.max(prefixIndex2,suffixIndex2):Math.min(prefixIndex2, suffixIndex2) ); if(nextFixIndex>-1){ if (isPush) { Cons astList = Cons.newInstance(r); String subExpStr = nextStr.substring(0, nextFixIndex); exp2List(subExpStr).forEach(astList::add); r.add(astList); return parse(tempExpStr.substring(nextFixIndex+1), astList); } else { String subExpStr = nextStr.substring(0, nextFixIndex); exp2List(subExpStr).forEach(r.parent()::add); return parse(tempExpStr.substring(nextFixIndex+1), r.parent()); } }else{ return r; } } private static List<Object> exp2List(String exp){ return exp.trim().length()==0? Collections.emptyList(): Arrays.stream(exp.trim().split(" ")).map(Parse::parseObj).collect(Collectors.toList()); } private static String format(String str) { return str.contains(" ") ? format(str.replaceAll(" ", " ")) : str; } private static String replaceSpace(String str){ if(str.contains("'")){ int first = str.indexOf("'"); int last = str.substring(first+1).indexOf("'")+first+1; String substring = str.substring(first, last + 1); substring = substring.replaceAll(" ","\\\\\\O"); return str.substring(0, first) + substring + replaceSpace(str.substring(last + 1)); } return str; } private static Object parseObj(String atom){ try { return Integer.valueOf(atom); } catch (NumberFormatException e) { if (atom.equals("true") || atom.equals("false")) { return Boolean.valueOf(atom); } else if (atom.indexOf("'") == 0 && atom.lastIndexOf("'") == atom.length() - 1) { return Strings.of(atom.replaceAll("'", "").replaceAll("\\\\O", " ")); } else { return Symbols.of(atom); } } } public static void main(String[] args) { log.debug("{}", (parse("(1 2 3 4 5 6 7 (8 9 10) 11 12 (13 (14 15)) 16 true 's 5 8' '456 ' a b c (7 8 9))"))); log.debug("{}", parse("(let ((cons (lambda (x y) (lambda (g) (g x y (lambda (a) (set! x a)) (lambda (a) (set! y a)))))) (car (lambda (f) (f (lambda (x y sx sy) (x))))) (cdr (lambda (f) (f (lambda (x y sx sy) (y))))) (set-car! (lambda (f a) (f (lambda (x y sx sy) ((sx a) f))))) (set-cdr! (lambda (f a) (f (lambda (x y sx sy) ((sy a) f)))))) ( cdr (set-cdr! (cons 1 2) 3)))")); }}
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; }}
JLispInterpreter3
@Slf4jpublic class JLispInterpreter3 { 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); FunManager.FUNCTIONAL.forEach(root::setEnv); return eval(Parse.parse(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(), JLispInterpreter3::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(toSubCons(o, exp), env); } else { return o; } } private static Cons toSubCons(Object obj, Cons parent) { return Cons.of(Collections.singletonList(obj),parent, false); } @Value(staticConstructor = "of") public static class ApplyArgs { Cons exp; Env env; Supplier<Object[]> lazyArgs; BiFunction<Cons, Env, Object> eval; }}
FunManager
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)); reg("lambda", applyArgs -> lambda(applyArgs.getExp(), applyArgs.getEnv())); reg("define", FunManager::define); 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())); regBooleanFun(); reg("if", applyArgs -> if0(applyArgs.getExp(), applyArgs.getEnv(), applyArgs.getEval())); reg("cond", applyArgs -> cond(applyArgs.getExp(), applyArgs.getEnv(), applyArgs.getEval())); } private static void reg(String optKey, Function<ApplyArgs, Object> opt) { FUNCTIONAL.put(optKey, opt); } 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 Object if0(Cons cdr, Env env, BiFunction<Cons, Env, Object> eval) { Cons car = cdr.carCons(); if (isaBoolean(eval.apply(car, env))) { Object then = cdr.cdr().car(); return getAtom(then, cdr.cdr(), env); } else { if (cdr.cdr().cdr().data().size() > 0) { return eval.apply(cdr.cdr().cdr(), env); } return null; } } 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); } } private static Object define(ApplyArgs applyArgs) { Cons cdr = applyArgs.getExp(); Object val = applyArgs.eval.apply(cdr.cdr(), applyArgs.getEnv()); validateTrue(!applyArgs.getEnv().contains(cdr.carSymbols()), "Do not repeat the definition " + cdr.carSymbols()); applyArgs.getEnv().setEnv(cdr.carSymbols(), val); return null; } 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); } 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; } 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); } } private static void validateTrue(boolean flag, String err) { if (!flag) { throw new IllegalArgumentException(err); } } 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; } private static boolean isaBoolean(Object o) { if (o instanceof Boolean) { return (Boolean) o; } else { return !o.equals(0); } } private static Stream<Integer> toIntStream(Supplier<Object[]> supplierObjs) { return Stream.of(supplierObjs.get()).map(Object::toString).map(Integer::valueOf).collect(Collectors.toList()).stream(); }}