├── .gitignore ├── doc ├── pylisp.odp └── pylisp.pdf ├── pycon5-it └── Pylisp.pdf ├── README.txt ├── examples └── py8q.lisp └── src ├── pylisp.lisp └── pylisp.py /.gitignore: -------------------------------------------------------------------------------- 1 | *~ -------------------------------------------------------------------------------- /doc/pylisp.odp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/6502/pylisp/HEAD/doc/pylisp.odp -------------------------------------------------------------------------------- /doc/pylisp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/6502/pylisp/HEAD/doc/pylisp.pdf -------------------------------------------------------------------------------- /pycon5-it/Pylisp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/6502/pylisp/HEAD/pycon5-it/Pylisp.pdf -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | A Lisp dialect compiler targeting Python bytecode 2 | 3 | - A compile-only implementation 4 | - Works with Python 2.x / 3.x and PyPy 5 | - Macros 6 | - Lisp-2 7 | - NOT aiming at becoming a Common Lisp implementation 8 | 9 | Example session: 10 | 11 | ~/checkout/pylisp/src$ python pylisp.py 12 | PyLisp 0.002 13 | > (defun square (x) (* x x)) 14 | --> 15 | > (map #'square (range 10)) 16 | --> (0 1 4 9 16 25 36 49 64 81) 17 | > (defun adder (x) (lambda (y) (setq x (+ x y)))) 18 | --> 19 | > (let ((a (adder 10))) 20 | (dotimes (i 5) 21 | (print (funcall a 3)))) 22 | 13 23 | 16 24 | 19 25 | 22 26 | 25 27 | --> None 28 | > 29 | -------------------------------------------------------------------------------- /examples/py8q.lisp: -------------------------------------------------------------------------------- 1 | (defun n-queens (n) 2 | (let ((result (list))) 3 | (defun bitfree? (L x) 4 | (= 0 (logand (ash 1 x) L))) 5 | 6 | (defun bitset (L x) 7 | (logior (ash 1 x) L)) 8 | 9 | (defun place (i sol cols diags cdiags) 10 | (if (= i n) 11 | (push (slice sol) result) 12 | (dotimes (j n) 13 | (let ((diag (+ i j)) 14 | (cdiag (+ i (- n 1 j)))) 15 | (when (and (bitfree? cols j) 16 | (bitfree? diags diag) 17 | (bitfree? cdiags cdiag)) 18 | (place (+ 1 i) 19 | (+ (list j) sol) 20 | (bitset cols j) 21 | (bitset diags diag) 22 | (bitset cdiags cdiag))))))) 23 | 24 | (place 0 (list) 0 0 0) 25 | result)) 26 | 27 | (print (n-queens 8)) -------------------------------------------------------------------------------- /src/pylisp.lisp: -------------------------------------------------------------------------------- 1 | (setq None py:None) 2 | (setq True py:True) 3 | (setq False py:False) 4 | 5 | (fsetq + (python "lambda x, *args: x+''.join(args) if isinstance(x, str) else sum(args, x)")) 6 | (fsetq - (python "lambda x, *others: x - sum(others) if others else -x")) 7 | (fsetq * (python "lambda x, y: x * y")) 8 | (fsetq % (python "lambda x, y: x % y")) 9 | (fsetq / (python "lambda x, y: x / y")) 10 | (fsetq < (python "lambda *args: all(args[i-1] < args[i] for i in range(1, len(args)))")) 11 | (fsetq <= (python "lambda *args: all(args[i-1] <= args[i] for i in range(1, len(args)))")) 12 | (fsetq > (python "lambda *args: all(args[i-1] > args[i] for i in range(1, len(args)))")) 13 | (fsetq >= (python "lambda *args: all(args[i-1] >= args[i] for i in range(1, len(args)))")) 14 | (fsetq = (python "lambda *args: all(args[i-1] == args[i] for i in range(1, len(args)))")) 15 | (fsetq != (python "lambda x, y: x != y")) 16 | (fsetq logand (python "lambda x, y: x & y")) 17 | (fsetq logior (python "lambda x, y: x | y")) 18 | (fsetq logxor (python "lambda x, y: x ^ y")) 19 | (fsetq ash (python "lambda x, y: x << y if y >= 0 else x >> -y")) 20 | (defun print (x) (out (+ (str x) "\n"))) 21 | (fsetq list (python "lambda *args: list(args)")) 22 | (fsetq aref (python "lambda v, i: v[i]")) 23 | (fsetq set-aref (python "lambda v, i, x: (v.__setitem__(i, x), x)[1]")) 24 | (fsetq funcall (python "lambda f, *args: f(*args)")) 25 | (fsetq apply (python "lambda f, args: f(*args)")) 26 | (fsetq range (python "lambda *args: list(range(*args))")) 27 | (fsetq map (python "lambda *args: list(map(*args))")) 28 | (fsetq symbol? (python "lambda x: isinstance(x, Symbol)")) 29 | (fsetq list? (python "lambda x: isinstance(x, list)")) 30 | (fsetq string? (python "lambda x: isinstance(x, (str, unicode))")) 31 | (fsetq number? (python "lambda x: isinstance(x, (int, float, long))")) 32 | (fsetq symbol-name (python "lambda x: f_Ldemangle(x.name)")) 33 | (fsetq symbol-function (python "lambda x: globals().get('f' + x.name)")) 34 | (fsetq symbol-macro (python "lambda x: globals().get('m' + x.name)")) 35 | (defun first (x) (aref x 0)) 36 | (defun second (x) (aref x 1)) 37 | (defun last (x) (aref x -1)) 38 | (fsetq push (python "lambda x, L: (L.append(x), x)[1]")) 39 | (fsetq rest (python "lambda x: x[1:]")) 40 | (defun xlist (x) (apply #'list x)) 41 | (fsetq length py:len) 42 | (fsetq slice (python "lambda L, a=0, b=None: L[a:b]")) 43 | 44 | (defmacro dotimes (var+count *body) 45 | (list 'mapn 46 | (+ (list 'lambda (list (first var+count))) 47 | (xlist body)) 48 | (second var+count))) 49 | 50 | (defmacro dolist (var+list *body) 51 | (list 'mapl 52 | (+ (list 'lambda (list (first var+list))) 53 | (xlist body)) 54 | (second var+list))) 55 | 56 | (defmacro when (test *body) 57 | (list 'if test 58 | (+ (list 'progn) (xlist body)))) 59 | 60 | (defmacro not (x) 61 | (list 'bytecode 62 | x 63 | '(emit "UNARY_NOT"))) 64 | 65 | (defmacro unless (test *body) 66 | (list 'if (list 'not test) 67 | (+ (list 'progn) (xlist body)))) 68 | 69 | (defmacro let (bindings *body) 70 | (+ (list 'funcall 71 | (+ (list 'lambda (map #'first bindings)) 72 | (xlist body))) 73 | (map #'second bindings))) 74 | 75 | (defmacro and (x *conds) 76 | (let ((code (list 'bytecode x)) 77 | (target (label))) 78 | (dolist (y conds) 79 | (push (list 'emit "JUMP_IF_FALSE_OR_POP" target) code) 80 | (push '(stack-effect -1) code) 81 | (push y code)) 82 | (push (list 'emit "LABEL" target) code) 83 | code)) 84 | 85 | (defmacro or (x *conds) 86 | (let ((code (list 'bytecode x)) 87 | (target (label))) 88 | (dolist (y conds) 89 | (push (list 'emit "JUMP_IF_TRUE_OR_POP" target) code) 90 | (push '(stack-effect -1) code) 91 | (push y code)) 92 | (push (list 'emit "LABEL" target) code) 93 | code)) 94 | 95 | (defmacro cond (*cases) 96 | (if cases 97 | (list 'if 98 | (first (first cases)) 99 | (+ (list 'progn) (rest (first cases))) 100 | (+ (list 'cond) (xlist (rest cases)))) 101 | None)) 102 | 103 | (defun lassoc-binop (opcode x others) 104 | (let ((code (list 'bytecode x)) 105 | (op (list 'emit opcode))) 106 | (dolist (y others) 107 | (push y code) 108 | (push op code) 109 | (push '(stack-effect -1) code)) 110 | code)) 111 | 112 | (defmacro + (x *others) (if others (lassoc-binop "BINARY_ADD" x others) x)) 113 | (defmacro * (x *others) (if others (lassoc-binop "BINARY_MULTIPLY" x others) x)) 114 | 115 | (defmacro - (x *others) 116 | (if others 117 | (lassoc-binop "BINARY_SUBTRACT" x others) 118 | (list 'bytecode 119 | x 120 | '(emit "UNARY_NEGATIVE")))) 121 | 122 | (defmacro / (x *others) 123 | (if others 124 | (lassoc-binop "BINARY_TRUE_DIVIDE" x others) 125 | (list 'bytecode 126 | 1 127 | x 128 | '(emit "BINARY_TRUE_DIVIDE") 129 | '(stack-effect -1)))) 130 | 131 | (defmacro aref (x *others) 132 | (if others 133 | (lassoc-binop "BINARY_SUBSCR" x others) 134 | x)) 135 | 136 | (fsetq butlast (python "lambda L: L[:-1]")) 137 | 138 | (defmacro set-aref (x *others) 139 | (list 'bytecode 140 | (+ (list 'aref x) (xlist (butlast (butlast others)))) 141 | (aref others (- (length others) 2)) 142 | (last others) 143 | '(emit "DUP_TOP") 144 | '(stack-effect 1) 145 | '(emit "ROT_FOUR") 146 | '(emit "ROT_FOUR") 147 | '(emit "STORE_SUBSCR") 148 | '(stack-effect -1))) 149 | 150 | (defun compare-op (name index x others) 151 | (if (= (length others) 1) 152 | (list 'bytecode 153 | x 154 | (first others) 155 | (list 'emit "COMPARE_OP" index) 156 | '(stack-effect -1)) 157 | (+ (list 'funcall (list 'function name) x) (xlist others)))) 158 | 159 | (defmacro < (x *others) (compare-op '< 0 x others)) 160 | (defmacro <= (x *others) (compare-op '<= 1 x others)) 161 | (defmacro = (x *others) (compare-op '= 2 x others)) 162 | (defmacro /= (x *others) (compare-op '/= 3 x others)) 163 | (defmacro > (x *others) (compare-op '> 4 x others)) 164 | (defmacro >= (x *others) (compare-op '>= 5 x others)) 165 | 166 | (defmacro setf (place value) 167 | (if (symbol? place) 168 | (list 'setq place value) 169 | (if (and (list? place) (symbol? (first place))) 170 | (+ (list (intern (+ "set-" (symbol-name (first place))))) 171 | (rest place) 172 | (list value)) 173 | (error "Invalid setf place")))) 174 | 175 | (defun bqconst (x) 176 | (if (and (list? x) x) 177 | (if (or (= (aref x 0) '|,|) 178 | (= (aref x 0) '|`|) 179 | (= (aref x 0) '|,@|)) 180 | False 181 | (and (bqconst (first x)) 182 | (bqconst (rest x)))) 183 | True)) 184 | 185 | (defun bquote (x) 186 | (cond 187 | ((or (number? x) (string? x)) 188 | x) 189 | ((bqconst x) 190 | (list 'quote x)) 191 | ((list? x) 192 | (cond 193 | ((= (aref x 0) '|`|) 194 | (list '|`| (bquote (aref x 1)))) 195 | ((= (aref x 0) '|,|) 196 | (aref x 1)) 197 | ((= (aref x 0) '|,@|) 198 | (error ",@ must be used inside lists")) 199 | (True 200 | (let ((res (list '+)) 201 | (clist (list 'list))) 202 | (dolist (el x) 203 | (cond 204 | ((and (list? el) el (= (aref el 0) '|,@|)) 205 | (when (> (length clist) 1) 206 | (push clist res) 207 | (setq clist (list 'list))) 208 | (push (aref el 1) res)) 209 | (True 210 | (push (bquote el) clist)))) 211 | (when (> (length clist) 1) 212 | (push clist res)) 213 | (if (> (length res) 2) 214 | res 215 | (aref res 1)))))) 216 | (True (list 'quote x)))) 217 | 218 | (defmacro |`| (x) 219 | (bquote x)) 220 | 221 | (setf *gensym* 0) 222 | 223 | (defun gensym () 224 | (setf *gensym* (+ 1 *gensym*)) 225 | (intern (+ "#:" (str *gensym*) ""))) 226 | 227 | (defun macroexpand-1 (x) 228 | (if (and (list? x) 229 | x 230 | (symbol? (first x)) 231 | (symbol-macro (first x))) 232 | (apply (symbol-macro (first x)) (rest x)) 233 | x)) 234 | 235 | (fsetq dis (python "lambda x: dis.dis(x)")) 236 | 237 | (defmacro funcall (f *args) 238 | `(bytecode 239 | ,f 240 | ,@(xlist args) 241 | (emit "CALL_FUNCTION" ,(length args)) 242 | (stack-effect ,(- (length args))))) 243 | 244 | (defmacro flet (bindings *body) 245 | `(funcall (lambda ,(map (lambda (n) 246 | (intern (+ "py:f" (mangle (symbol-name (first n)))))) 247 | bindings) 248 | ,@(xlist body)) 249 | ,@(map (lambda (n) 250 | `(lambda ,@(rest n))) 251 | bindings))) 252 | 253 | (defmacro labels (bindings *body) 254 | `(funcall (lambda ,(map (lambda (n) 255 | (intern (+ "py:f" (mangle (symbol-name (first n)))))) 256 | bindings) 257 | ,@(map (lambda (n) 258 | `(setq ,(intern (+ "py:f" (mangle (symbol-name (first n))))) 259 | (lambda ,@(rest n)))) 260 | bindings) 261 | ,@(xlist body)) 262 | ,@(* (list None) (length bindings)))) 263 | 264 | (fsetq clock (python "__import__('time').time")) 265 | 266 | (defmacro time (*body) 267 | (let ((start (gensym)) 268 | (res (gensym))) 269 | `(let ((,start (clock)) 270 | (,res (progn ,@(xlist body)))) 271 | (print (+ (% "Time = %0.3f ms" (* 1000 (- (clock) ,start))))) 272 | ,res))) 273 | 274 | (defmacro while (test *body) 275 | (if body 276 | (let ((testl (label)) 277 | (loopl (label))) 278 | `(bytecode 279 | (emit "JUMP_ABSOLUTE" ,testl) 280 | (emit "LABEL" ,loopl) 281 | (progn ,@(xlist body)) 282 | (emit "POP_TOP") 283 | (stack-effect -1) 284 | (emit "LABEL" ,testl) 285 | ,test 286 | (emit "POP_JUMP_IF_TRUE" ,loopl) 287 | ,None)) 288 | (let ((loopl (label))) 289 | `(bytecode 290 | (emit "LABEL" ,loopl) 291 | ,test 292 | (emit "POP_JUMP_IF_TRUE" ,loopl) 293 | ,None)))) 294 | 295 | (defmacro push (x L) 296 | `(bytecode 297 | ,x 298 | (emit "DUP_TOP") 299 | (stack-effect 1) 300 | ,L 301 | (emit "LOAD_ATTR" "append") 302 | (emit "ROT_TWO") 303 | (emit "CALL_FUNCTION" 1) 304 | (emit "POP_TOP") 305 | (stack-effect -1))) 306 | 307 | (defun modify-aref (place modifier) 308 | `(bytecode 309 | (aref ,@(butlast place)) ;; Array 310 | (emit "DUP_TOP") ;; Array Array 311 | (stack-effect 1) 312 | ,(last place) ;; Array Array Index 313 | (emit "DUP_TOP") ;; Array Array Index Index 314 | (stack-effect 1) 315 | (emit "ROT_THREE") ;; Array Index Array Index 316 | (emit "BINARY_SUBSCR") ;; Array Index OldValue 317 | (stack-effect -1) 318 | ,@modifier ;; Array Index NewValue 319 | (emit "DUP_TOP") ;; Array Index NewValue NewValue 320 | (stack-effect 1) 321 | (emit "ROT_FOUR") ;; NewValue Array Index NewValue 322 | (emit "ROT_FOUR") ;; NewValue NewValue Array Index 323 | (emit "STORE_SUBSCR") ;; NewValue 324 | (stack-effect -2))) 325 | 326 | (defmacro inc-aref (*args) 327 | (modify-aref (xlist (butlast args)) 328 | `(,(last args) 329 | (emit "BINARY_ADD") 330 | (stack-effect -1)))) 331 | 332 | (defmacro dec-aref (*args) 333 | (modify-aref (xlist (butlast args)) 334 | `(,(last args) 335 | (emit "BINARY_SUBTRACT") 336 | (stack-effect -1)))) 337 | 338 | (defmacro incf (place *delta) 339 | (setf delta (if delta (first delta) 1)) 340 | (cond 341 | ((symbol? place) 342 | `(setf ,place (+ ,place ,delta))) 343 | ((and (list? place) 344 | place 345 | (symbol? (first place))) 346 | `(,(intern (+ "inc-" (symbol-name (first place)))) 347 | ,@(rest place) 348 | ,delta)) 349 | (True (error "Invalid incf place")))) 350 | 351 | (defmacro decf (place *delta) 352 | (setf delta (if delta (first delta) 1)) 353 | (cond 354 | ((symbol? place) 355 | `(setf ,place (- ,place ,delta))) 356 | ((and (list? place) 357 | place 358 | (symbol? (first place))) 359 | `(,(intern (+ "dec-" (symbol-name (first place)))) 360 | ,@(rest place) 361 | ,delta)) 362 | (True (error "Invalid decf place")))) 363 | 364 | (defmacro dotimes* (var+count *body) 365 | (let ((var (first var+count)) 366 | (count (gensym))) 367 | `(let ((,var 0) 368 | (,count ,(second var+count))) 369 | (while (< ,var ,count) 370 | ,@(xlist body) 371 | (incf ,var))))) 372 | 373 | (defmacro let** (bindings *body) 374 | (let ((symbols (map (lambda (b) 375 | (if (and (list? (first b)) 376 | (= (length (first b)) 2) 377 | (= 'function (first (first b)))) 378 | (intern (+ "py:f" (mangle (symbol-name (second (first b)))))) 379 | (first b))) 380 | bindings))) 381 | `(let ,(map (lambda (s) `(,s None)) symbols) 382 | ,@(let ((res (list))) 383 | (dotimes* (i (length symbols)) 384 | (push `(setf ,(aref symbols i) 385 | ,(if (list? (first (aref bindings i))) 386 | `(lambda ,@(rest (aref bindings i))) 387 | (second (aref bindings i)))) 388 | res)) 389 | res) 390 | ,@(xlist body)))) 391 | 392 | (defmacro . (base *fields) 393 | `(bytecode 394 | ,base 395 | ,@(map (lambda (f) 396 | `(emit "LOAD_ATTR" ,(symbol-name f))) 397 | fields))) 398 | 399 | (defmacro set-. (base *rest) 400 | (let ((n (length rest))) 401 | `(bytecode 402 | ,base 403 | ,@(map (lambda (f) 404 | `(emit "LOAD_ATTR" ,(symbol-name f))) 405 | (slice rest 0 (- n 2))) 406 | ,(last rest) ; obj v 407 | (emit "DUP_TOP") ; obj v v 408 | (stack-effect 1) 409 | (emit "ROT_THREE") ; v obj v 410 | (emit "ROT_THREE") ; v v obj 411 | (emit "STORE_ATTR" ,(symbol-name (aref rest (- n 2)))) 412 | (stack-effect -2)))) 413 | 414 | (defmacro tuple (*args) 415 | `(bytecode 416 | ,@(xlist args) 417 | (stack-effect ,(length args)) 418 | (emit "BUILD_TUPLE" ,(length args)) 419 | (stack-effect ,(- (length args))) 420 | (stack-effect 1))) 421 | 422 | (fsetq dict (python "dict")) 423 | (fsetq make-class (python "type")) 424 | 425 | (defmacro defobject (name fields) 426 | (let ((sname (symbol-name name))) 427 | `(progn 428 | (fsetq ,name (make-class ,sname (tuple) (dict))) 429 | (setf (. #',name __init__) 430 | (lambda (self ,@fields) 431 | ,@(map (lambda (f) 432 | `(setf (. self ,f) ,f)) 433 | fields) 434 | None)) 435 | ',name))) 436 | 437 | (defmacro defmethod (class name args *body) 438 | `(progn 439 | (setf (. (function ,class) ,name) 440 | (lambda (self ,@args) 441 | ,@(xlist body))) 442 | ',name)) 443 | 444 | (print "PyLisp 0.007") 445 | -------------------------------------------------------------------------------- /src/pylisp.py: -------------------------------------------------------------------------------- 1 | import re 2 | import sys 3 | import dis 4 | import inspect 5 | from opcode import opname, opmap 6 | import types 7 | 8 | python3 = sys.version_info > (3,) 9 | 10 | if python3: 11 | unicode = str 12 | long = int 13 | raw_input = input 14 | 15 | # When true every code object produced is immediately disassembled 16 | _L_2adebug_2a = False 17 | 18 | # Map all defined opcodes to globals 19 | for k in opmap.keys(): 20 | globals()[k.replace("+", "_PLUS_")] = opmap[k] 21 | 22 | # A few extra opcodes are defined as negative numbers and are used 23 | # during first compilation pass. Before creating the actual Code 24 | # object they're resolved to final python bytecode 25 | 26 | # Generic opcodes referring to globals, locals or refs 27 | LOAD = -1 28 | STORE = -2 29 | 30 | # Load_closure opcode 31 | LOADCL = -3 32 | 33 | # Label pseudo-instruction 34 | LABEL = -4 35 | 36 | # Utility functions 37 | 38 | def f_Lout(x): 39 | sys.stdout.write(x) 40 | 41 | def f_Lmangle(x): 42 | return "_L" + re.sub("[^A-Za-z]", lambda x: "_%02x" % ord(x.group(0)), x) 43 | 44 | def f_Ldemangle(x): 45 | return re.sub("_([0-9a-f]{2})", lambda x: chr(int(x.group(1), 16)), x[2:]) 46 | 47 | class Symbol(object): 48 | def __init__(self, name): 49 | self.name = f_Lmangle(name) 50 | 51 | def __repr__(self): 52 | return f_Ldemangle(self.name) 53 | 54 | symbols = {} 55 | 56 | def f_Lintern(x): 57 | try: 58 | _ =symbols[x] 59 | except KeyError: 60 | _ = symbols[x] = Symbol(x) 61 | return _ 62 | 63 | # Symbols known to the compiler 64 | 65 | _if = f_Lintern("if") 66 | _progn = f_Lintern("progn") 67 | _lambda = f_Lintern("lambda") 68 | _setq = f_Lintern("setq") 69 | _define = f_Lintern("define") 70 | _defun = f_Lintern("defun") 71 | _defmacro = f_Lintern("defmacro") 72 | _fsetq = f_Lintern("fsetq") 73 | _msetq = f_Lintern("msetq") 74 | _quote = f_Lintern("quote") 75 | _bquote = f_Lintern("`") 76 | _commasplice = f_Lintern(",@") 77 | _comma = f_Lintern(",") 78 | _function = f_Lintern("function") 79 | _python = f_Lintern("python") 80 | _emit = f_Lintern("emit") 81 | _bytecode = f_Lintern("bytecode") 82 | _stackeffect = f_Lintern("stack-effect") 83 | _dot = f_Lintern(".") 84 | 85 | # Global array for quoted values 86 | _globals = [] 87 | 88 | 89 | # Compiler context object 90 | 91 | ctx = None 92 | 93 | class f_Llabel(object): 94 | def __init__(self): 95 | self.address = None 96 | 97 | class Context(object): 98 | def __init__(self): 99 | self.local = {} # known local names 100 | self.outer = {} # known names from outer scopes 101 | self.captured = {} # local names captured by inner scopes 102 | 103 | self.pending_fixes = {} 104 | self.args = 0 105 | self.curstack = 1 106 | self.maxstack = 1 107 | self.constants = [None] # Why None is always here?? 108 | self.locals = [] # Local names 109 | self.names = [] # Global names 110 | self.cellvars = [] # Names of locals captured 111 | self.freevars = [] # Nonlocals 112 | self.code = [] 113 | 114 | def stack(self, *n): 115 | for delta in n: 116 | self.curstack += delta 117 | if self.curstack > self.maxstack: 118 | self.maxstack = self.curstack 119 | 120 | def make_code(self, name, rest): 121 | ctx.code.append((RETURN_VALUE,)) 122 | 123 | # Fix up generic opcodes & names 124 | free_fixup = [] 125 | for i, op in enumerate(self.code): 126 | if op[0] in (LOAD_ATTR, STORE_ATTR): 127 | if op[1] not in self.names: 128 | self.names.append(op[1]) 129 | self.code[i] = op = (op[0], self.names.index(op[1])) 130 | if op[0] in (LOAD, STORE, LOADCL): 131 | opcode, sym = op 132 | if sym in self.captured: 133 | opcode = {LOAD: LOAD_DEREF, 134 | STORE: STORE_DEREF, 135 | LOADCL: LOAD_CLOSURE}[opcode] 136 | if sym not in self.cellvars: 137 | self.cellvars.append(sym) 138 | sym = self.cellvars.index(sym) 139 | elif sym in self.local: 140 | # Opcode cannot be LOADCL 141 | opcode = {LOAD: LOAD_FAST, 142 | STORE: STORE_FAST}[opcode] 143 | if sym not in self.locals: 144 | self.locals.append(sym) 145 | sym = self.locals.index(sym) 146 | elif sym in self.outer: 147 | opcode = {LOAD: LOAD_DEREF, 148 | STORE: STORE_DEREF, 149 | LOADCL: LOAD_CLOSURE}[opcode] 150 | # Inform outer scope of the capture 151 | self.outer[sym] += 1 152 | if sym not in self.freevars: 153 | self.freevars.append(sym) 154 | # Index must be computed later because it's relative 155 | # to the number of cellvars 156 | free_fixup.append(i) 157 | else: 158 | opcode = LOAD_GLOBAL if opcode is LOAD else STORE_GLOBAL 159 | if sym not in self.names: 160 | self.names.append(sym) 161 | sym = self.names.index(sym) 162 | self.code[i] = (opcode, sym) 163 | for i in free_fixup: 164 | opcode, sym = self.code[i] 165 | self.code[i] = (opcode, 166 | len(self.cellvars) + self.freevars.index(sym)) 167 | bytestr = [] 168 | 169 | # Fix up labels 170 | pc = 0 171 | for op in self.code: 172 | if op[0] == LABEL: 173 | op[1].address = pc 174 | else: 175 | pc += 1 if len(op) == 1 else 3 176 | 177 | for op in self.code: 178 | if op[0] != LABEL: 179 | bytestr.append(op[0]) 180 | if len(op) == 2: 181 | v = op[1] 182 | if isinstance(v, f_Llabel): 183 | v = v.address 184 | bytestr.append(v & 255) 185 | bytestr.append(v >> 8) 186 | 187 | bytestr = bytes(bytestr) if python3 else "".join(map(chr, bytestr)) 188 | 189 | flags = 0 if self.cellvars else (inspect.CO_NEWLOCALS | inspect.CO_OPTIMIZED) 190 | 191 | if not self.freevars: 192 | flags |= inspect.CO_NOFREE 193 | 194 | if rest: 195 | flags |= inspect.CO_VARARGS 196 | self.args -= 1 197 | 198 | co = types.CodeType(*([self.args,] + # argcount 199 | ([0] if python3 else []) + # kw-only argcount 200 | [len(self.locals), # nlocals 201 | self.maxstack, # stacksize 202 | flags, # flags 203 | bytestr, # bytecode 204 | tuple(self.constants), # constants 205 | tuple(self.names), # names 206 | tuple(self.locals), # varnames 207 | "", # filename 208 | name, # name 209 | 0, # firstlineno 210 | bytes(), # lnotab 211 | tuple(self.freevars), # freevars 212 | tuple(self.cellvars)])) # cellvars 213 | if _L_2adebug_2a: 214 | f_Lout("Created callable %s (stacksize = %i)\n" % (name, self.maxstack)) 215 | f_Lout(" local = %r\n" % self.local) 216 | f_Lout(" outer = %r\n" % self.outer) 217 | f_Lout(" captured = %r\n" % self.captured) 218 | for k in dir(co): 219 | if k[:1] != "_": 220 | f_Lout(" %s = %r\n" % (k, getattr(co, k))) 221 | dis.dis(co) 222 | f_Lout("-" * 70 + "\n") 223 | return co 224 | 225 | # Compiles an expression to current compiler context 226 | def f_Lcompile(x): 227 | global ctx 228 | if x is None or isinstance(x, (str, int, long, float, bool)): 229 | if x not in ctx.constants: 230 | ctx.constants.append(x) 231 | ctx.code.append((LOAD_CONST, ctx.constants.index(x))) 232 | ctx.stack(1) 233 | return 234 | 235 | if isinstance(x, Symbol): 236 | if x.name[:7] == "_Lpy_3a": 237 | ctx.names.append(f_Ldemangle(x.name)[3:]) 238 | ctx.code.append((LOAD_GLOBAL, len(ctx.names)-1)) 239 | else: 240 | ctx.code.append((LOAD, x.name)) 241 | ctx.stack(1) 242 | return 243 | 244 | if isinstance(x, list): 245 | s = x[0] 246 | 247 | m = globals().get("m" + s.name) 248 | if m: 249 | f_Lcompile(m(*x[1:])) 250 | return 251 | 252 | if s is _emit: 253 | opcode = globals()[x[1]] 254 | ctx.code.append(tuple([opcode] + x[2:])) 255 | return 256 | 257 | if s is _bytecode: 258 | for y in x[1:]: 259 | f_Lcompile(y) 260 | return 261 | 262 | if s is _stackeffect: 263 | ctx.stack(*x[1:]) 264 | return 265 | 266 | if s is _setq: 267 | f_Lcompile(x[2]) 268 | ctx.code.append((DUP_TOP,)) 269 | s = x[1] 270 | if s.name[:7] == "_Lpy_3a": 271 | ctx.code.append((STORE, f_Ldemangle(s.name)[3:])) 272 | else: 273 | ctx.code.append((STORE, s.name)) 274 | ctx.stack(2, -1) 275 | return 276 | 277 | if s is _progn: 278 | if len(x) == 1: 279 | f_Lcompile(None) 280 | else: 281 | f_Lcompile(x[1]) 282 | for y in x[2:]: 283 | ctx.code.append((POP_TOP,)) 284 | ctx.stack(-1) 285 | f_Lcompile(y) 286 | return 287 | 288 | if s is _if: 289 | f_Lcompile(x[1]) 290 | elselab = f_Llabel() 291 | endlab = f_Llabel() 292 | ctx.code.append((POP_JUMP_IF_FALSE, elselab)) 293 | ctx.stack(-1) 294 | f_Lcompile(x[2]) 295 | ctx.code.append((JUMP_ABSOLUTE, endlab)) 296 | ctx.stack(-1) 297 | ctx.code.append((LABEL, elselab)) 298 | f_Lcompile(x[3] if len(x) == 4 else None) 299 | ctx.code.append((LABEL, endlab)) 300 | return 301 | 302 | if s is _quote: 303 | ctx.constants.append(x[1]) 304 | ctx.code.append((LOAD_CONST, len(ctx.constants)-1)) 305 | ctx.stack(1) 306 | return 307 | 308 | if s is _function: 309 | ctx.code.append((LOAD, "f" + x[1].name)) 310 | ctx.stack(1) 311 | return 312 | 313 | if s is _lambda: 314 | octx = ctx 315 | ctx = Context() 316 | ctx.outer = octx.outer.copy() 317 | argnames = [f_Ldemangle(v.name)[3:] if v.name[:7] == "_Lpy_3a" else v.name 318 | for v in x[1]] 319 | rest = False 320 | if argnames and argnames[-1][:5] == '_L_2a' and len(argnames[-1]) > 5: 321 | # Rest argument 322 | rest = True 323 | argnames[-1] = "_L" + argnames[-1][5:] 324 | for v in octx.local.keys(): 325 | ctx.outer[v] = 0 326 | for v in argnames: 327 | ctx.local[v] = 0 328 | ctx.args += 1 329 | # The first `arg` names in locals are the parameters, 330 | # even if a parameter is captured in a closure; this 331 | # implies there is a negligible waste of one unused 332 | # slot for locals in the stack frame for each captured 333 | # parameter. 334 | ctx.locals.append(v) 335 | f_Lcompile([_progn] + x[2:]) 336 | code = ctx.make_code("lambda", rest) 337 | for v in ctx.outer: 338 | if ctx.outer[v]: 339 | if v in octx.local: 340 | # A local in parent context has been captured 341 | octx.captured[v] = 1 342 | else: 343 | # Propagate captures to upper levels 344 | octx.outer[v] = 1 345 | nctx = ctx 346 | ctx = octx 347 | ctx.constants.append(code) 348 | if nctx.freevars: 349 | # Closure 350 | for v in nctx.freevars: 351 | ctx.code.append((LOADCL, v)) 352 | ctx.stack(1) 353 | ctx.code.append((BUILD_TUPLE, len(nctx.freevars))) 354 | ctx.stack(-len(nctx.freevars), 1) 355 | ctx.code.append((LOAD_CONST, len(ctx.constants)-1)) 356 | ctx.stack(1) 357 | if sys.version_info > (3, 3): 358 | # Python 3 qualified name 359 | ctx.constants.append("") 360 | ctx.code.append((LOAD_CONST, len(ctx.constants)-1)) 361 | ctx.stack(1, -3) 362 | else: 363 | ctx.stack(-2) 364 | ctx.code.append((MAKE_CLOSURE, 0)) 365 | else: 366 | # Function (why not an empty closure?) 367 | ctx.code.append((LOAD_CONST, len(ctx.constants)-1)) 368 | ctx.stack(1) 369 | if sys.version_info > (3, 3): 370 | # Python 3 qualified name 371 | ctx.constants.append("") 372 | ctx.code.append((LOAD_CONST, len(ctx.constants)-1)) 373 | ctx.stack(1, -2) 374 | else: 375 | ctx.stack(-1) 376 | ctx.code.append((MAKE_FUNCTION, 0)) 377 | return 378 | 379 | if s is _fsetq: 380 | f_Lcompile([_setq, f_Lintern("py:f" + x[1].name), x[2]]) 381 | return 382 | 383 | if s is _msetq: 384 | f_Lcompile([_setq, f_Lintern("py:m" + x[1].name), x[2]]) 385 | return 386 | 387 | if s is _defun: 388 | f_Lcompile([_setq, f_Lintern("py:f" + x[1].name), [_lambda] + x[2:]]) 389 | return 390 | 391 | if s is _defmacro: 392 | f_Lcompile([_setq, f_Lintern("py:m" + x[1].name), [_lambda] + x[2:]]) 393 | return 394 | 395 | ctx.code.append((LOAD, "f" + s.name)) 396 | ctx.stack(1) 397 | for y in x[1:]: 398 | f_Lcompile(y) 399 | ctx.code.append((CALL_FUNCTION, len(x)-1)) 400 | ctx.stack(-(len(x) - 1)) 401 | return 402 | 403 | raise RuntimeError("Unable to compile %r" % x) 404 | 405 | def tokenize(x): 406 | tokens = re.findall("[ \\t\\n]+|" + # spaces 407 | "\\|(?:[^\\|\\\\]|\\\\.)*\\||" + # escaped symbol 408 | ";.*|" + # comment 409 | "'|#'|`|,@|,|" + # shortcuts ` #' ` ,@ , 410 | "[()]|" + # open-close parenthesis 411 | "\"(?:[^\"\\\\]|\\\\.)*\"|" + # quoted string 412 | "[+-]?[0-9]+(?:\\.[0-9]+)(?:[eE]-?[0-9]+)?|" + # number 413 | "[^() \\n]*", # symbol 414 | x) 415 | tokens = [x for x in tokens if x.strip() != "" and x[0] != ";"][::-1] 416 | return tokens 417 | 418 | def parse(x, f): 419 | tokens = tokenize(x) 420 | def r(): 421 | v = tokens.pop() 422 | if v[0] == "\"": 423 | return eval(v) 424 | if v == "(": 425 | L = [] 426 | while tokens[-1] != ")": 427 | L.append(r()) 428 | tokens.pop() 429 | return L 430 | if v[0] == "|": 431 | return f_Lintern(v[1:-1]) 432 | if v == "'": 433 | return [_quote, r()] 434 | if v == "`": 435 | return [_bquote, r()] 436 | if v == ",@": 437 | return [_commasplice, r()] 438 | if v == ",": 439 | return [_comma, r()] 440 | if v == "#'": 441 | return [_function, r()] 442 | try: 443 | return int(v) 444 | except ValueError: 445 | try: 446 | return float(v) 447 | except ValueError: 448 | if "." in v and v[0] != "." and v[-1] != "." and ".." not in v: 449 | parts = list(map(f_Lintern, v.split("."))) 450 | return [_dot] + parts 451 | else: 452 | return f_Lintern(v) 453 | while tokens: 454 | f(r()) 455 | 456 | def f_Lpython(x): 457 | return eval(x) 458 | 459 | def f_Leval(x): 460 | global ctx 461 | octx = ctx 462 | ctx = Context() 463 | f_Lcompile(x) 464 | code = ctx.make_code("eval", False) 465 | ctx = octx 466 | return eval(code) 467 | 468 | def f_Lstr(x): 469 | if isinstance(x, list): 470 | return "(" + " ".join(map(f_Lstr, x)) + ")" 471 | else: 472 | return str(x) 473 | 474 | def f_Leval_2bprint(x): 475 | f_Lout("--> " + f_Lstr(f_Leval(x)) + "\n") 476 | 477 | def f_Lmapn(f, count): 478 | for i in range(count): 479 | f(i) 480 | 481 | def f_Lmapl(f, L): 482 | for i in L: 483 | f(i) 484 | 485 | def f_Lerror(x): 486 | raise RuntimeError(x) 487 | 488 | def f_Lload(x): 489 | with open(x) as f: 490 | parse(f.read(), f_Leval) 491 | 492 | f_Lload("pylisp.lisp") 493 | 494 | def balanced(x): 495 | tk = tokenize(x) 496 | return x.count("(") == x.count(")") 497 | 498 | curr = "" 499 | while True: 500 | try: 501 | t = raw_input("> " if balanced(curr) else " ") 502 | except EOFError: 503 | break 504 | curr += t + "\n" 505 | if balanced(curr): 506 | try: 507 | parse(curr, f_Leval_2bprint) 508 | except Exception as e: 509 | print("ERROR: %s" % e) 510 | curr = "" 511 | --------------------------------------------------------------------------------