├── README └── Lisp.java /README: -------------------------------------------------------------------------------- 1 | == JavaLisp 2 | 3 | Lisp implementation in Java. 4 | 5 | 6 | == How to use 7 | 8 | % javac Lisp.java && java Lisp 9 | > (car '(a b c)) 10 | a 11 | > (cdr '(a b c)) 12 | (b c) 13 | > (cons 1 (cons 2 (cons 3 ()))) 14 | (1 2 3) 15 | > (defun fact (n) (if (eq n 0) 1 (* n (fact (- n 1))))) 16 | fact 17 | > (fact 10) 18 | 3628800 19 | > (defun fib (n) (if (eq n 1) 1 (if (eq n 0) 1 (+ (fib(- n 1)) (fib(- n 2)))))) 20 | fib 21 | > (fib 12) 22 | 233 23 | > (defun gen (n) (lambda (m) (setq n (+ n m)))) 24 | gen 25 | > (setq x (gen 100)) 26 | 27 | > (x 10) 28 | 110 29 | > (x 90) 30 | 200 31 | > (x 300) 32 | 500 33 | -------------------------------------------------------------------------------- /Lisp.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.IOException; 3 | import java.io.InputStreamReader; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | enum Type { 8 | NIL, NUM, SYM, ERROR, CONS, SUBR, EXPR, 9 | } 10 | 11 | class LObj { 12 | public Type tag() { return tag_; } 13 | public LObj(Type type, Object obj) { 14 | tag_ = type; 15 | data_ = obj; 16 | } 17 | public Integer num() { 18 | return (Integer)data_; 19 | } 20 | public String str() { 21 | return (String)data_; 22 | } 23 | public Cons cons() { 24 | return (Cons)data_; 25 | } 26 | public Subr subr() { 27 | return (Subr)data_; 28 | } 29 | public Expr expr() { 30 | return (Expr)data_; 31 | } 32 | 33 | @Override public String toString() { 34 | if (tag_ == Type.NIL) { 35 | return "nil"; 36 | } else if (tag_ == Type.NUM) { 37 | return num().toString(); 38 | } else if (tag_ == Type.SYM) { 39 | return str(); 40 | } else if (tag_ == Type.ERROR) { 41 | return ""; 42 | } else if (tag_ == Type.CONS) { 43 | return listToString(this); 44 | } else if (tag_ == Type.SUBR) { 45 | return ""; 46 | } else if (tag_ == Type.EXPR) { 47 | return ""; 48 | } 49 | return ""; 50 | } 51 | 52 | private String listToString(LObj obj) { 53 | String ret = ""; 54 | boolean first = true; 55 | while (obj.tag() == Type.CONS) { 56 | if (first) { 57 | first = false; 58 | } else { 59 | ret += " "; 60 | } 61 | ret += obj.cons().car.toString(); 62 | obj = obj.cons().cdr; 63 | } 64 | if (obj.tag() == Type.NIL) { 65 | return "(" + ret + ")"; 66 | } 67 | return "(" + ret + " . " + obj.toString() + ")"; 68 | } 69 | 70 | private Type tag_; 71 | private Object data_; 72 | } 73 | 74 | class Cons { 75 | public Cons(LObj a, LObj d) { 76 | car = a; 77 | cdr = d; 78 | } 79 | public LObj car; 80 | public LObj cdr; 81 | } 82 | 83 | class Subr { 84 | public LObj call(LObj args) { return args; } 85 | } 86 | 87 | class Expr { 88 | public Expr(LObj a, LObj b, LObj e) { 89 | args = a; 90 | body = b; 91 | env = e; 92 | } 93 | public LObj args; 94 | public LObj body; 95 | public LObj env; 96 | } 97 | 98 | class Util { 99 | public static LObj makeNum(Integer num) { 100 | return new LObj(Type.NUM, num); 101 | } 102 | public static LObj makeError(String str) { 103 | return new LObj(Type.ERROR, str); 104 | } 105 | public static LObj makeCons(LObj a, LObj d) { 106 | return new LObj(Type.CONS, new Cons(a, d)); 107 | } 108 | public static LObj makeSubr(Subr subr) { 109 | return new LObj(Type.SUBR, subr); 110 | } 111 | public static LObj makeExpr(LObj args, LObj env) { 112 | return new LObj(Type.EXPR, new Expr(safeCar(args), safeCdr(args), env)); 113 | } 114 | public static LObj makeSym(String str) { 115 | if (str.equals("nil")) { 116 | return kNil; 117 | } else if (!symbolMap.containsKey(str)) { 118 | symbolMap.put(str, new LObj(Type.SYM, str)); 119 | } 120 | return symbolMap.get(str); 121 | } 122 | 123 | public static LObj safeCar(LObj obj) { 124 | if (obj.tag() == Type.CONS) { 125 | return obj.cons().car; 126 | } 127 | return kNil; 128 | } 129 | public static LObj safeCdr(LObj obj) { 130 | if (obj.tag() == Type.CONS) { 131 | return obj.cons().cdr; 132 | } 133 | return kNil; 134 | } 135 | 136 | public static LObj nreverse(LObj lst) { 137 | LObj ret = kNil; 138 | while (lst.tag() == Type.CONS) { 139 | LObj tmp = lst.cons().cdr; 140 | lst.cons().cdr = ret; 141 | ret = lst; 142 | lst = tmp; 143 | } 144 | return ret; 145 | } 146 | 147 | public static LObj pairlis(LObj lst1, LObj lst2) { 148 | LObj ret = kNil; 149 | while (lst1.tag() == Type.CONS && lst2.tag() == Type.CONS) { 150 | ret = makeCons(makeCons(lst1.cons().car, lst2.cons().car), ret); 151 | lst1 = lst1.cons().cdr; 152 | lst2 = lst2.cons().cdr; 153 | } 154 | return nreverse(ret); 155 | } 156 | 157 | public final static LObj kNil = new LObj(Type.NIL, "nil"); 158 | private static Map symbolMap = new HashMap(); 159 | } 160 | 161 | class ParseState { 162 | public ParseState(LObj o, String s) { 163 | obj = o; 164 | next = s; 165 | } 166 | public LObj obj; 167 | public String next; 168 | } 169 | 170 | class Reader { 171 | private static boolean isSpace(char c) { 172 | return c == '\t' || c == '\r' || c == '\n' || c == ' '; 173 | } 174 | 175 | private static boolean isDelimiter(char c) { 176 | return c == kLPar || c == kRPar || c == kQuote || isSpace(c); 177 | } 178 | 179 | private static String skipSpaces(String str) { 180 | int i; 181 | for (i = 0; i < str.length(); i++) { 182 | if (!isSpace(str.charAt(i))) { 183 | break; 184 | } 185 | } 186 | return str.substring(i, str.length()); 187 | } 188 | 189 | private static LObj makeNumOrSym(String str) { 190 | try { 191 | return Util.makeNum(Integer.parseInt(str)); 192 | } catch (NumberFormatException e) { 193 | return Util.makeSym(str); 194 | } 195 | } 196 | 197 | private static ParseState parseError(String s) { 198 | return new ParseState(Util.makeError(s), ""); 199 | } 200 | 201 | private static ParseState readAtom(String str) { 202 | String next = ""; 203 | for (int i = 0; i < str.length(); i++) { 204 | if (isDelimiter(str.charAt(i))) { 205 | next = str.substring(i, str.length()); 206 | str = str.substring(0, i); 207 | break; 208 | } 209 | } 210 | return new ParseState(makeNumOrSym(str), next); 211 | } 212 | 213 | public static ParseState read(String str) { 214 | str = skipSpaces(str); 215 | if (str.length() == 0) { 216 | return parseError("empty input"); 217 | } else if (str.charAt(0) == kRPar) { 218 | return parseError("invalid syntax: " + str); 219 | } else if (str.charAt(0) == kLPar) { 220 | return readList(str.substring(1, str.length())); 221 | } else if (str.charAt(0) == kQuote) { 222 | ParseState tmp = read(str.substring(1, str.length())); 223 | return new ParseState( 224 | Util.makeCons(Util.makeSym("quote"), 225 | Util.makeCons(tmp.obj, Util.kNil)), 226 | tmp.next); 227 | } 228 | return readAtom(str); 229 | } 230 | 231 | private static ParseState readList(String str) { 232 | LObj ret = Util.kNil; 233 | while (true) { 234 | str = skipSpaces(str); 235 | if (str.length() == 0) { 236 | return parseError("unfinished parenthesis"); 237 | } else if (str.charAt(0) == kRPar) { 238 | break; 239 | } 240 | ParseState tmp = read(str); 241 | if (tmp.obj.tag() == Type.ERROR) { 242 | return tmp; 243 | } 244 | ret = Util.makeCons(tmp.obj, ret); 245 | str = tmp.next; 246 | } 247 | return new ParseState(Util.nreverse(ret), 248 | str.substring(1, str.length())); 249 | } 250 | 251 | private final static char kLPar = '('; 252 | private final static char kRPar = ')'; 253 | private final static char kQuote = '\''; 254 | 255 | } 256 | 257 | class Evaluator { 258 | private static LObj findVar(LObj sym, LObj env) { 259 | while (env.tag() == Type.CONS) { 260 | LObj alist = env.cons().car; 261 | while (alist.tag() == Type.CONS) { 262 | if (alist.cons().car.cons().car == sym) { 263 | return alist.cons().car; 264 | } 265 | alist = alist.cons().cdr; 266 | } 267 | env = env.cons().cdr; 268 | } 269 | return Util.kNil; 270 | } 271 | 272 | public static void addToEnv(LObj sym, LObj val, LObj env) { 273 | env.cons().car = Util.makeCons(Util.makeCons(sym, val), env.cons().car); 274 | } 275 | 276 | public static LObj eval(LObj obj, LObj env) { 277 | if (obj.tag() == Type.NIL || obj.tag() == Type.NUM || 278 | obj.tag() == Type.ERROR) { 279 | return obj; 280 | } else if (obj.tag() == Type.SYM) { 281 | LObj bind = findVar(obj, env); 282 | if (bind == Util.kNil) { 283 | return Util.makeError(obj.str() + " has no value"); 284 | } 285 | return bind.cons().cdr; 286 | } 287 | 288 | LObj op = Util.safeCar(obj); 289 | LObj args = Util.safeCdr(obj); 290 | if (op == Util.makeSym("quote")) { 291 | return Util.safeCar(args); 292 | } else if (op == Util.makeSym("if")) { 293 | if (eval(Util.safeCar(args), env) == Util.kNil) { 294 | return eval(Util.safeCar(Util.safeCdr(Util.safeCdr(args))), 295 | env); 296 | } 297 | return eval(Util.safeCar(Util.safeCdr(args)), env); 298 | } else if (op == Util.makeSym("lambda")) { 299 | return Util.makeExpr(args, env); 300 | } else if (op == Util.makeSym("defun")) { 301 | LObj expr = Util.makeExpr(Util.safeCdr(args), env); 302 | LObj sym = Util.safeCar(args); 303 | addToEnv(sym, expr, gEnv); 304 | return sym; 305 | } else if (op == Util.makeSym("setq")) { 306 | LObj val = eval(Util.safeCar(Util.safeCdr(args)), env); 307 | LObj sym = Util.safeCar(args); 308 | LObj bind = findVar(sym, env); 309 | if (bind == Util.kNil) { 310 | addToEnv(sym, val, gEnv); 311 | } else { 312 | bind.cons().cdr = val; 313 | } 314 | return val; 315 | } 316 | return apply(eval(op, env), evlis(args, env), env); 317 | } 318 | 319 | private static LObj evlis(LObj lst, LObj env) { 320 | LObj ret = Util.kNil; 321 | while (lst.tag() == Type.CONS) { 322 | LObj elm = eval(lst.cons().car, env); 323 | if (elm.tag() == Type.ERROR) { 324 | return elm; 325 | } 326 | ret = Util.makeCons(elm, ret); 327 | lst = lst.cons().cdr; 328 | } 329 | return Util.nreverse(ret); 330 | } 331 | 332 | private static LObj progn(LObj body, LObj env) { 333 | LObj ret = Util.kNil; 334 | while (body.tag() == Type.CONS) { 335 | ret = eval(body.cons().car, env); 336 | body = body.cons().cdr; 337 | } 338 | return ret; 339 | } 340 | 341 | private static LObj apply(LObj fn, LObj args, LObj env) { 342 | if (fn.tag() == Type.ERROR) { 343 | return fn; 344 | } else if (args.tag() == Type.ERROR) { 345 | return args; 346 | } else if (fn.tag() == Type.SUBR) { 347 | return fn.subr().call(args); 348 | } else if (fn.tag() == Type.EXPR) { 349 | return progn(fn.expr().body, 350 | Util.makeCons(Util.pairlis(fn.expr().args, args), 351 | fn.expr().env)); 352 | } 353 | return Util.makeError(fn.toString() + " is not function"); 354 | } 355 | 356 | private static LObj makeGlobalEnv() { 357 | Subr subrCar = new Subr() { 358 | @Override public LObj call(LObj args) { 359 | return Util.safeCar(Util.safeCar(args)); 360 | } 361 | }; 362 | Subr subrCdr = new Subr() { 363 | @Override public LObj call(LObj args) { 364 | return Util.safeCdr(Util.safeCar(args)); 365 | } 366 | }; 367 | Subr subrCons = new Subr() { 368 | @Override public LObj call(LObj args) { 369 | return Util.makeCons(Util.safeCar(args), 370 | Util.safeCar(Util.safeCdr(args))); 371 | } 372 | }; 373 | Subr subrEq = new Subr() { 374 | @Override public LObj call(LObj args) { 375 | LObj x = Util.safeCar(args); 376 | LObj y = Util.safeCar(Util.safeCdr(args)); 377 | if (x.tag() == Type.NUM && y.tag() == Type.NUM) { 378 | if (x.num() == y.num()) { 379 | return Util.makeSym("t"); 380 | } else { 381 | return Util.kNil; 382 | } 383 | } else if (x == y) { 384 | return Util.makeSym("t"); 385 | } 386 | return Util.kNil; 387 | } 388 | }; 389 | Subr subrAtom = new Subr() { 390 | @Override public LObj call(LObj args) { 391 | if (Util.safeCar(args).tag() == Type.CONS) { 392 | return Util.kNil; 393 | } 394 | return Util.makeSym("t"); 395 | } 396 | }; 397 | 398 | Subr subrNumberp = new Subr() { 399 | @Override public LObj call(LObj args) { 400 | if (Util.safeCar(args).tag() == Type.NUM) { 401 | return Util.makeSym("t"); 402 | } 403 | return Util.kNil; 404 | } 405 | }; 406 | Subr subrSymbolp = new Subr() { 407 | @Override public LObj call(LObj args) { 408 | if (Util.safeCar(args).tag() == Type.SYM) { 409 | return Util.makeSym("t"); 410 | } 411 | return Util.kNil; 412 | } 413 | }; 414 | class SubrAddOrMul extends Subr { 415 | @Override public LObj call(LObj args) { 416 | Integer ret = initVal; 417 | while (args.tag() == Type.CONS) { 418 | if (args.cons().car.tag() != Type.NUM) { 419 | return Util.makeError("wrong type"); 420 | } 421 | ret = calc(ret, args.cons().car.num()); 422 | args = args.cons().cdr; 423 | } 424 | return Util.makeNum(ret); 425 | } 426 | public SubrAddOrMul(Integer i) { 427 | initVal = i; 428 | } 429 | public Integer calc(Integer x, Integer y) { return 0; } 430 | private Integer initVal; 431 | } 432 | Subr subrAdd = new SubrAddOrMul(0) { 433 | @Override public Integer calc(Integer x, Integer y) { 434 | return x + y; 435 | } 436 | }; 437 | Subr subrMul = new SubrAddOrMul(1) { 438 | @Override public Integer calc(Integer x, Integer y) { 439 | return x * y; 440 | } 441 | }; 442 | class SubrSubOrDivOrMod extends Subr { 443 | @Override public LObj call(LObj args) { 444 | LObj x = Util.safeCar(args); 445 | LObj y = Util.safeCar(Util.safeCdr(args)); 446 | if (x.tag() != Type.NUM || y.tag() != Type.NUM) { 447 | return Util.makeError("wrong type"); 448 | } 449 | return Util.makeNum(calc(x.num(), y.num())); 450 | } 451 | public Integer calc(Integer x, Integer y) { return 0; } 452 | } 453 | Subr subrSub = new SubrSubOrDivOrMod() { 454 | @Override public Integer calc(Integer x, Integer y) { 455 | return x - y; 456 | } 457 | }; 458 | Subr subrDiv = new SubrSubOrDivOrMod() { 459 | @Override public Integer calc(Integer x, Integer y) { 460 | return x / y; 461 | } 462 | }; 463 | Subr subrMod = new SubrSubOrDivOrMod() { 464 | @Override public Integer calc(Integer x, Integer y) { 465 | return x % y; 466 | } 467 | }; 468 | 469 | LObj env = Util.makeCons(Util.kNil, Util.kNil); 470 | addToEnv(Util.makeSym("car"), Util.makeSubr(subrCar), env); 471 | addToEnv(Util.makeSym("cdr"), Util.makeSubr(subrCdr), env); 472 | addToEnv(Util.makeSym("cons"), Util.makeSubr(subrCons), env); 473 | addToEnv(Util.makeSym("eq"), Util.makeSubr(subrEq), env); 474 | addToEnv(Util.makeSym("atom"), Util.makeSubr(subrAtom), env); 475 | addToEnv(Util.makeSym("numberp"), Util.makeSubr(subrNumberp), env); 476 | addToEnv(Util.makeSym("symbolp"), Util.makeSubr(subrSymbolp), env); 477 | addToEnv(Util.makeSym("+"), Util.makeSubr(subrAdd), env); 478 | addToEnv(Util.makeSym("*"), Util.makeSubr(subrMul), env); 479 | addToEnv(Util.makeSym("-"), Util.makeSubr(subrSub), env); 480 | addToEnv(Util.makeSym("/"), Util.makeSubr(subrDiv), env); 481 | addToEnv(Util.makeSym("mod"), Util.makeSubr(subrMod), env); 482 | addToEnv(Util.makeSym("t"), Util.makeSym("t"), env); 483 | return env; 484 | } 485 | 486 | public static LObj globalEnv() { return gEnv; } 487 | 488 | private static LObj gEnv = makeGlobalEnv(); 489 | } 490 | 491 | public class Lisp { 492 | public static void main(String[] args) { 493 | InputStreamReader ireader = new InputStreamReader(System.in); 494 | BufferedReader breader = new BufferedReader(ireader); 495 | LObj gEnv = Evaluator.globalEnv(); 496 | try { 497 | String line; 498 | System.out.print("> "); 499 | while ((line = breader.readLine()) != null) { 500 | System.out.print(Evaluator.eval(Reader.read(line).obj, gEnv)); 501 | System.out.print("\n> "); 502 | } 503 | } catch (IOException e) {} 504 | } 505 | } 506 | --------------------------------------------------------------------------------