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 static function.builtin.Eval.eval;
|
||||
import static function.builtin.SET.set;
|
||||
import static function.builtin.Set.set;
|
||||
import static function.builtin.cons.LIST.makeList;
|
||||
|
||||
@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