Skip to content

Instantly share code, notes, and snippets.

@kravchik
Created June 20, 2012 20:26
Show Gist options
  • Save kravchik/2962011 to your computer and use it in GitHub Desktop.
Save kravchik/2962011 to your computer and use it in GitHub Desktop.
Primitively simple lisp implementation
package simple;
import par.ParParser;
import java.util.List;
import java.util.Map;
import static yk.Util.copy;
import static yk.Util.list;
import static yk.Util.map;
import static par.ParParser.Symbol;
/**
* Kravchik Yuri
* Date: 19.06.2012
* Time: 11:32 PM
*/
public class Executor {
private Map initialContext = map();
private final Symbol inline = s("inline");
private final List<Thr> threads = list();
private void l(String name) {
initialContext.put(s(name), new Lambda(null, null));
}
private static Symbol s(String s) { return new Symbol(s.intern()); }
public void run(Object program) {//program is data - lists, symbols, Floats, Integers and Strings
l("add-float");
l("sub-float");
l("mul-float");
l("def");
l("break");
l("fn");
l("quote");
l("do");
l("spawn");
l("macro");
l("by-index");
initialContext.put(s("main"), new Lambda(program, initialContext));
threads.add(new Thr(0, initialContext, list(new ParParser.Symbol("main"))));
//run while there is threads
while(!threads.isEmpty()) {
try {
Thr thr = threads.remove(0);
System.out.println("process end with " + exec(thr.context, thr.call));
} catch (Break ignore) {}
}
}
public Object exec(Map map, List list, int index) {
while(true) {//inline with X, if result is (inline X)
Object result = exec(map, list.get(index));
if (result instanceof List && ((List)result).get(0).equals(inline)) list.set(index, ((List)result).get(1));
else return result;
}
}
public Object exec(Map map, Object o) {
if (o instanceof List) {
Object result = execCall(map, (List) o);
if (result instanceof List && ((List)result).get(0) instanceof Lambda) {
Lambda l = (Lambda) ((List) result).get(0);
return exec(l.context, l.body);
}
return result;
}
if (o instanceof Symbol) return map.get(o);
return o;
}
public Object tail(Map map, Lambda lambda) {
return list(new Lambda(lambda.body, map));
}
public Object execCall(Map map, List list) {
Lambda toCall;
Object firstItem = exec(map, list, 0);
if (!(firstItem instanceof Lambda)) throw new Error(firstItem + " is not lambda");
toCall = (Lambda) firstItem;
if (toCall == map.get(s("fn"))) return new Lambda(list.get(1), map);//TODO refactor with macros
if (toCall == map.get(s("macro"))) return new Lambda(list.get(1), map, true);
if (toCall == map.get(s("quote"))) return execQuote(map, list.get(1));
if (toCall == map.get(s("def"))) {
Object val = exec(map, list, 2);
map.put(list.get(1), val);
return val;
}
if (toCall == map.get(s("while"))) {
Object result = null;
while(true) {
Object condition = exec(map, list, 1);
if (condition.equals(Boolean.FALSE)) break;
result = exec(map, list, 2);
}
return result;
}
if (toCall == map.get(s("if"))) return !exec(map, list, 1).equals(Boolean.FALSE) ? exec(map, list, 2) : list.size() > 3 ? exec(map, list, 3) : null;
List resolved;
if (toCall.isMacro) resolved = list.subList(1, list.size());
else {
resolved = list();
for (int i = 1; i < list.size(); i++) resolved.add(exec(map, list, i));
}
if (toCall == map.get(s("do"))) return resolved.get(resolved.size() - 1);//todo implement by macros?
if (toCall == map.get(s("break"))) throw new Break();
if (toCall == map.get(s("add-float"))) return (Float)resolved.get(0) + (Float)resolved.get(1);
if (toCall == map.get(s("sub-float"))) return (Float)resolved.get(0) - (Float)resolved.get(1);
if (toCall == map.get(s("mul-float"))) return (Float)resolved.get(0) * (Float)resolved.get(1);
if (toCall == map.get(s("by-index"))) return ((List)resolved.get(1)).get((Integer) resolved.get(0));
if (toCall == map.get(s("list"))) return resolved;
if (toCall == map.get(s("spawn"))) {
threads.add(new Thr((Integer) resolved.get(0), copy(map, s("args"), resolved), (List) resolved.get(1)));
return null;
}
return tail(copy(toCall.context, s("args"), resolved), toCall);
}
public Object execQuote(Map map, Object o) {
if (o instanceof List) {
if (((ParParser.Symbol)((List)o).get(0)).name.equals("unquote")) {
return exec(map, ((List) o).get(1));
} else {
List result = list();
for (Object l : (List)o) result.add(execQuote(map, l));
return result;
}
}
return o;
}
}
package simple;
import par.ParParser;
import java.util.List;
import java.util.Map;
/**
* Kravchik Yuri
* Date: 20.06.2012
* Time: 12:46 AM
*/
public class Lambda {
public boolean isMacro;
public Map context;
public Object body;
public Lambda(Object body, Map context) {
this.body = body;
this.context = context;
}
public Lambda(Object body, Map context, boolean isMacro) {
this.body = body;
this.context = context;
this.isMacro = isMacro;
}
}
@kravchik
Copy link
Author

ParParser is javacc generated parser for lisp-like syntax.

@kravchik
Copy link
Author

Lambdas and threading is already here.
There is not shown ParParser - javacc generated parser for lisp-like syntax.
Templates, work with data structures are not implemented yet.

valid program:

(do
(def foo (fn (x) (mul-float x 2)))
(spawn 1 (quote (foo 3)))
(def x 5)
(spawn 1 (quote (foo 4)))
(add-float x 5)
(spawn 1 (quote (foo (unquote (add-float 10 x))))))

@kravchik
Copy link
Author

Added macros support.
Args is transferred as array only, but one can create macros of function with normally named args. And more - var args, map-like args also easily constructed with macros.

@kravchik
Copy link
Author

It seems like I have added not usual macros support but kind of "run-time macros". It is interesting where this could lead. "Handmade hot spot" or something. Some data-driven run-time program modifications...

@kravchik
Copy link
Author

Added automatic tail recursion, 'while' and 'if'. Executor is growing up. It's a bad sign :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment