package function.builtin; import error.LispException; import function.ArgumentValidator; import sexpression.AtSignExpression; import sexpression.BackquoteExpression; import sexpression.CommaExpression; import sexpression.Cons; import sexpression.SExpression; import static function.builtin.EVAL.eval; import static sexpression.Nil.NIL; class BackquoteEvaluator { private ArgumentValidator listValidator; private ArgumentValidator atSignValidator; private BackquoteExpression backTick; private Cons resolvedList; private Cons leader; private Cons follower; public BackquoteEvaluator(BackquoteExpression backTick) { this.listValidator = new ArgumentValidator("`|list|"); this.atSignValidator = new ArgumentValidator("@|list|"); this.backTick = backTick; this.resolvedList = new Cons(NIL, NIL); this.leader = resolvedList; this.follower = resolvedList; } public SExpression evaluate() { SExpression expression = backTick.getExpression(); if (expression.isCons()) expression = resolveList((Cons) expression); else if (expression.isComma()) expression = eval(((CommaExpression) expression).getExpression()); else if (expression.isAtSign()) throw new AtSignNotInCommaException(); return expression; } public SExpression resolveList(Cons list) { listValidator.validate(list); createResolvedList(list); return resolvedList; } private void createResolvedList(Cons list) { for (; list.isCons(); list = (Cons) list.getRest()) resolveExpression(list.getFirst()); follower.setRest(NIL); } private void resolveExpression(SExpression expression) { if (expression.isAtSign()) throw new AtSignNotInCommaException(); else if (expression.isComma()) resolveCommaExpression(expression); else if (expression.isList()) resolveListExpression(expression); else addResolvedExpression(expression); } private void resolveCommaExpression(SExpression expression) { CommaEvaluationResult result = evaluateComma((CommaExpression) expression); if (result.isAtSign()) unpackResolvedList((Cons) result.getResult()); else addResolvedExpression(result.getResult()); } private CommaEvaluationResult evaluateComma(CommaExpression comma) { SExpression expression = comma.getExpression(); validateCommaExpression(expression); if (expression.isAtSign()) return new CommaEvaluationAtSignResult(evaluateAtSign((AtSignExpression) expression)); else return new CommaEvaluationResult(eval(expression)); } private void validateCommaExpression(SExpression expression) { if (expression.isComma()) throw new NestedCommaException(); } private Cons evaluateAtSign(AtSignExpression atSign) { SExpression expression = atSign.getExpression(); validateAtSignUnevaluatedExpression(expression); SExpression evaluation = eval(expression); return getValidatedList(evaluation); } private void validateAtSignUnevaluatedExpression(SExpression expression) { if (expression.isComma()) throw new NestedCommaException(); else if (expression.isAtSign()) throw new NestedAtSignException(); } private Cons getValidatedList(SExpression evaluation) { if (!evaluation.isList()) throw new AtSignNotListException(); Cons evaluatedList = (Cons) evaluation; atSignValidator.validate(evaluatedList); return evaluatedList; } private void unpackResolvedList(Cons list) { for (; list.isCons(); list = (Cons) list.getRest()) addResolvedExpression(list.getFirst()); } private void addResolvedExpression(SExpression expression) { leader.setFirst(expression); leader.setRest(new Cons(NIL, NIL)); follower = leader; leader = (Cons) leader.getRest(); } private void resolveListExpression(SExpression expression) { BackquoteEvaluator evaluator = new BackquoteEvaluator(new BackquoteExpression(expression)); addResolvedExpression(evaluator.evaluate()); } private static class CommaEvaluationResult { private SExpression result; public CommaEvaluationResult(SExpression result) { this.result = result; } public SExpression getResult() { return result; } public boolean isAtSign() { return false; } } private static class CommaEvaluationAtSignResult extends CommaEvaluationResult { public CommaEvaluationAtSignResult(SExpression result) { super(result); } @Override public boolean isAtSign() { return true; } } public static class NestedCommaException extends LispException { private static final long serialVersionUID = 1L; @Override public String getMessage() { return "nested comma"; } } public static class NestedAtSignException extends LispException { private static final long serialVersionUID = 1L; @Override public String getMessage() { return "nested at sign"; } } public static class AtSignNotInCommaException extends LispException { private static final long serialVersionUID = 1L; @Override public String getMessage() { return "at sign not in comma"; } } public static class AtSignNotListException extends LispException { private static final long serialVersionUID = 1L; @Override public String getMessage() { return "at sign did not evaluate to a list"; } } }