Add function for displaying execution context
- Use lambda character in addition to keyword
This commit is contained in:
parent
737502c4de
commit
62f351ec62
|
@ -10,32 +10,32 @@
|
||||||
accumulator
|
accumulator
|
||||||
(recur (- index 1) (cons index accumulator))))))
|
(recur (- index 1) (cons index accumulator))))))
|
||||||
|
|
||||||
(defmacro array (length)
|
(defun array (length)
|
||||||
(let* ((this (gensym))
|
(let* ((this)
|
||||||
(index-prefix (fuse this 'index))
|
|
||||||
(indices (call static :create-indices length))
|
(indices (call static :create-indices length))
|
||||||
(index-bindings (map (lambda (i) `(,(fuse index-prefix i))) indices))
|
(index-bindings (map (λ (i) (list (fuse 'index i))) indices)))
|
||||||
(scope `((,this) ,@index-bindings)))
|
|
||||||
|
|
||||||
`(let ,scope
|
(eval
|
||||||
(setq ,this
|
`(let ,index-bindings
|
||||||
|
|
||||||
|
(setq this
|
||||||
(dlambda
|
(dlambda
|
||||||
(:get (i)
|
(:get (i)
|
||||||
(eval (fuse ',index-prefix i)))
|
(eval (fuse 'index i)))
|
||||||
|
|
||||||
(:set (i value)
|
(:set (i value)
|
||||||
(if (and (< i ,length) (> i -1))
|
(if (and (< i ,length) (> i -1))
|
||||||
(set (fuse ',index-prefix i) value)
|
(set (fuse 'index i) value)
|
||||||
(call ,this :get i))) ;; show error
|
(call this :get i))) ;; show error
|
||||||
|
|
||||||
(:length ()
|
(:length ()
|
||||||
,length)
|
,length)
|
||||||
|
|
||||||
(t ()
|
(t ()
|
||||||
((lambda (length accumulator)
|
((λ (size accumulator)
|
||||||
(if (< length 1)
|
(if (< size 1)
|
||||||
accumulator
|
accumulator
|
||||||
(recur
|
(recur
|
||||||
(- length 1)
|
(- size 1)
|
||||||
(cons (call ,this :get (- length 1)) accumulator))))
|
(cons (call this :get (- size 1)) accumulator))))
|
||||||
,length nil))))))))
|
,length nil)))))))))
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -11,14 +11,14 @@ import sexpression.LambdaExpression;
|
||||||
import sexpression.SExpression;
|
import sexpression.SExpression;
|
||||||
import sexpression.Symbol;
|
import sexpression.Symbol;
|
||||||
|
|
||||||
@FunctionNames({ "LAMBDA" })
|
@FunctionNames({ "LAMBDA", "Λ" })
|
||||||
public class LAMBDA extends LispSpecialFunction {
|
public class LAMBDA extends LispSpecialFunction {
|
||||||
|
|
||||||
public static boolean isLambdaExpression(SExpression sexpr) {
|
public static boolean isLambdaExpression(SExpression sexpr) {
|
||||||
if (sexpr.isCons()) {
|
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;
|
return false;
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package sexpression;
|
package sexpression;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
@DisplayName("symbol")
|
@DisplayName("symbol")
|
||||||
public class Symbol extends Atom {
|
public class Symbol extends Atom {
|
||||||
|
|
||||||
|
@ -10,7 +12,7 @@ public class Symbol extends Atom {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Symbol(String text) {
|
public Symbol(String text) {
|
||||||
super(text.toUpperCase());
|
super(text.toUpperCase(Locale.ROOT));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
package stream;
|
package stream;
|
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
|
||||||
public class SafeInputStream {
|
public class SafeInputStream {
|
||||||
|
|
||||||
private InputStream underlyingStream;
|
private InputStreamReader underlyingStream;
|
||||||
|
|
||||||
public SafeInputStream(InputStream underlyingStream) {
|
public SafeInputStream(InputStream underlyingStream) {
|
||||||
this.underlyingStream = underlyingStream;
|
this.underlyingStream = new InputStreamReader(underlyingStream, UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int read() {
|
public int read() {
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
package table;
|
package table;
|
||||||
|
|
||||||
|
import static sexpression.Nil.NIL;
|
||||||
|
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
import function.LispFunction;
|
import function.LispFunction;
|
||||||
|
import sexpression.Cons;
|
||||||
import sexpression.SExpression;
|
import sexpression.SExpression;
|
||||||
|
|
||||||
public class ExecutionContext {
|
public class ExecutionContext {
|
||||||
|
@ -48,6 +51,15 @@ public class ExecutionContext {
|
||||||
return null;
|
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) {
|
public void pushFunctionCall(LispFunction function) {
|
||||||
functionCalls.push(new LispFunctionRecurInfo(function));
|
functionCalls.push(new LispFunctionRecurInfo(function));
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import function.builtin.GENSYM;
|
||||||
import function.builtin.LOAD;
|
import function.builtin.LOAD;
|
||||||
import function.builtin.PRINT;
|
import function.builtin.PRINT;
|
||||||
import function.builtin.SET;
|
import function.builtin.SET;
|
||||||
|
import function.builtin.SYMBOLS;
|
||||||
import function.builtin.SYMBOL_FUNCTION;
|
import function.builtin.SYMBOL_FUNCTION;
|
||||||
import function.builtin.cons.CONS;
|
import function.builtin.cons.CONS;
|
||||||
import function.builtin.cons.FIRST;
|
import function.builtin.cons.FIRST;
|
||||||
|
@ -106,6 +107,7 @@ public class FunctionTable {
|
||||||
allBuiltIns.add(SET.class);
|
allBuiltIns.add(SET.class);
|
||||||
allBuiltIns.add(SETQ.class);
|
allBuiltIns.add(SETQ.class);
|
||||||
allBuiltIns.add(SYMBOL_FUNCTION.class);
|
allBuiltIns.add(SYMBOL_FUNCTION.class);
|
||||||
|
allBuiltIns.add(SYMBOLS.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LispFunction lookupFunction(String functionName) {
|
public static LispFunction lookupFunction(String functionName) {
|
||||||
|
|
|
@ -1,8 +1,16 @@
|
||||||
package table;
|
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.SExpression;
|
||||||
|
import sexpression.Symbol;
|
||||||
|
|
||||||
public class SymbolTable {
|
public class SymbolTable {
|
||||||
|
|
||||||
|
@ -38,4 +46,36 @@ public class SymbolTable {
|
||||||
return parent == null;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,14 @@ public class EVALTest extends SymbolAndFunctionCleaner {
|
||||||
assertSExpressionsMatch(parseString("()"), evaluateString(input));
|
assertSExpressionsMatch(parseString("()"), evaluateString(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void evalUsesCurrentLexicalEnvironment() {
|
||||||
|
String input = "(let ((x 1)) (eval '(+ x 1)))";
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("2"), evaluateString(input));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void lookupKeywordSymbol() {
|
public void lookupKeywordSymbol() {
|
||||||
String symbol = ":symbol";
|
String symbol = ":symbol";
|
||||||
|
|
|
@ -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)");
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,6 +29,13 @@ public class LAMBDATest extends SymbolAndFunctionCleaner {
|
||||||
assertSExpressionsMatch(parseString("(LAMBDA (X) X)"), evaluateString(input));
|
assertSExpressionsMatch(parseString("(LAMBDA (X) X)"), evaluateString(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void lambdaSymbol() {
|
||||||
|
String input = "(λ (x) x)";
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("(LAMBDA (X) X)"), evaluateString(input));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void lambdaWithNoBody() {
|
public void lambdaWithNoBody() {
|
||||||
String input = "(lambda ())";
|
String input = "(lambda ())";
|
||||||
|
@ -108,6 +115,13 @@ public class LAMBDATest extends SymbolAndFunctionCleaner {
|
||||||
assertSExpressionsMatch(new LispNumber("205"), evaluateString(input));
|
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)
|
@Test(expected = TooFewArgumentsException.class)
|
||||||
public void anonymousLambdaCallWithTooFewArguments() {
|
public void anonymousLambdaCallWithTooFewArguments() {
|
||||||
evaluateString("((lambda (x) x))");
|
evaluateString("((lambda (x) x))");
|
||||||
|
|
|
@ -219,4 +219,12 @@ public class LispCommentRemovingInputStreamTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readUnicodeCharacter() {
|
||||||
|
String input = "λ";
|
||||||
|
String expectedResult = "λ";
|
||||||
|
|
||||||
|
assertEquals(expectedResult, getLispCommentRemovingInputStreamResult(input));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import static testutil.TestUtilities.assertSExpressionsMatch;
|
||||||
import static testutil.TestUtilities.makeList;
|
import static testutil.TestUtilities.makeList;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -53,7 +54,7 @@ public class SExpressionTest {
|
||||||
public void symbol_ToString() {
|
public void symbol_ToString() {
|
||||||
String input = "symbol";
|
String input = "symbol";
|
||||||
|
|
||||||
assertSExpressionMatchesString(input.toUpperCase(), new Symbol(input));
|
assertSExpressionMatchesString(input.toUpperCase(Locale.ROOT), new Symbol(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in New Issue