transcendental-lisp/src/main/function/builtin/BackquoteEvaluator.java

205 lines
5.9 KiB
Java

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";
}
}
}