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
|
||||
(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
|
||||
(eval
|
||||
`(let ,index-bindings
|
||||
|
||||
(setq this
|
||||
(dlambda
|
||||
(:get (i)
|
||||
(eval (fuse ',index-prefix i)))
|
||||
(eval (fuse 'index i)))
|
||||
|
||||
(:set (i value)
|
||||
(if (and (< i ,length) (> i -1))
|
||||
(set (fuse ',index-prefix i) value)
|
||||
(call ,this :get i))) ;; show error
|
||||
(set (fuse 'index i) value)
|
||||
(call this :get i))) ;; show error
|
||||
|
||||
(:length ()
|
||||
,length)
|
||||
|
||||
(t ()
|
||||
((lambda (length accumulator)
|
||||
(if (< length 1)
|
||||
((λ (size accumulator)
|
||||
(if (< size 1)
|
||||
accumulator
|
||||
(recur
|
||||
(- length 1)
|
||||
(cons (call ,this :get (- length 1)) accumulator))))
|
||||
,length nil))))))))
|
||||
(- size 1)
|
||||
(cons (call this :get (- size 1)) accumulator))))
|
||||
,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.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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
@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))");
|
||||
|
|
|
@ -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 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
|
||||
|
|
Loading…
Reference in New Issue