Convert Set and Load to kotlin
This commit is contained in:
parent
7416e0e163
commit
924357b5cd
|
@ -1,204 +0,0 @@
|
||||||
package function.builtin;
|
|
||||||
|
|
||||||
import error.LispException;
|
|
||||||
import function.ArgumentValidator;
|
|
||||||
import sexpression.AtSignExpression;
|
|
||||||
import sexpression.BackquoteExpression;
|
|
||||||
import sexpression.CommaExpression;
|
|
||||||
import sexpression.Cons;
|
|
||||||
import sexpression.Nil;
|
|
||||||
import sexpression.SExpression;
|
|
||||||
|
|
||||||
import static function.builtin.Eval.eval;
|
|
||||||
|
|
||||||
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.INSTANCE, Nil.INSTANCE);
|
|
||||||
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.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
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.INSTANCE, Nil.INSTANCE));
|
|
||||||
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";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
package function.builtin
|
||||||
|
|
||||||
|
import error.LispException
|
||||||
|
import function.ArgumentValidator
|
||||||
|
import function.builtin.Eval.Companion.eval
|
||||||
|
import sexpression.AtSignExpression
|
||||||
|
import sexpression.BackquoteExpression
|
||||||
|
import sexpression.CommaExpression
|
||||||
|
import sexpression.Cons
|
||||||
|
import sexpression.Nil
|
||||||
|
import sexpression.SExpression
|
||||||
|
|
||||||
|
internal class BackquoteEvaluator(private val backTick: BackquoteExpression) {
|
||||||
|
|
||||||
|
private val listValidator: ArgumentValidator = ArgumentValidator("`|list|")
|
||||||
|
private val atSignValidator: ArgumentValidator = ArgumentValidator("@|list|")
|
||||||
|
private val resolvedList: Cons = Cons(Nil, Nil)
|
||||||
|
private var leader = resolvedList
|
||||||
|
private var follower = resolvedList
|
||||||
|
|
||||||
|
fun evaluate(): SExpression {
|
||||||
|
var expression = backTick.expression
|
||||||
|
|
||||||
|
if (expression.isCons)
|
||||||
|
expression = resolveList(expression as Cons)
|
||||||
|
else if (expression.isComma)
|
||||||
|
expression = eval((expression as CommaExpression).expression)
|
||||||
|
else if (expression.isAtSign)
|
||||||
|
throw AtSignNotInCommaException()
|
||||||
|
|
||||||
|
return expression
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveList(list: Cons): SExpression {
|
||||||
|
listValidator.validate(list)
|
||||||
|
createResolvedList(list)
|
||||||
|
|
||||||
|
return resolvedList
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createResolvedList(list: Cons) {
|
||||||
|
list.forEach { resolveExpression(it.first) }
|
||||||
|
|
||||||
|
follower.rest = Nil
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveExpression(expression: SExpression) = when {
|
||||||
|
expression.isAtSign -> throw AtSignNotInCommaException()
|
||||||
|
expression.isComma -> resolveCommaExpression(expression)
|
||||||
|
expression.isList -> resolveListExpression(expression)
|
||||||
|
else -> addResolvedExpression(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveCommaExpression(expression: SExpression) {
|
||||||
|
val result = evaluateComma(expression as CommaExpression)
|
||||||
|
|
||||||
|
if (result.isAtSign)
|
||||||
|
unpackResolvedList(result.result as Cons)
|
||||||
|
else
|
||||||
|
addResolvedExpression(result.result)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun evaluateComma(comma: CommaExpression): CommaEvaluationResult {
|
||||||
|
val expression = comma.expression
|
||||||
|
validateCommaExpression(expression)
|
||||||
|
|
||||||
|
return if (expression.isAtSign)
|
||||||
|
CommaEvaluationAtSignResult(evaluateAtSign(expression as AtSignExpression))
|
||||||
|
else
|
||||||
|
CommaEvaluationResult(eval(expression))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun validateCommaExpression(expression: SExpression) {
|
||||||
|
if (expression.isComma)
|
||||||
|
throw NestedCommaException()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun evaluateAtSign(atSign: AtSignExpression): Cons {
|
||||||
|
val expression = atSign.expression
|
||||||
|
validateAtSignUnevaluatedExpression(expression)
|
||||||
|
val evaluation = eval(expression)
|
||||||
|
|
||||||
|
return getValidatedList(evaluation)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun validateAtSignUnevaluatedExpression(expression: SExpression) {
|
||||||
|
if (expression.isComma)
|
||||||
|
throw NestedCommaException()
|
||||||
|
else if (expression.isAtSign)
|
||||||
|
throw NestedAtSignException()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getValidatedList(evaluation: SExpression): Cons {
|
||||||
|
if (!evaluation.isList)
|
||||||
|
throw AtSignNotListException()
|
||||||
|
|
||||||
|
val evaluatedList = evaluation as Cons
|
||||||
|
atSignValidator.validate(evaluatedList)
|
||||||
|
|
||||||
|
return evaluatedList
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unpackResolvedList(list: Cons) {
|
||||||
|
list.forEach { addResolvedExpression(it.first) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addResolvedExpression(expression: SExpression) {
|
||||||
|
leader.first = expression
|
||||||
|
leader.rest = Cons(Nil, Nil)
|
||||||
|
follower = leader
|
||||||
|
leader = leader.rest as Cons
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveListExpression(expression: SExpression) {
|
||||||
|
val evaluator = BackquoteEvaluator(BackquoteExpression(expression))
|
||||||
|
addResolvedExpression(evaluator.evaluate())
|
||||||
|
}
|
||||||
|
|
||||||
|
private open class CommaEvaluationResult(val result: SExpression) {
|
||||||
|
|
||||||
|
open val isAtSign = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CommaEvaluationAtSignResult(result: SExpression) : CommaEvaluationResult(result) {
|
||||||
|
|
||||||
|
override val isAtSign = true
|
||||||
|
}
|
||||||
|
|
||||||
|
class NestedCommaException : LispException() {
|
||||||
|
|
||||||
|
override val message = "nested comma"
|
||||||
|
}
|
||||||
|
|
||||||
|
class NestedAtSignException : LispException() {
|
||||||
|
|
||||||
|
override val message = "nested at sign"
|
||||||
|
}
|
||||||
|
|
||||||
|
class AtSignNotInCommaException : LispException() {
|
||||||
|
|
||||||
|
override val message = "at sign not in comma"
|
||||||
|
}
|
||||||
|
|
||||||
|
class AtSignNotListException : LispException() {
|
||||||
|
|
||||||
|
override val message = "at sign did not evaluate to a list"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,117 +0,0 @@
|
||||||
package function.builtin;
|
|
||||||
|
|
||||||
import environment.RuntimeEnvironment;
|
|
||||||
import error.LispException;
|
|
||||||
import error.LispWarning;
|
|
||||||
import function.ArgumentValidator;
|
|
||||||
import function.FunctionNames;
|
|
||||||
import function.LispFunction;
|
|
||||||
import parser.LispParser;
|
|
||||||
import sexpression.Cons;
|
|
||||||
import sexpression.LispString;
|
|
||||||
import sexpression.Nil;
|
|
||||||
import sexpression.SExpression;
|
|
||||||
import sexpression.Symbol;
|
|
||||||
import util.Path;
|
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.util.Stack;
|
|
||||||
|
|
||||||
import static function.builtin.Eval.eval;
|
|
||||||
import static java.text.MessageFormat.format;
|
|
||||||
|
|
||||||
@FunctionNames({ "LOAD" })
|
|
||||||
public class LOAD extends LispFunction {
|
|
||||||
|
|
||||||
private static Stack<String> pathPrefixes = new Stack<>();
|
|
||||||
private ArgumentValidator argumentValidator;
|
|
||||||
private RuntimeEnvironment environment;
|
|
||||||
|
|
||||||
public LOAD(String name) {
|
|
||||||
this.argumentValidator = new ArgumentValidator(name);
|
|
||||||
this.argumentValidator.setExactNumberOfArguments(1);
|
|
||||||
this.argumentValidator.setEveryArgumentExpectedType(LispString.class);
|
|
||||||
this.environment = RuntimeEnvironment.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SExpression call(Cons argumentList) {
|
|
||||||
argumentValidator.validate(argumentList);
|
|
||||||
|
|
||||||
LispString quotedName = (LispString) argumentList.getFirst();
|
|
||||||
String fileName = removeSurroundingQuotes(quotedName.toString());
|
|
||||||
|
|
||||||
return processFile(fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String removeSurroundingQuotes(String fileName) {
|
|
||||||
return fileName.substring(1, (fileName.length() - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
private SExpression processFile(String fileName) {
|
|
||||||
boolean isSuccessful = false;
|
|
||||||
String prefixedFileName = prefixFileNameIfNecessary(fileName);
|
|
||||||
LispParser parser = attemptToCreateParserOnFile(prefixedFileName);
|
|
||||||
|
|
||||||
if (parser != null)
|
|
||||||
isSuccessful = isSuccessfulEvaluationWithPathPrefix(prefixedFileName, parser);
|
|
||||||
|
|
||||||
return isSuccessful ? Symbol.Companion.getT() : Nil.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String prefixFileNameIfNecessary(String fileName) {
|
|
||||||
if (pathPrefixes.isEmpty())
|
|
||||||
return environment.getPath() + fileName;
|
|
||||||
|
|
||||||
return pathPrefixes.peek() + fileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
private LispParser attemptToCreateParserOnFile(String fileName) {
|
|
||||||
LispParser parser = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
parser = new LispParser(new FileInputStream(fileName), fileName);
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
environment.getErrorManager().handle(new CouldNotLoadFileWarning(fileName));
|
|
||||||
}
|
|
||||||
|
|
||||||
return parser;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isSuccessfulEvaluationWithPathPrefix(String prefixedFileName, LispParser parser) {
|
|
||||||
pathPrefixes.push(Path.INSTANCE.getPathPrefix(prefixedFileName));
|
|
||||||
boolean isSuccessful = isSuccessfulEvaluation(parser);
|
|
||||||
pathPrefixes.pop();
|
|
||||||
|
|
||||||
return isSuccessful;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isSuccessfulEvaluation(LispParser parser) {
|
|
||||||
while (!parser.isEof()) {
|
|
||||||
try {
|
|
||||||
eval(parser.nextSExpression());
|
|
||||||
} catch (LispException e) {
|
|
||||||
environment.getErrorManager().handle(e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class CouldNotLoadFileWarning extends LispWarning {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
private String fileName;
|
|
||||||
|
|
||||||
public CouldNotLoadFileWarning(String fileName) {
|
|
||||||
this.fileName = fileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getMessage() {
|
|
||||||
return format("could not load ''{0}''", fileName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
package function.builtin
|
||||||
|
|
||||||
|
import environment.RuntimeEnvironment
|
||||||
|
import error.LispException
|
||||||
|
import error.LispWarning
|
||||||
|
import function.ArgumentValidator
|
||||||
|
import function.FunctionNames
|
||||||
|
import function.LispFunction
|
||||||
|
import function.builtin.Eval.Companion.eval
|
||||||
|
import parser.LispParser
|
||||||
|
import sexpression.Cons
|
||||||
|
import sexpression.LispString
|
||||||
|
import sexpression.Nil
|
||||||
|
import sexpression.SExpression
|
||||||
|
import sexpression.Symbol
|
||||||
|
import util.Path
|
||||||
|
import java.io.FileInputStream
|
||||||
|
import java.io.FileNotFoundException
|
||||||
|
import java.util.Stack
|
||||||
|
|
||||||
|
@FunctionNames("LOAD")
|
||||||
|
class Load(name: String) : LispFunction() {
|
||||||
|
|
||||||
|
private val argumentValidator: ArgumentValidator = ArgumentValidator(name).apply {
|
||||||
|
setExactNumberOfArguments(1)
|
||||||
|
setEveryArgumentExpectedType(LispString::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun call(argumentList: Cons): SExpression {
|
||||||
|
argumentValidator.validate(argumentList)
|
||||||
|
|
||||||
|
val quotedName = argumentList.first.toString()
|
||||||
|
val fileName = quotedName.removeSurrounding("\"")
|
||||||
|
|
||||||
|
return processFile(fileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processFile(fileName: String): SExpression {
|
||||||
|
var isSuccessful = false
|
||||||
|
val prefixedFileName = prefixFileNameIfNecessary(fileName)
|
||||||
|
val parser = attemptToCreateParserOnFile(prefixedFileName)
|
||||||
|
|
||||||
|
if (parser != null)
|
||||||
|
isSuccessful = isSuccessfulEvaluationWithPathPrefix(prefixedFileName, parser)
|
||||||
|
|
||||||
|
return if (isSuccessful) Symbol.T else Nil
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun prefixFileNameIfNecessary(fileName: String) =
|
||||||
|
if (pathPrefixes.isEmpty())
|
||||||
|
RuntimeEnvironment.path!! + fileName
|
||||||
|
else
|
||||||
|
pathPrefixes.peek() + fileName
|
||||||
|
|
||||||
|
private fun attemptToCreateParserOnFile(fileName: String): LispParser? {
|
||||||
|
var parser: LispParser? = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
parser = LispParser(FileInputStream(fileName), fileName)
|
||||||
|
} catch (e: FileNotFoundException) {
|
||||||
|
RuntimeEnvironment.errorManager!!.handle(CouldNotLoadFileWarning(fileName))
|
||||||
|
}
|
||||||
|
|
||||||
|
return parser
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isSuccessfulEvaluationWithPathPrefix(prefixedFileName: String, parser: LispParser): Boolean {
|
||||||
|
pathPrefixes.push(Path.getPathPrefix(prefixedFileName))
|
||||||
|
val isSuccessful = isSuccessfulEvaluation(parser)
|
||||||
|
pathPrefixes.pop()
|
||||||
|
|
||||||
|
return isSuccessful
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isSuccessfulEvaluation(parser: LispParser): Boolean {
|
||||||
|
while (!parser.isEof()) {
|
||||||
|
try {
|
||||||
|
eval(parser.nextSExpression())
|
||||||
|
} catch (e: LispException) {
|
||||||
|
RuntimeEnvironment.errorManager!!.handle(e)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
class CouldNotLoadFileWarning(fileName: String) : LispWarning() {
|
||||||
|
|
||||||
|
override val message = "could not load '$fileName'"
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val pathPrefixes = Stack<String>()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,56 +0,0 @@
|
||||||
package function.builtin;
|
|
||||||
|
|
||||||
import function.ArgumentValidator;
|
|
||||||
import function.FunctionNames;
|
|
||||||
import function.LispFunction;
|
|
||||||
import sexpression.Cons;
|
|
||||||
import sexpression.SExpression;
|
|
||||||
import sexpression.Symbol;
|
|
||||||
import table.ExecutionContext;
|
|
||||||
import table.FunctionTable;
|
|
||||||
import table.SymbolTable;
|
|
||||||
|
|
||||||
@FunctionNames({ "SET" })
|
|
||||||
public class SET extends LispFunction {
|
|
||||||
|
|
||||||
public static SExpression set(Cons argumentList) {
|
|
||||||
return FunctionTable.INSTANCE.lookupFunction("SET").call(argumentList);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ArgumentValidator argumentValidator;
|
|
||||||
private ExecutionContext executionContext;
|
|
||||||
|
|
||||||
public SET(String name) {
|
|
||||||
this.argumentValidator = new ArgumentValidator(name);
|
|
||||||
this.argumentValidator.setExactNumberOfArguments(2);
|
|
||||||
this.argumentValidator.setFirstArgumentExpectedType(Symbol.class);
|
|
||||||
this.executionContext = ExecutionContext.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SExpression call(Cons argumentList) {
|
|
||||||
argumentValidator.validate(argumentList);
|
|
||||||
|
|
||||||
Cons rest = (Cons) argumentList.getRest();
|
|
||||||
SExpression symbol = argumentList.getFirst();
|
|
||||||
SExpression value = rest.getFirst();
|
|
||||||
|
|
||||||
SymbolTable table = findScopeOfSymbol(symbol);
|
|
||||||
table.set(symbol.toString(), value);
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private SymbolTable findScopeOfSymbol(SExpression symbol) {
|
|
||||||
SymbolTable table = executionContext.getScope();
|
|
||||||
|
|
||||||
while (!isSymbolInTable(symbol, table) && !table.isGlobal())
|
|
||||||
table = table.getParent();
|
|
||||||
|
|
||||||
return table;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isSymbolInTable(SExpression symbol, SymbolTable table) {
|
|
||||||
return table.contains(symbol.toString());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package function.builtin
|
||||||
|
|
||||||
|
import function.ArgumentValidator
|
||||||
|
import function.FunctionNames
|
||||||
|
import function.LispFunction
|
||||||
|
import sexpression.Cons
|
||||||
|
import sexpression.SExpression
|
||||||
|
import sexpression.Symbol
|
||||||
|
import table.ExecutionContext
|
||||||
|
import table.FunctionTable
|
||||||
|
import table.SymbolTable
|
||||||
|
|
||||||
|
@FunctionNames("SET")
|
||||||
|
class Set(name: String) : LispFunction() {
|
||||||
|
|
||||||
|
private val argumentValidator: ArgumentValidator = ArgumentValidator(name).apply {
|
||||||
|
setExactNumberOfArguments(2)
|
||||||
|
setFirstArgumentExpectedType(Symbol::class.java)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun call(argumentList: Cons): SExpression {
|
||||||
|
argumentValidator.validate(argumentList)
|
||||||
|
|
||||||
|
val rest = argumentList.rest as Cons
|
||||||
|
val symbol = argumentList.first
|
||||||
|
val value = rest.first
|
||||||
|
|
||||||
|
val table = findScopeOfSymbol(symbol)
|
||||||
|
table[symbol.toString()] = value
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findScopeOfSymbol(symbol: SExpression): SymbolTable {
|
||||||
|
var table: SymbolTable = ExecutionContext.scope
|
||||||
|
|
||||||
|
while (!isSymbolInTable(symbol, table) && !table.isGlobal())
|
||||||
|
table = table.parent!!
|
||||||
|
|
||||||
|
return table
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isSymbolInTable(symbol: SExpression, table: SymbolTable) =
|
||||||
|
table.contains(symbol.toString())
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun set(argumentList: Cons) = FunctionTable.lookupFunction("SET")!!.call(argumentList)
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ import sexpression.SExpression;
|
||||||
import sexpression.Symbol;
|
import sexpression.Symbol;
|
||||||
|
|
||||||
import static function.builtin.Eval.eval;
|
import static function.builtin.Eval.eval;
|
||||||
import static function.builtin.SET.set;
|
import static function.builtin.Set.set;
|
||||||
import static function.builtin.cons.LIST.makeList;
|
import static function.builtin.cons.LIST.makeList;
|
||||||
|
|
||||||
@FunctionNames({ "SETQ" })
|
@FunctionNames({ "SETQ" })
|
||||||
|
|
|
@ -1,180 +0,0 @@
|
||||||
package function.builtin;
|
|
||||||
|
|
||||||
import function.ArgumentValidator.DottedArgumentListException;
|
|
||||||
import function.builtin.BackquoteEvaluator.AtSignNotInCommaException;
|
|
||||||
import function.builtin.BackquoteEvaluator.AtSignNotListException;
|
|
||||||
import function.builtin.BackquoteEvaluator.NestedAtSignException;
|
|
||||||
import function.builtin.BackquoteEvaluator.NestedCommaException;
|
|
||||||
import org.junit.Test;
|
|
||||||
import sexpression.AtSignExpression;
|
|
||||||
import sexpression.BackquoteExpression;
|
|
||||||
import sexpression.CommaExpression;
|
|
||||||
import sexpression.Cons;
|
|
||||||
import sexpression.LispNumber;
|
|
||||||
import sexpression.LispString;
|
|
||||||
import sexpression.Nil;
|
|
||||||
import sexpression.SExpression;
|
|
||||||
import sexpression.Symbol;
|
|
||||||
|
|
||||||
import static testutil.TestUtilities.assertIsErrorWithMessage;
|
|
||||||
import static testutil.TestUtilities.assertSExpressionsMatch;
|
|
||||||
import static testutil.TestUtilities.makeList;
|
|
||||||
|
|
||||||
public class BackquoteEvaluatorTest {
|
|
||||||
|
|
||||||
private BackquoteEvaluator createBackquoteEvaluator(SExpression expression) {
|
|
||||||
return new BackquoteEvaluator(new BackquoteExpression(expression));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void evaluateNil() {
|
|
||||||
BackquoteEvaluator evaluator = createBackquoteEvaluator(Nil.INSTANCE);
|
|
||||||
|
|
||||||
assertSExpressionsMatch(Nil.INSTANCE, evaluator.evaluate());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void evaluateNumber() {
|
|
||||||
SExpression input = new LispNumber("99");
|
|
||||||
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
|
|
||||||
|
|
||||||
assertSExpressionsMatch(input, evaluator.evaluate());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void evaluateList() {
|
|
||||||
SExpression input = makeList(new LispNumber("1"), new LispNumber("99"));
|
|
||||||
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
|
|
||||||
|
|
||||||
assertSExpressionsMatch(input, evaluator.evaluate());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void evaluateComma() {
|
|
||||||
SExpression input = new CommaExpression(makeList(new Symbol("+"), new LispNumber("1"), new LispNumber("9")));
|
|
||||||
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
|
|
||||||
|
|
||||||
assertSExpressionsMatch(new LispNumber("10"), evaluator.evaluate());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void evaluateListWithComma() {
|
|
||||||
SExpression input = makeList(new CommaExpression(makeList(new Symbol("+"),
|
|
||||||
new LispNumber("1"),
|
|
||||||
new LispNumber("9"))));
|
|
||||||
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
|
|
||||||
|
|
||||||
assertSExpressionsMatch(makeList(new LispNumber("10")), evaluator.evaluate());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = NestedCommaException.class)
|
|
||||||
public void evaluateListWithNestedComma() {
|
|
||||||
SExpression input = makeList(new CommaExpression(new CommaExpression(new Symbol("+"))));
|
|
||||||
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
|
|
||||||
|
|
||||||
evaluator.evaluate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = AtSignNotInCommaException.class)
|
|
||||||
public void evaluateListWithNoCommaPrecedingAtSign() {
|
|
||||||
SExpression input = makeList(new AtSignExpression(makeList(new Symbol("+"))));
|
|
||||||
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
|
|
||||||
|
|
||||||
evaluator.evaluate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = AtSignNotInCommaException.class)
|
|
||||||
public void evaluateAtSign() {
|
|
||||||
SExpression input = new AtSignExpression(makeList(new Symbol("+")));
|
|
||||||
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
|
|
||||||
|
|
||||||
evaluator.evaluate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = NestedAtSignException.class)
|
|
||||||
public void evaluateListWithNestedAtSigns() {
|
|
||||||
SExpression input =
|
|
||||||
makeList(new CommaExpression(new AtSignExpression(new AtSignExpression(makeList(new Symbol("+"))))));
|
|
||||||
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
|
|
||||||
|
|
||||||
evaluator.evaluate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = NestedCommaException.class)
|
|
||||||
public void evaluateListWithCommaAfterAtSign() {
|
|
||||||
SExpression input =
|
|
||||||
makeList(new CommaExpression(new AtSignExpression(new CommaExpression(makeList(new Symbol("+"))))));
|
|
||||||
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
|
|
||||||
|
|
||||||
evaluator.evaluate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void evaluateListWithAtSign() {
|
|
||||||
SExpression input = makeList(new CommaExpression(new AtSignExpression(makeList(new Symbol("LIST"),
|
|
||||||
new LispNumber("1"),
|
|
||||||
new LispNumber("9")))));
|
|
||||||
SExpression expected = makeList(new LispNumber("1"), new LispNumber("9"));
|
|
||||||
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
|
|
||||||
|
|
||||||
assertSExpressionsMatch(expected, evaluator.evaluate());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = AtSignNotListException.class)
|
|
||||||
public void atSignDoesNotEvaluateToList() {
|
|
||||||
SExpression input = makeList(new CommaExpression(new AtSignExpression(new LispNumber("1"))));
|
|
||||||
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
|
|
||||||
|
|
||||||
evaluator.evaluate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void evaluateListWithCommasAndAtSign() {
|
|
||||||
Cons list1 = makeList(new Symbol("LIST"), new LispNumber("1"), new LispNumber("9"));
|
|
||||||
Cons list2 = makeList(new Symbol("+"), new LispNumber("20"), new LispNumber("5"));
|
|
||||||
Cons list3 = makeList(new Symbol("LIST"), new LispNumber("7"), new LispNumber("6"));
|
|
||||||
|
|
||||||
SExpression input = makeList(new LispNumber("78"),
|
|
||||||
new CommaExpression(new AtSignExpression(list1)),
|
|
||||||
new CommaExpression(list2),
|
|
||||||
new CommaExpression(list3),
|
|
||||||
new LispString("\"sky\""));
|
|
||||||
|
|
||||||
SExpression expected = makeList(new LispNumber("78"),
|
|
||||||
new LispNumber("1"),
|
|
||||||
new LispNumber("9"),
|
|
||||||
new LispNumber("25"),
|
|
||||||
makeList(new LispNumber("7"), new LispNumber("6")),
|
|
||||||
new LispString("\"sky\""));
|
|
||||||
|
|
||||||
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
|
|
||||||
|
|
||||||
assertSExpressionsMatch(expected, evaluator.evaluate());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = DottedArgumentListException.class)
|
|
||||||
public void evaluateDottedList() {
|
|
||||||
BackquoteEvaluator evaluator =
|
|
||||||
createBackquoteEvaluator(new Cons(Symbol.Companion.getT(), Symbol.Companion.getT()));
|
|
||||||
|
|
||||||
evaluator.evaluate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = DottedArgumentListException.class)
|
|
||||||
public void atSignWithDottedList() {
|
|
||||||
SExpression input = makeList(new CommaExpression(new AtSignExpression(makeList(new Symbol("CONS"),
|
|
||||||
Symbol.Companion.getT(),
|
|
||||||
Symbol.Companion.getT()))));
|
|
||||||
BackquoteEvaluator evaluator = createBackquoteEvaluator(input);
|
|
||||||
|
|
||||||
evaluator.evaluate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void backquoteExceptionsHaveCorrectAttributes() {
|
|
||||||
assertIsErrorWithMessage(new NestedCommaException());
|
|
||||||
assertIsErrorWithMessage(new NestedAtSignException());
|
|
||||||
assertIsErrorWithMessage(new AtSignNotInCommaException());
|
|
||||||
assertIsErrorWithMessage(new AtSignNotListException());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,192 @@
|
||||||
|
package function.builtin
|
||||||
|
|
||||||
|
import function.ArgumentValidator.DottedArgumentListException
|
||||||
|
import function.builtin.BackquoteEvaluator.AtSignNotInCommaException
|
||||||
|
import function.builtin.BackquoteEvaluator.AtSignNotListException
|
||||||
|
import function.builtin.BackquoteEvaluator.NestedAtSignException
|
||||||
|
import function.builtin.BackquoteEvaluator.NestedCommaException
|
||||||
|
import org.junit.jupiter.api.Assertions.assertThrows
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import sexpression.AtSignExpression
|
||||||
|
import sexpression.BackquoteExpression
|
||||||
|
import sexpression.CommaExpression
|
||||||
|
import sexpression.Cons
|
||||||
|
import sexpression.LispNumber
|
||||||
|
import sexpression.LispString
|
||||||
|
import sexpression.Nil
|
||||||
|
import sexpression.SExpression
|
||||||
|
import sexpression.Symbol
|
||||||
|
import sexpression.Symbol.Companion.T
|
||||||
|
import testutil.LispTestInstance
|
||||||
|
import testutil.TestUtilities.assertIsErrorWithMessage
|
||||||
|
import testutil.TestUtilities.assertSExpressionsMatch
|
||||||
|
import testutil.TestUtilities.makeList
|
||||||
|
|
||||||
|
@LispTestInstance
|
||||||
|
class BackquoteEvaluatorTest {
|
||||||
|
|
||||||
|
private fun createBackquoteEvaluator(expression: SExpression): BackquoteEvaluator {
|
||||||
|
return BackquoteEvaluator(BackquoteExpression(expression))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evaluateNil() {
|
||||||
|
val evaluator = createBackquoteEvaluator(Nil)
|
||||||
|
|
||||||
|
assertSExpressionsMatch(Nil, evaluator.evaluate())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evaluateNumber() {
|
||||||
|
val input = LispNumber("99")
|
||||||
|
val evaluator = createBackquoteEvaluator(input)
|
||||||
|
|
||||||
|
assertSExpressionsMatch(input, evaluator.evaluate())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evaluateList() {
|
||||||
|
val input = makeList(LispNumber("1"), LispNumber("99"))
|
||||||
|
val evaluator = createBackquoteEvaluator(input)
|
||||||
|
|
||||||
|
assertSExpressionsMatch(input, evaluator.evaluate())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evaluateComma() {
|
||||||
|
val input = CommaExpression(makeList(Symbol("+"), LispNumber("1"), LispNumber("9")))
|
||||||
|
val evaluator = createBackquoteEvaluator(input)
|
||||||
|
|
||||||
|
assertSExpressionsMatch(LispNumber("10"), evaluator.evaluate())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evaluateListWithComma() {
|
||||||
|
val input = makeList(CommaExpression(makeList(Symbol("+"), LispNumber("1"), LispNumber("9"))))
|
||||||
|
val evaluator = createBackquoteEvaluator(input)
|
||||||
|
|
||||||
|
assertSExpressionsMatch(makeList(LispNumber("10")), evaluator.evaluate())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evaluateListWithNestedComma() {
|
||||||
|
val input = makeList(CommaExpression(CommaExpression(Symbol("+"))))
|
||||||
|
val evaluator = createBackquoteEvaluator(input)
|
||||||
|
|
||||||
|
assertThrows(NestedCommaException::class.java) {
|
||||||
|
evaluator.evaluate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evaluateListWithNoCommaPrecedingAtSign() {
|
||||||
|
val input = makeList(AtSignExpression(makeList(Symbol("+"))))
|
||||||
|
val evaluator = createBackquoteEvaluator(input)
|
||||||
|
|
||||||
|
assertThrows(AtSignNotInCommaException::class.java) {
|
||||||
|
evaluator.evaluate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evaluateAtSign() {
|
||||||
|
val input = AtSignExpression(makeList(Symbol("+")))
|
||||||
|
val evaluator = createBackquoteEvaluator(input)
|
||||||
|
|
||||||
|
assertThrows(AtSignNotInCommaException::class.java) {
|
||||||
|
evaluator.evaluate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evaluateListWithNestedAtSigns() {
|
||||||
|
val input = makeList(CommaExpression(AtSignExpression(AtSignExpression(makeList(Symbol("+"))))))
|
||||||
|
val evaluator = createBackquoteEvaluator(input)
|
||||||
|
|
||||||
|
assertThrows(NestedAtSignException::class.java) {
|
||||||
|
evaluator.evaluate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evaluateListWithCommaAfterAtSign() {
|
||||||
|
val input = makeList(CommaExpression(AtSignExpression(CommaExpression(makeList(Symbol("+"))))))
|
||||||
|
val evaluator = createBackquoteEvaluator(input)
|
||||||
|
|
||||||
|
assertThrows(NestedCommaException::class.java) {
|
||||||
|
evaluator.evaluate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evaluateListWithAtSign() {
|
||||||
|
val input = makeList(CommaExpression(AtSignExpression(makeList(Symbol("LIST"),
|
||||||
|
LispNumber("1"),
|
||||||
|
LispNumber("9")))))
|
||||||
|
val expected = makeList(LispNumber("1"), LispNumber("9"))
|
||||||
|
val evaluator = createBackquoteEvaluator(input)
|
||||||
|
|
||||||
|
assertSExpressionsMatch(expected, evaluator.evaluate())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun atSignDoesNotEvaluateToList() {
|
||||||
|
val input = makeList(CommaExpression(AtSignExpression(LispNumber("1"))))
|
||||||
|
val evaluator = createBackquoteEvaluator(input)
|
||||||
|
|
||||||
|
assertThrows(AtSignNotListException::class.java) {
|
||||||
|
evaluator.evaluate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evaluateListWithCommasAndAtSign() {
|
||||||
|
val list1 = makeList(Symbol("LIST"), LispNumber("1"), LispNumber("9"))
|
||||||
|
val list2 = makeList(Symbol("+"), LispNumber("20"), LispNumber("5"))
|
||||||
|
val list3 = makeList(Symbol("LIST"), LispNumber("7"), LispNumber("6"))
|
||||||
|
|
||||||
|
val input = makeList(LispNumber("78"),
|
||||||
|
CommaExpression(AtSignExpression(list1)),
|
||||||
|
CommaExpression(list2),
|
||||||
|
CommaExpression(list3),
|
||||||
|
LispString("\"sky\""))
|
||||||
|
|
||||||
|
val expected = makeList(LispNumber("78"),
|
||||||
|
LispNumber("1"),
|
||||||
|
LispNumber("9"),
|
||||||
|
LispNumber("25"),
|
||||||
|
makeList(LispNumber("7"), LispNumber("6")),
|
||||||
|
LispString("\"sky\""))
|
||||||
|
|
||||||
|
val evaluator = createBackquoteEvaluator(input)
|
||||||
|
|
||||||
|
assertSExpressionsMatch(expected, evaluator.evaluate())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evaluateDottedList() {
|
||||||
|
val evaluator = createBackquoteEvaluator(Cons(T, T))
|
||||||
|
|
||||||
|
assertThrows(DottedArgumentListException::class.java) {
|
||||||
|
evaluator.evaluate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun atSignWithDottedList() {
|
||||||
|
val input = makeList(CommaExpression(AtSignExpression(makeList(Symbol("CONS"), T, T))))
|
||||||
|
val evaluator = createBackquoteEvaluator(input)
|
||||||
|
|
||||||
|
assertThrows(DottedArgumentListException::class.java) {
|
||||||
|
evaluator.evaluate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun backquoteExceptionsHaveCorrectAttributes() {
|
||||||
|
assertIsErrorWithMessage(NestedCommaException())
|
||||||
|
assertIsErrorWithMessage(NestedAtSignException())
|
||||||
|
assertIsErrorWithMessage(AtSignNotInCommaException())
|
||||||
|
assertIsErrorWithMessage(AtSignNotListException())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,138 +0,0 @@
|
||||||
package function.builtin;
|
|
||||||
|
|
||||||
import environment.RuntimeEnvironment;
|
|
||||||
import error.ErrorManager;
|
|
||||||
import function.ArgumentValidator.BadArgumentTypeException;
|
|
||||||
import function.ArgumentValidator.TooFewArgumentsException;
|
|
||||||
import function.ArgumentValidator.TooManyArgumentsException;
|
|
||||||
import org.junit.Test;
|
|
||||||
import testutil.SymbolAndFunctionCleaner;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static testutil.TestUtilities.evaluateString;
|
|
||||||
import static testutil.TypeAssertions.assertNil;
|
|
||||||
import static testutil.TypeAssertions.assertT;
|
|
||||||
|
|
||||||
public class LOADTest extends SymbolAndFunctionCleaner {
|
|
||||||
|
|
||||||
private ByteArrayOutputStream outputStream;
|
|
||||||
private ByteArrayOutputStream errorOutputStream;
|
|
||||||
private RuntimeEnvironment environment;
|
|
||||||
|
|
||||||
public LOADTest() {
|
|
||||||
this.environment = RuntimeEnvironment.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertWarningMessagePrinted() {
|
|
||||||
assertTrue(outputStream.toByteArray().length > 0);
|
|
||||||
assertTrue(errorOutputStream.toByteArray().length == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertErrorMessagePrinted() {
|
|
||||||
assertTrue(errorOutputStream.toByteArray().length > 0);
|
|
||||||
assertTrue(outputStream.toByteArray().length == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertNothingPrinted() {
|
|
||||||
assertTrue(errorOutputStream.toByteArray().length == 0);
|
|
||||||
assertTrue(outputStream.toByteArray().length == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void additionalSetUp() {
|
|
||||||
outputStream = new ByteArrayOutputStream();
|
|
||||||
errorOutputStream = new ByteArrayOutputStream();
|
|
||||||
|
|
||||||
environment.reset();
|
|
||||||
environment.setOutput(new PrintStream(outputStream));
|
|
||||||
environment.setErrorOutput(new PrintStream(errorOutputStream));
|
|
||||||
environment.setErrorManager(new ErrorManager());
|
|
||||||
environment.setPath("");
|
|
||||||
environment.setWarningOutputDecorator(s -> s);
|
|
||||||
environment.setErrorOutputDecorator(s -> s);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void additionalTearDown() {
|
|
||||||
environment.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void loadEmptyFileName_ReturnsNilAndPrintsWarning() {
|
|
||||||
String input = "(load \"\")";
|
|
||||||
|
|
||||||
assertNil(evaluateString(input));
|
|
||||||
assertWarningMessagePrinted();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void loadGoodFile_ReturnsTAndPrintsNothing() {
|
|
||||||
String file = LOADTest.class.getResource("load-good.lisp").getFile();
|
|
||||||
String input = "(load \"" + file + "\")";
|
|
||||||
|
|
||||||
assertT(evaluateString(input));
|
|
||||||
assertNothingPrinted();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void loadBadFile_ReturnsNilAndPrintsError() {
|
|
||||||
String file = LOADTest.class.getResource("load-bad.lisp").getFile();
|
|
||||||
String input = "(load \"" + file + "\")";
|
|
||||||
|
|
||||||
assertNil(evaluateString(input));
|
|
||||||
assertErrorMessagePrinted();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void loadNonExistentFile_ReturnsNilAndPrintsWarning() {
|
|
||||||
String input = "(load \"doesNotExist.lisp\")";
|
|
||||||
|
|
||||||
assertNil(evaluateString(input));
|
|
||||||
assertWarningMessagePrinted();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void nestedLoadsInTheSameDirectory() {
|
|
||||||
String file = LOADTest.class.getResource("nested/nested.lisp").getFile();
|
|
||||||
String input = "(load \"" + file + "\")";
|
|
||||||
|
|
||||||
assertT(evaluateString(input));
|
|
||||||
assertNothingPrinted();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void nestedLoadsInDifferentDirectories() {
|
|
||||||
String file = LOADTest.class.getResource("nested/one/load-one.lisp").getFile();
|
|
||||||
String input = "(load \"" + file + "\")";
|
|
||||||
|
|
||||||
assertT(evaluateString(input));
|
|
||||||
assertNothingPrinted();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = BadArgumentTypeException.class)
|
|
||||||
public void loadWithBadArgumentType() {
|
|
||||||
evaluateString("(load '1)");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = TooManyArgumentsException.class)
|
|
||||||
public void loadWithTooManyArguments() {
|
|
||||||
evaluateString("(load \"1\" \"2\")");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = TooFewArgumentsException.class)
|
|
||||||
public void loadWithTooFewArguments() {
|
|
||||||
evaluateString("(load)");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void loadUsesRuntimePath() {
|
|
||||||
environment.setPath(LOADTest.class.getResource("nested/one/").getPath());
|
|
||||||
String input = "(load \"load-one.lisp\")";
|
|
||||||
|
|
||||||
assertT(evaluateString(input));
|
|
||||||
assertNothingPrinted();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
package function.builtin
|
||||||
|
|
||||||
|
import environment.RuntimeEnvironment
|
||||||
|
import error.ErrorManager
|
||||||
|
import function.ArgumentValidator.BadArgumentTypeException
|
||||||
|
import function.ArgumentValidator.TooFewArgumentsException
|
||||||
|
import function.ArgumentValidator.TooManyArgumentsException
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.jupiter.api.Assertions.assertThrows
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import testutil.LispTestInstance
|
||||||
|
import testutil.SymbolAndFunctionCleaner
|
||||||
|
import testutil.TestUtilities.evaluateString
|
||||||
|
import testutil.TypeAssertions.assertNil
|
||||||
|
import testutil.TypeAssertions.assertT
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.PrintStream
|
||||||
|
|
||||||
|
@LispTestInstance
|
||||||
|
class LoadTest : SymbolAndFunctionCleaner() {
|
||||||
|
|
||||||
|
private var outputStream = ByteArrayOutputStream()
|
||||||
|
private var errorOutputStream = ByteArrayOutputStream()
|
||||||
|
|
||||||
|
private fun assertWarningMessagePrinted() {
|
||||||
|
assertThat(outputStream.toByteArray()).isNotEmpty()
|
||||||
|
assertThat(errorOutputStream.toByteArray()).isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertErrorMessagePrinted() {
|
||||||
|
assertThat(errorOutputStream.toByteArray()).isNotEmpty()
|
||||||
|
assertThat(outputStream.toByteArray()).isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertNothingPrinted() {
|
||||||
|
assertThat(errorOutputStream.toByteArray()).isEmpty()
|
||||||
|
assertThat(outputStream.toByteArray()).isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun additionalSetUp() {
|
||||||
|
outputStream.reset()
|
||||||
|
errorOutputStream.reset()
|
||||||
|
|
||||||
|
RuntimeEnvironment.reset()
|
||||||
|
RuntimeEnvironment.output = PrintStream(outputStream)
|
||||||
|
RuntimeEnvironment.errorOutput = PrintStream(errorOutputStream)
|
||||||
|
RuntimeEnvironment.errorManager = ErrorManager()
|
||||||
|
RuntimeEnvironment.path = ""
|
||||||
|
RuntimeEnvironment.warningOutputDecorator = { s -> s }
|
||||||
|
RuntimeEnvironment.errorOutputDecorator = { s -> s }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun additionalTearDown() {
|
||||||
|
RuntimeEnvironment.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun loadEmptyFileName_ReturnsNilAndPrintsWarning() {
|
||||||
|
val input = "(load \"\")"
|
||||||
|
|
||||||
|
assertNil(evaluateString(input))
|
||||||
|
assertWarningMessagePrinted()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun loadGoodFile_ReturnsTAndPrintsNothing() {
|
||||||
|
val file = LoadTest::class.java.getResource("load-good.lisp").file
|
||||||
|
val input = "(load \"$file\")"
|
||||||
|
|
||||||
|
assertT(evaluateString(input))
|
||||||
|
assertNothingPrinted()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun loadBadFile_ReturnsNilAndPrintsError() {
|
||||||
|
val file = LoadTest::class.java.getResource("load-bad.lisp").file
|
||||||
|
val input = "(load \"$file\")"
|
||||||
|
|
||||||
|
assertNil(evaluateString(input))
|
||||||
|
assertErrorMessagePrinted()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun loadNonExistentFile_ReturnsNilAndPrintsWarning() {
|
||||||
|
val input = "(load \"doesNotExist.lisp\")"
|
||||||
|
|
||||||
|
assertNil(evaluateString(input))
|
||||||
|
assertWarningMessagePrinted()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun nestedLoadsInTheSameDirectory() {
|
||||||
|
val file = LoadTest::class.java.getResource("nested/nested.lisp").file
|
||||||
|
val input = "(load \"$file\")"
|
||||||
|
|
||||||
|
assertT(evaluateString(input))
|
||||||
|
assertNothingPrinted()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun nestedLoadsInDifferentDirectories() {
|
||||||
|
val file = LoadTest::class.java.getResource("nested/one/load-one.lisp").file
|
||||||
|
val input = "(load \"$file\")"
|
||||||
|
|
||||||
|
assertT(evaluateString(input))
|
||||||
|
assertNothingPrinted()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun loadWithBadArgumentType() {
|
||||||
|
assertThrows(BadArgumentTypeException::class.java) {
|
||||||
|
evaluateString("(load '1)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun loadWithTooManyArguments() {
|
||||||
|
assertThrows(TooManyArgumentsException::class.java) {
|
||||||
|
evaluateString("(load \"1\" \"2\")")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun loadWithTooFewArguments() {
|
||||||
|
assertThrows(TooFewArgumentsException::class.java) {
|
||||||
|
evaluateString("(load)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun loadUsesRuntimePath() {
|
||||||
|
RuntimeEnvironment.path = LoadTest::class.java.getResource("nested/one/").path
|
||||||
|
val input = "(load \"load-one.lisp\")"
|
||||||
|
|
||||||
|
assertT(evaluateString(input))
|
||||||
|
assertNothingPrinted()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,83 +0,0 @@
|
||||||
package function.builtin;
|
|
||||||
|
|
||||||
import function.ArgumentValidator.BadArgumentTypeException;
|
|
||||||
import function.ArgumentValidator.TooFewArgumentsException;
|
|
||||||
import function.ArgumentValidator.TooManyArgumentsException;
|
|
||||||
import function.builtin.Eval.UndefinedSymbolException;
|
|
||||||
import org.junit.Test;
|
|
||||||
import sexpression.LispNumber;
|
|
||||||
import table.SymbolTable;
|
|
||||||
import testutil.SymbolAndFunctionCleaner;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static testutil.TestUtilities.assertSExpressionsMatch;
|
|
||||||
import static testutil.TestUtilities.evaluateString;
|
|
||||||
|
|
||||||
public class SETTest extends SymbolAndFunctionCleaner {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void set() {
|
|
||||||
evaluateString("(set 'a 23)");
|
|
||||||
assertSExpressionsMatch(new LispNumber("23"), evaluateString("a"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void lookupDefinedSymbol() {
|
|
||||||
evaluateString("(set 'a 23)");
|
|
||||||
assertSExpressionsMatch(new LispNumber("23"), getExecutionContext().lookupSymbolValue("A"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void lookupUndefinedSymbol() {
|
|
||||||
assertNull(getExecutionContext().lookupSymbolValue("A"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setGlobalVariable() {
|
|
||||||
evaluateString("(set 'a 23)");
|
|
||||||
SymbolTable global = getExecutionContext().getScope();
|
|
||||||
getExecutionContext().setScope(new SymbolTable(global));
|
|
||||||
|
|
||||||
evaluateString("(set 'a 94)");
|
|
||||||
getExecutionContext().setScope(global);
|
|
||||||
assertSExpressionsMatch(new LispNumber("94"), evaluateString("a"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = UndefinedSymbolException.class)
|
|
||||||
public void setLocalVariableDefined_DoesNotSetGlobal() {
|
|
||||||
SymbolTable global = getExecutionContext().getScope();
|
|
||||||
SymbolTable local = new SymbolTable(global);
|
|
||||||
local.set("A", new LispNumber("99"));
|
|
||||||
getExecutionContext().setScope(local);
|
|
||||||
|
|
||||||
evaluateString("(set 'a 94)");
|
|
||||||
getExecutionContext().setScope(global);
|
|
||||||
evaluateString("a");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setLocalVariableUndefined_SetsGlobal() {
|
|
||||||
SymbolTable global = getExecutionContext().getScope();
|
|
||||||
SymbolTable local = new SymbolTable(global);
|
|
||||||
getExecutionContext().setScope(local);
|
|
||||||
|
|
||||||
evaluateString("(set 'a 94)");
|
|
||||||
getExecutionContext().setScope(global);
|
|
||||||
assertSExpressionsMatch(new LispNumber("94"), evaluateString("a"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = BadArgumentTypeException.class)
|
|
||||||
public void setWithNonSymbol() {
|
|
||||||
evaluateString("(set '1 2)");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = TooFewArgumentsException.class)
|
|
||||||
public void setWithTooFewArguments() {
|
|
||||||
evaluateString("(set 'x)");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = TooManyArgumentsException.class)
|
|
||||||
public void setWithTooManyArguments() {
|
|
||||||
evaluateString("(set 'a 'b 'c)");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
package function.builtin
|
||||||
|
|
||||||
|
import function.ArgumentValidator.BadArgumentTypeException
|
||||||
|
import function.ArgumentValidator.TooFewArgumentsException
|
||||||
|
import function.ArgumentValidator.TooManyArgumentsException
|
||||||
|
import function.builtin.Eval.UndefinedSymbolException
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.jupiter.api.Assertions.assertThrows
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import sexpression.LispNumber
|
||||||
|
import table.SymbolTable
|
||||||
|
import testutil.LispTestInstance
|
||||||
|
import testutil.SymbolAndFunctionCleaner
|
||||||
|
import testutil.TestUtilities.assertSExpressionsMatch
|
||||||
|
import testutil.TestUtilities.evaluateString
|
||||||
|
|
||||||
|
@LispTestInstance
|
||||||
|
class SetTest : SymbolAndFunctionCleaner() {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun set() {
|
||||||
|
evaluateString("(set 'a 23)")
|
||||||
|
assertSExpressionsMatch(LispNumber("23"), evaluateString("a"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun lookupDefinedSymbol() {
|
||||||
|
evaluateString("(set 'a 23)")
|
||||||
|
assertSExpressionsMatch(LispNumber("23"), executionContext.lookupSymbolValue("A")!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun lookupUndefinedSymbol() {
|
||||||
|
assertThat(executionContext.lookupSymbolValue("A")).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setGlobalVariable() {
|
||||||
|
evaluateString("(set 'a 23)")
|
||||||
|
val global = executionContext.scope
|
||||||
|
executionContext.scope = SymbolTable(global)
|
||||||
|
|
||||||
|
evaluateString("(set 'a 94)")
|
||||||
|
executionContext.scope = global
|
||||||
|
assertSExpressionsMatch(LispNumber("94"), evaluateString("a"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setLocalVariableDefined_DoesNotSetGlobal() {
|
||||||
|
val global = executionContext.scope
|
||||||
|
val local = SymbolTable(global)
|
||||||
|
local["A"] = LispNumber("99")
|
||||||
|
executionContext.scope = local
|
||||||
|
|
||||||
|
evaluateString("(set 'a 94)")
|
||||||
|
executionContext.scope = global
|
||||||
|
|
||||||
|
assertThrows(UndefinedSymbolException::class.java) {
|
||||||
|
evaluateString("a")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setLocalVariableUndefined_SetsGlobal() {
|
||||||
|
val global = executionContext.scope
|
||||||
|
val local = SymbolTable(global)
|
||||||
|
executionContext.scope = local
|
||||||
|
|
||||||
|
evaluateString("(set 'a 94)")
|
||||||
|
executionContext.scope = global
|
||||||
|
assertSExpressionsMatch(LispNumber("94"), evaluateString("a"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setWithNonSymbol() {
|
||||||
|
assertThrows(BadArgumentTypeException::class.java) {
|
||||||
|
evaluateString("(set '1 2)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setWithTooFewArguments() {
|
||||||
|
assertThrows(TooFewArgumentsException::class.java) {
|
||||||
|
evaluateString("(set 'x)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setWithTooManyArguments() {
|
||||||
|
assertThrows(TooManyArgumentsException::class.java) {
|
||||||
|
evaluateString("(set 'a 'b 'c)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue