Created
June 20, 2012 20:26
-
-
Save kravchik/2962011 to your computer and use it in GitHub Desktop.
Primitively simple lisp implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
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.
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...
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
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))))))