diff --git a/src/main/java/serene/simple/BaseScope.java b/src/main/java/serene/simple/BaseScope.java new file mode 100644 index 0000000..d0d6365 --- /dev/null +++ b/src/main/java/serene/simple/BaseScope.java @@ -0,0 +1,34 @@ +package serene.simple; + +import java.util.HashMap; +import java.util.Map; + +public abstract class BaseScope { + private final Map symbolsMapping = new HashMap(); + private final BaseScope parent; + + public BaseScope() { + this(null); + } + + public BaseScope(BaseScope parent) { + this.parent = parent; + } + + public Object lookupSymbol(String symbolName) { + if (this.symbolsMapping.containsKey(symbolName)) { + return this.symbolsMapping.get(symbolName); + } + else if (this.parent != null) { + return this.parent.lookupSymbol(symbolName); + } + else { + throw new RuntimeException(String.format("Variable '%s' is not defined in this scope.", + symbolName)); + } + } + + public void insertSymbol(String symbolName, Object symbolValue) { + this.symbolsMapping.put(symbolName, symbolValue); + } +} diff --git a/src/main/java/serene/simple/DefSpecialForm.java b/src/main/java/serene/simple/DefSpecialForm.java index 1e2b061..3dfda3f 100644 --- a/src/main/java/serene/simple/DefSpecialForm.java +++ b/src/main/java/serene/simple/DefSpecialForm.java @@ -1,15 +1,15 @@ package serene.simple; public class DefSpecialForm extends SpecialForm { - public DefSpecialForm(ListNode listNode) { - super(listNode); - } + public DefSpecialForm(ListNode listNode) { + super(listNode); + } - @Override - public Object eval(Scope scope) { - SymbolNode sym = (SymbolNode) this.node.rest().first(); - scope.insertSymbol(sym.name, - this.node.rest().rest().first().eval(scope)); - return ListNode.EMPTY; - } + @Override + public Object eval(BaseScope scope) { + SymbolNode sym = (SymbolNode) this.node.rest().first(); + scope.insertSymbol(sym.name, + this.node.rest().rest().first().eval(scope)); + return ListNode.EMPTY; + } } diff --git a/src/main/java/serene/simple/FalseNode.java b/src/main/java/serene/simple/FalseNode.java index e218dee..99842d7 100644 --- a/src/main/java/serene/simple/FalseNode.java +++ b/src/main/java/serene/simple/FalseNode.java @@ -4,7 +4,7 @@ public class FalseNode extends Node { public boolean isTruthy = false; @Override - public Object eval(Scope scope) { + public Object eval(BaseScope scope) { return Boolean.FALSE; } diff --git a/src/main/java/serene/simple/FnSpecialForm.java b/src/main/java/serene/simple/FnSpecialForm.java index a569952..76bc01c 100644 --- a/src/main/java/serene/simple/FnSpecialForm.java +++ b/src/main/java/serene/simple/FnSpecialForm.java @@ -3,47 +3,47 @@ package serene.simple; import java.util.function.Function; public class FnSpecialForm extends SpecialForm { - public FnSpecialForm(ListNode paramsAndBody) { - super(paramsAndBody); - } + public FnSpecialForm(ListNode paramsAndBody) { + super(paramsAndBody); + } - @Override - public Object eval(final Scope parentScope) { - final ListNode formalParams = (ListNode) this.node.rest().first(); - final ListNode body = this.node.rest().rest(); + @Override + public Object eval(final BaseScope parentScope) { + final ListNode formalParams = (ListNode) this.node.rest().first(); + final ListNode body = this.node.rest().rest(); - return new Function() { - @Override - public Object apply(Object arguments) { - Object[] args = (Object[]) arguments; + return new Function() { + @Override + public Object apply(Object arguments) { + Object[] args = (Object[]) arguments; - Scope scope = new Scope(parentScope); - if (args.length != formalParams.length) { - throw new RuntimeException(String.format("Wrong number of arguments. Expected: %s, Got: %s.", - formalParams.length, - args.length)); - } - // Map parameter values to formal parameter names - int i = 0; - for (Node param : formalParams) { - SymbolNode paramSymbol = (SymbolNode) param; - scope.insertSymbol(paramSymbol.name, args[i]); - i++; - } + Scope scope = new Scope(parentScope); + if (args.length != formalParams.length) { + throw new RuntimeException(String.format("Wrong number of arguments. Expected: %s, Got: %s.", + formalParams.length, + args.length)); + } + // Map parameter values to formal parameter names + int i = 0; + for (Node param : formalParams) { + SymbolNode paramSymbol = (SymbolNode) param; + scope.insertSymbol(paramSymbol.name, args[i]); + i++; + } - // Evaluate body - Object output = null; - for (Node node : body) { - output = node.eval(scope); - } + // Evaluate body + Object output = null; + for (Node node : body) { + output = node.eval(scope); + } - return output; - } + return output; + } - @Override - public String toString() { - return "Function@" + System.identityHashCode(this); - } - }; - } + @Override + public String toString() { + return "Function@" + System.identityHashCode(this); + } + }; + } } diff --git a/src/main/java/serene/simple/FunctionNode.java b/src/main/java/serene/simple/FunctionNode.java index 0f35c51..1cd87d6 100644 --- a/src/main/java/serene/simple/FunctionNode.java +++ b/src/main/java/serene/simple/FunctionNode.java @@ -2,7 +2,7 @@ package serene.simple; public class FunctionNode extends Node { @Override - public Object eval(Scope scope) { + public Object eval(BaseScope scope) { return this; } diff --git a/src/main/java/serene/simple/IfSpecialForm.java b/src/main/java/serene/simple/IfSpecialForm.java index 21c44b0..b3e707d 100644 --- a/src/main/java/serene/simple/IfSpecialForm.java +++ b/src/main/java/serene/simple/IfSpecialForm.java @@ -2,24 +2,23 @@ package serene.simple; public class IfSpecialForm extends SpecialForm { + private Node pred; + private Node ifNode; + private Node elseNode; - private Node pred; - private Node ifNode; - private Node elseNode; + public IfSpecialForm(ListNode l) { + super(l); + this.pred = l.rest().first(); + this.ifNode = l.rest().rest().first(); + this.elseNode = l.rest().rest().rest().first(); + } - public IfSpecialForm(ListNode l) { - super(l); - this.pred = l.rest().first(); - this.ifNode = l.rest().rest().first(); - this.elseNode = l.rest().rest().rest().first(); - } - - @Override - public Object eval(final Scope scope) { - Object result = this.pred.eval(scope); - if (result == null || (result instanceof Boolean && (Boolean) result == false)) { - return this.elseNode.eval(scope); - } - return this.ifNode.eval(scope); + @Override + public Object eval(final BaseScope scope) { + Object result = this.pred.eval(scope); + if (result == null || (result instanceof Boolean && (Boolean) result == false)) { + return this.elseNode.eval(scope); } + return this.ifNode.eval(scope); + } } diff --git a/src/main/java/serene/simple/ListNode.java b/src/main/java/serene/simple/ListNode.java index 4475574..0ae6165 100644 --- a/src/main/java/serene/simple/ListNode.java +++ b/src/main/java/serene/simple/ListNode.java @@ -4,107 +4,125 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.function.Function; + +import serene.simple.builtin.AFn; + import static java.util.Arrays.asList; public class ListNode extends Node implements Iterable { - public static final ListNode EMPTY = new ListNode<>(); + public static final ListNode EMPTY = new ListNode<>(); - public final T first; - public final ListNode rest; - public final int length; + public final T first; + public final ListNode rest; + public final int length; - public ListNode() { - this.first = null; - this.rest = null; - this.length = 0; + public ListNode() { + this.first = null; + this.rest = null; + this.length = 0; + } + + public ListNode(T f, ListNode r) { + this.first = f; + this.rest = r; + this.length = r.length + 1; + } + + @SafeVarargs + public static ListNode list(T... objs) { + return list(asList(objs)); + } + + public static ListNode list(List objs) { + ListNode l = (ListNode) EMPTY; + for (int i = objs.size() - 1; i >= 0; i--) { + l = l.cons(objs.get(i)); + } + return l; + } + + public ListNode cons(T node) { + return new ListNode(node, this); + } + + @Override + public Object eval(BaseScope scope) { + Object f = this.first().eval(scope); + List args = new ArrayList(); + + for (T node : this.rest()) { + args.add(node.eval(scope)); } - public ListNode(T f, ListNode r) { - this.first = f; - this.rest = r; - this.length = r.length + 1; + if (f instanceof AFn) { + return evalBuiltin(scope, (AFn) f, args); } - - @SafeVarargs - public static ListNode list(T... objs) { - return list(asList(objs)); + else { + return evalFn(scope, (Function) f, args); } + } - public static ListNode list(List objs) { - ListNode l = (ListNode) EMPTY; - for (int i = objs.size() - 1; i >= 0; i--) { - l = l.cons(objs.get(i)); + public Object evalBuiltin(BaseScope scope, AFn fn, List args) { + fn.setArguments(args); + return fn.eval(scope); + } + + public Object evalFn(BaseScope scope, Function fn, List args) { + return fn.apply(args.toArray()); + } + + public T first() { + if (this != EMPTY) { + return this.first; + } + return null; + } + + public ListNode rest() { + if (this != EMPTY) { + return this.rest; + } + return (ListNode) EMPTY; + } + + + @Override + public Iterator iterator() { + return new Iterator() { + private ListNode l = ListNode.this; + + @Override + public boolean hasNext() { + return this.l != EMPTY; + } + + @Override + public T next() { + if (this.l == EMPTY) { + return null; } - return l; + T first = this.l.first; + this.l = this.l.rest; + return first; + } + + @Override + public void remove() { + throw new SereneException("Iterator is immutable"); + } + }; + } + + public String toString() { + if (this.length == 0) { + return "()"; } - public ListNode cons(T node) { - return new ListNode(node, this); + String output = "(" + this.first(); + for(Node x : this.rest()) { + output = output + " " + x.toString(); } - @Override - public Object eval(Scope scope) { - Function f = (Function) this.first().eval(scope); - List args = new ArrayList(); - - for (T node : this.rest()) { - args.add(node.eval(scope)); - } - return f.apply(args.toArray()); - } - - public T first() { - if (this != EMPTY) { - return this.first; - } - return null; - } - - public ListNode rest() { - if (this != EMPTY) { - return this.rest; - } - return (ListNode) EMPTY; - } - - - @Override - public Iterator iterator() { - return new Iterator() { - private ListNode l = ListNode.this; - - @Override - public boolean hasNext() { - return this.l != EMPTY; - } - - @Override - public T next() { - if (this.l == EMPTY) { - return null; - } - T first = this.l.first; - this.l = this.l.rest; - return first; - } - - @Override - public void remove() { - throw new SereneException("Iterator is immutable"); - } - }; - } - - public String toString() { - if (this.length == 0) { - return "()"; - } - - String output = "(" + this.first(); - for(Node x : this.rest()) { - output = output + " " + x.toString(); - } - - return output + ")"; - } + return output + ")"; + } } diff --git a/src/main/java/serene/simple/Main.java b/src/main/java/serene/simple/Main.java index 72295f8..0a93c09 100644 --- a/src/main/java/serene/simple/Main.java +++ b/src/main/java/serene/simple/Main.java @@ -10,62 +10,62 @@ import java.io.FileInputStream; public class Main { - public static void main(String[] args) throws IOException { - if (args.length == 0) { - startRepl(); - return; - } - runSerene(args[0]); + public static void main(String[] args) throws IOException { + if (args.length == 0) { + startRepl(); + return; } + runSerene(args[0]); + } - private static void startRepl() throws IOException { - Scope rootScope = Scope.getRootScope(); + private static void startRepl() throws IOException { + BaseScope rootScope = new RootScope(); - Console console = System.console(); + Console console = System.console(); - System.out.println("Serene 'simple' v0.1.0"); - while(true) { - String inputData = console.readLine("serene-> "); - //String inputData = "(fn (x) x)"; + System.out.println("Serene 'simple' v0.1.0"); + while(true) { + String inputData = console.readLine("serene-> "); + //String inputData = "(fn (x) x)"; - if (inputData == null) break; + if (inputData == null) break; - ByteArrayInputStream inputStream = new ByteArrayInputStream(inputData.getBytes()); + ByteArrayInputStream inputStream = new ByteArrayInputStream(inputData.getBytes()); - try { - ListNode nodes = Reader.read(inputStream); - Object result = ListNode.EMPTY; - - for (Node n : nodes) { - result = n.eval(rootScope); - } - - if (result != ListNode.EMPTY) { - System.out.print(";; "); - if (result == null) { - System.out.println("nil"); - } - else { - System.out.println(result.toString()); - } - } - } - catch(Exception e) { - System.out.println("Error: "); - e.printStackTrace(System.out); - } - - } - } - - private static void runSerene(String filePath) throws IOException { - Scope rootScope = Scope.getRootScope(); - - ListNode nodes = Reader.read(new FileInputStream(filePath)); + try { + ListNode nodes = Reader.read(inputStream); + Object result = ListNode.EMPTY; for (Node n : nodes) { - n.eval(rootScope); + result = n.eval(rootScope); } + + if (result != ListNode.EMPTY) { + System.out.print(";; "); + if (result == null) { + System.out.println("nil"); + } + else { + System.out.println(result.toString()); + } + } + } + catch(Exception e) { + System.out.println("Error: "); + e.printStackTrace(System.out); + } + } + } + + private static void runSerene(String filePath) throws IOException { + BaseScope rootScope = new RootScope(); + + ListNode nodes = Reader.read(new FileInputStream(filePath)); + + for (Node n : nodes) { + n.eval(rootScope); + } + } } diff --git a/src/main/java/serene/simple/NilNode.java b/src/main/java/serene/simple/NilNode.java index 3a78374..d650685 100644 --- a/src/main/java/serene/simple/NilNode.java +++ b/src/main/java/serene/simple/NilNode.java @@ -4,7 +4,7 @@ public class NilNode extends Node { public boolean isTruthy = false; @Override - public Object eval(Scope scope) { + public Object eval(BaseScope scope) { return null; } diff --git a/src/main/java/serene/simple/Node.java b/src/main/java/serene/simple/Node.java index b04bbb3..4ede8d3 100644 --- a/src/main/java/serene/simple/Node.java +++ b/src/main/java/serene/simple/Node.java @@ -1,17 +1,17 @@ package serene.simple; public abstract class Node { - public boolean isTruthy = true; + public boolean isTruthy = true; - public abstract Object eval(Scope scope); + public abstract Object eval(BaseScope scope); - public boolean equals(Node n) { - return this == n; - } + public boolean equals(Node n) { + return this == n; + } - @Override - public String toString() { - String className = this.getClass().getName(); - return String.format("%s<%s>", className, System.identityHashCode(this)); - } + @Override + public String toString() { + String className = this.getClass().getName(); + return String.format("%s<%s>", className, System.identityHashCode(this)); + } } diff --git a/src/main/java/serene/simple/NumberNode.java b/src/main/java/serene/simple/NumberNode.java index 8cab51c..e0757d0 100644 --- a/src/main/java/serene/simple/NumberNode.java +++ b/src/main/java/serene/simple/NumberNode.java @@ -8,7 +8,7 @@ public class NumberNode extends Node { } @Override - public Object eval(Scope scope) { + public Object eval(BaseScope scope) { return this.value; } diff --git a/src/main/java/serene/simple/QuoteSpecialForm.java b/src/main/java/serene/simple/QuoteSpecialForm.java index ed8a9b9..78fc364 100644 --- a/src/main/java/serene/simple/QuoteSpecialForm.java +++ b/src/main/java/serene/simple/QuoteSpecialForm.java @@ -1,21 +1,16 @@ package serene.simple; public class QuoteSpecialForm extends SpecialForm { + public QuoteSpecialForm(ListNode node) throws SereneException { + super(node); - // public QuoteSpecialForm(Node node) { - // this.x = node; - // } - - public QuoteSpecialForm(ListNode node) throws SereneException { - super(node); - - if (this.node.rest().length != 1) { - throw new SereneException("quote expects only one arguement"); - } + if (this.node.rest().length != 1) { + throw new SereneException("quote expects only one arguement"); } + } - @Override - public Object eval(final Scope scope) { - return this.node.rest().first(); - } + @Override + public Object eval(final BaseScope scope) { + return this.node.rest().first(); + } } diff --git a/src/main/java/serene/simple/RootScope.java b/src/main/java/serene/simple/RootScope.java new file mode 100644 index 0000000..2a3de03 --- /dev/null +++ b/src/main/java/serene/simple/RootScope.java @@ -0,0 +1,42 @@ +package serene.simple; + +import java.util.HashMap; +import java.util.Map; + +import serene.simple.builtin.AFn; +import serene.simple.builtin.PrintlnFn; + +public class RootScope extends BaseScope { + private final BaseScope parent; + private final HashMap symbolsMapping = new HashMap() {{ + put("println", new PrintlnFn()); + }}; + // "+", PlusFn, + // "-", MinusFn, + // "*", TimesFn, + // "/", ObelusFn, + // "mod", ModFn, + // "now", NowFn, + + + public RootScope() { + this.parent = null; + System.out.println(this.symbolsMapping.get("println")); + + } + + @Override + public Object lookupSymbol(String symbolName) { + if (this.symbolsMapping.containsKey(symbolName)) { + return this.symbolsMapping.get(symbolName); + } + else { + throw new RuntimeException(String.format("Variable '%s' is not defined in this scope.", + symbolName)); + } + } + + public void insertSymbol(String symbolName, Object symbolValue) { + this.symbolsMapping.put(symbolName, symbolValue); + } +} diff --git a/src/main/java/serene/simple/Scope.java b/src/main/java/serene/simple/Scope.java index 272fc4a..4a3275d 100644 --- a/src/main/java/serene/simple/Scope.java +++ b/src/main/java/serene/simple/Scope.java @@ -1,38 +1,14 @@ package serene.simple; -import java.util.HashMap; -public class Scope { - private final HashMap symbolsMapping = new HashMap(); - private final Scope parent; - private static final Scope root = new Scope(null); +public class Scope extends BaseScope{ + private final BaseScope parent; public Scope() { - this(null); + this(new RootScope()); } - public Scope(Scope parent) { + public Scope(BaseScope parent) { this.parent = parent; } - - public Object lookupSymbol(String symbolName) { - if (this.symbolsMapping.containsKey(symbolName)) { - return this.symbolsMapping.get(symbolName); - } - else if (this.parent != null) { - return this.parent.lookupSymbol(symbolName); - } - else { - throw new RuntimeException(String.format("Variable '%s' is not defined in this scope.", - symbolName)); - } - } - - public void insertSymbol(String symbolName, Object symbolValue) { - this.symbolsMapping.put(symbolName, symbolValue); - } - - public static Scope getRootScope() { - return Scope.root; - } } diff --git a/src/main/java/serene/simple/SpecialForm.java b/src/main/java/serene/simple/SpecialForm.java index 9764d51..7dddea5 100644 --- a/src/main/java/serene/simple/SpecialForm.java +++ b/src/main/java/serene/simple/SpecialForm.java @@ -2,37 +2,37 @@ package serene.simple; public class SpecialForm extends Node { - private static final SymbolNode DEF = new SymbolNode("def"); - private static final SymbolNode FN = new SymbolNode("fn"); - private static final SymbolNode IF = new SymbolNode("if"); - private static final SymbolNode QUOTE = new SymbolNode("quote"); + private static final SymbolNode DEF = new SymbolNode("def"); + private static final SymbolNode FN = new SymbolNode("fn"); + private static final SymbolNode IF = new SymbolNode("if"); + private static final SymbolNode QUOTE = new SymbolNode("quote"); - public ListNode node; + public ListNode node; - public SpecialForm() {} + public SpecialForm() {} - public SpecialForm(ListNode node) { - this.node = node; + public SpecialForm(ListNode node) { + this.node = node; + } + + public static Node check(ListNode l) throws SereneException { + if (l == ListNode.EMPTY) { + return l; + } else if (l.first().equals(DEF)) { + return new DefSpecialForm(l); + } else if (l.first().equals(FN)) { + return new FnSpecialForm(l); + } else if (l.first().equals(IF)) { + return new IfSpecialForm(l); + } else if (l.first().equals(QUOTE)) { + return new QuoteSpecialForm(l); } - public static Node check(ListNode l) throws SereneException { - if (l == ListNode.EMPTY) { - return l; - } else if (l.first().equals(DEF)) { - return new DefSpecialForm(l); - } else if (l.first().equals(FN)) { - return new FnSpecialForm(l); - } else if (l.first().equals(IF)) { - return new IfSpecialForm(l); - } else if (l.first().equals(QUOTE)) { - return new QuoteSpecialForm(l); - } + return l; + } - return l; - } - - @Override - public Object eval(Scope scope) throws SereneException { - throw new SereneException("Can't use SpecialForm directly"); - } + @Override + public Object eval(BaseScope scope) throws SereneException { + throw new SereneException("Can't use SpecialForm directly"); + } } diff --git a/src/main/java/serene/simple/SymbolNode.java b/src/main/java/serene/simple/SymbolNode.java index 53382a8..150ea05 100644 --- a/src/main/java/serene/simple/SymbolNode.java +++ b/src/main/java/serene/simple/SymbolNode.java @@ -22,7 +22,7 @@ public class SymbolNode extends Node { } @Override - public Object eval(Scope scope) { + public Object eval(BaseScope scope) { return scope.lookupSymbol(this.name); } diff --git a/src/main/java/serene/simple/TrueNode.java b/src/main/java/serene/simple/TrueNode.java index 6906003..cd688b4 100644 --- a/src/main/java/serene/simple/TrueNode.java +++ b/src/main/java/serene/simple/TrueNode.java @@ -2,7 +2,7 @@ package serene.simple; public class TrueNode extends Node { @Override - public Object eval(Scope scope) { + public Object eval(BaseScope scope) { return true; } diff --git a/src/main/java/serene/simple/builtin/AFn.java b/src/main/java/serene/simple/builtin/AFn.java new file mode 100644 index 0000000..cac651b --- /dev/null +++ b/src/main/java/serene/simple/builtin/AFn.java @@ -0,0 +1,31 @@ +package serene.simple.builtin; + +import java.util.Collections; +import java.util.List; + +import serene.simple.Node; + + +public abstract class AFn extends Node { + + private List args; + + public void setArguments() { + this.args = Collections.EMPTY_LIST; + } + + public void setArguments(List args) { + this.args = args; + } + + public List arguments() { + return this.args; + } + + public abstract String fnName(); + + @Override + public String toString() { + return String.format("BuiltinFn<%s>", this.fnName()); + } +} diff --git a/src/main/java/serene/simple/builtin/PrintlnFn.java b/src/main/java/serene/simple/builtin/PrintlnFn.java new file mode 100644 index 0000000..ea7a845 --- /dev/null +++ b/src/main/java/serene/simple/builtin/PrintlnFn.java @@ -0,0 +1,23 @@ +package serene.simple.builtin; + +import serene.simple.BaseScope; + + +public class PrintlnFn extends AFn { + public String fnName() { + return "println"; + }; + + public Object eval(BaseScope scope) { + String output = ""; + + for(Object x: this.arguments()) { + output = String.format( + "%s %s", output, + (String) x.toString()); + } + + System.out.println(output); + return null; + } +}