Add function for displaying execution context

- Use lambda character in addition to keyword
This commit is contained in:
Mike Cifelli 2018-02-11 14:17:49 -05:00
parent 737502c4de
commit 62f351ec62
14 changed files with 209 additions and 33 deletions

View File

@ -19,7 +19,7 @@
(if (> years 0)
(setq years-passed (+ years-passed years))))
(:compound-interest (years)
(:compound-interest (years)
(if (> years 0)
(begin
(setq principal

View File

@ -10,32 +10,32 @@
accumulator
(recur (- index 1) (cons index accumulator))))))
(defmacro array (length)
(let* ((this (gensym))
(index-prefix (fuse this 'index))
(defun array (length)
(let* ((this)
(indices (call static :create-indices length))
(index-bindings (map (lambda (i) `(,(fuse index-prefix i))) indices))
(scope `((,this) ,@index-bindings)))
(index-bindings (map (λ (i) (list (fuse 'index i))) indices)))
`(let ,scope
(setq ,this
(dlambda
(:get (i)
(eval (fuse ',index-prefix i)))
(eval
`(let ,index-bindings
(:set (i value)
(if (and (< i ,length) (> i -1))
(set (fuse ',index-prefix i) value)
(call ,this :get i))) ;; show error
(setq this
(dlambda
(:get (i)
(eval (fuse 'index i)))
(:length ()
,length)
(:set (i value)
(if (and (< i ,length) (> i -1))
(set (fuse 'index i) value)
(call this :get i))) ;; show error
(t ()
((lambda (length accumulator)
(if (< length 1)
accumulator
(recur
(- length 1)
(cons (call ,this :get (- length 1)) accumulator))))
,length nil))))))))
(:length ()
,length)
(t ()
((λ (size accumulator)
(if (< size 1)
accumulator
(recur
(- size 1)
(cons (call this :get (- size 1)) accumulator))))
,length nil)))))))))

View File

@ -0,0 +1,29 @@
package function.builtin;
import function.ArgumentValidator;
import function.FunctionNames;
import function.LispFunction;
import sexpression.Cons;
import sexpression.SExpression;
import table.ExecutionContext;
@FunctionNames({ "SYMBOLS" })
public class SYMBOLS extends LispFunction {
private ArgumentValidator argumentValidator;
private ExecutionContext executionContext;
public SYMBOLS(String name) {
this.argumentValidator = new ArgumentValidator(name);
this.argumentValidator.setExactNumberOfArguments(0);
this.executionContext = ExecutionContext.getInstance();
}
@Override
public SExpression call(Cons argumentList) {
argumentValidator.validate(argumentList);
return executionContext.toList();
}
}

View File

@ -11,14 +11,14 @@ import sexpression.LambdaExpression;
import sexpression.SExpression;
import sexpression.Symbol;
@FunctionNames({ "LAMBDA" })
@FunctionNames({ "LAMBDA", "Λ" })
public class LAMBDA extends LispSpecialFunction {
public static boolean isLambdaExpression(SExpression sexpr) {
if (sexpr.isCons()) {
SExpression first = ((Cons) sexpr).getFirst();
String first = ((Cons) sexpr).getFirst().toString();
return "LAMBDA".equals(first.toString());
return "LAMBDA".equals(first) || "Λ".equals(first);
}
return false;

View File

@ -1,5 +1,7 @@
package sexpression;
import java.util.Locale;
@DisplayName("symbol")
public class Symbol extends Atom {
@ -10,7 +12,7 @@ public class Symbol extends Atom {
}
public Symbol(String text) {
super(text.toUpperCase());
super(text.toUpperCase(Locale.ROOT));
}
@Override

View File

@ -1,14 +1,17 @@
package stream;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class SafeInputStream {
private InputStream underlyingStream;
private InputStreamReader underlyingStream;
public SafeInputStream(InputStream underlyingStream) {
this.underlyingStream = underlyingStream;
this.underlyingStream = new InputStreamReader(underlyingStream, UTF_8);
}
public int read() {

View File

@ -1,8 +1,11 @@
package table;
import static sexpression.Nil.NIL;
import java.util.Stack;
import function.LispFunction;
import sexpression.Cons;
import sexpression.SExpression;
public class ExecutionContext {
@ -48,6 +51,15 @@ public class ExecutionContext {
return null;
}
public Cons toList() {
Cons symbols = NIL;
for (SymbolTable t = scope; t != null; t = t.getParent())
symbols = new Cons(t.toList(), symbols);
return symbols;
}
public void pushFunctionCall(LispFunction function) {
functionCalls.push(new LispFunctionRecurInfo(function));
}

View File

@ -19,6 +19,7 @@ import function.builtin.GENSYM;
import function.builtin.LOAD;
import function.builtin.PRINT;
import function.builtin.SET;
import function.builtin.SYMBOLS;
import function.builtin.SYMBOL_FUNCTION;
import function.builtin.cons.CONS;
import function.builtin.cons.FIRST;
@ -106,6 +107,7 @@ public class FunctionTable {
allBuiltIns.add(SET.class);
allBuiltIns.add(SETQ.class);
allBuiltIns.add(SYMBOL_FUNCTION.class);
allBuiltIns.add(SYMBOLS.class);
}
public static LispFunction lookupFunction(String functionName) {

View File

@ -1,8 +1,16 @@
package table;
import java.util.HashMap;
import static function.builtin.cons.LIST.makeList;
import static sexpression.Nil.NIL;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import sexpression.Cons;
import sexpression.SExpression;
import sexpression.Symbol;
public class SymbolTable {
@ -38,4 +46,36 @@ public class SymbolTable {
return parent == null;
}
public Cons toList() {
Cons context = NIL;
for (Entry<String, SExpression> binding : getSortedBindings())
context = append(context, makeList(makeSymbolValuePair(binding)));
return context;
}
private Set<Entry<String, SExpression>> getSortedBindings() {
return new TreeMap<>(table).entrySet();
}
private Cons makeSymbolValuePair(Entry<String, SExpression> binding) {
return new Cons(new Symbol(binding.getKey()), makeList(binding.getValue()));
}
// TODO - extract into built in append function
private Cons append(Cons firstList, Cons secondList) {
if (firstList.isNull())
return secondList; // FIXME - copy
Cons appendedList = firstList; // FIXME - copy
Cons appender = appendedList;
for (; !((Cons) appender.getRest()).isNull(); appender = (Cons) appender.getRest()) {}
appender.setRest(secondList); // FIXME - copy
return appendedList;
}
}

View File

@ -38,6 +38,14 @@ public class EVALTest extends SymbolAndFunctionCleaner {
assertSExpressionsMatch(parseString("()"), evaluateString(input));
}
@Test
public void evalUsesCurrentLexicalEnvironment() {
String input = "(let ((x 1)) (eval '(+ x 1)))";
assertSExpressionsMatch(parseString("2"), evaluateString(input));
}
@Test
public void lookupKeywordSymbol() {
String symbol = ":symbol";

View File

@ -0,0 +1,57 @@
package function.builtin;
import static testutil.TestUtilities.assertSExpressionsMatch;
import static testutil.TestUtilities.evaluateString;
import org.junit.Test;
import function.ArgumentValidator.TooManyArgumentsException;
import testutil.SymbolAndFunctionCleaner;
public class SYMBOLSTest extends SymbolAndFunctionCleaner {
@Test
public void noSymbols() {
assertSExpressionsMatch(evaluateString("'(nil)"), evaluateString("(symbols)"));
}
@Test
public void globalSymbol() {
evaluateString("(setq x 10)");
assertSExpressionsMatch(evaluateString("'(((x 10)))"), evaluateString("(symbols)"));
}
@Test
public void multipleSymbolsSorted() {
evaluateString("(setq x 10)");
evaluateString("(setq a 20)");
evaluateString("(setq y 30)");
evaluateString("(setq w 40)");
evaluateString("(setq e 50)");
assertSExpressionsMatch(evaluateString("'(((a 20) (e 50) (w 40) (x 10) (y 30)))"), evaluateString("(symbols)"));
}
@Test
public void fullExecutionContext() {
evaluateString("(setq x 10)");
evaluateString("(setq y 30)");
assertSExpressionsMatch(evaluateString("'(((x 10) (y 30)) ((a 100) (q 99) (z nil)))"),
evaluateString("(let ((q 99) (a 100) (z)) (symbols))"));
}
@Test
public void updateSymbolInLet() {
evaluateString("(setq x 10)");
evaluateString("(setq y 30)");
assertSExpressionsMatch(evaluateString("'(((x 10) (y 30)) ((q 99) (x 1) (z 2)))"),
evaluateString("(let ((q 99) (x 100) (z 2)) (setq x 1) (symbols))"));
}
@Test(expected = TooManyArgumentsException.class)
public void symbolsWithTooManyArguments() {
evaluateString("(symbols 1)");
}
}

View File

@ -29,6 +29,13 @@ public class LAMBDATest extends SymbolAndFunctionCleaner {
assertSExpressionsMatch(parseString("(LAMBDA (X) X)"), evaluateString(input));
}
@Test
public void lambdaSymbol() {
String input = "(λ (x) x)";
assertSExpressionsMatch(parseString("(LAMBDA (X) X)"), evaluateString(input));
}
@Test
public void lambdaWithNoBody() {
String input = "(lambda ())";
@ -108,6 +115,13 @@ public class LAMBDATest extends SymbolAndFunctionCleaner {
assertSExpressionsMatch(new LispNumber("205"), evaluateString(input));
}
@Test
public void anonymousLambdaCallWithSymbol() {
String input = "((λ (x) (+ x 1)) 3)";
assertSExpressionsMatch(new LispNumber("4"), evaluateString(input));
}
@Test(expected = TooFewArgumentsException.class)
public void anonymousLambdaCallWithTooFewArguments() {
evaluateString("((lambda (x) x))");

View File

@ -219,4 +219,12 @@ public class LispCommentRemovingInputStreamTest {
}
}
@Test
public void readUnicodeCharacter() {
String input = "λ";
String expectedResult = "λ";
assertEquals(expectedResult, getLispCommentRemovingInputStreamResult(input));
}
}

View File

@ -9,6 +9,7 @@ import static testutil.TestUtilities.assertSExpressionsMatch;
import static testutil.TestUtilities.makeList;
import java.math.BigInteger;
import java.util.Locale;
import org.junit.Test;
@ -53,7 +54,7 @@ public class SExpressionTest {
public void symbol_ToString() {
String input = "symbol";
assertSExpressionMatchesString(input.toUpperCase(), new Symbol(input));
assertSExpressionMatchesString(input.toUpperCase(Locale.ROOT), new Symbol(input));
}
@Test