205 lines
5.9 KiB
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";
|
|
}
|
|
}
|
|
}
|