spook

A "game" for the 2023 Autumn Lisp Game Jam. Won first place! (from the bottom...)
git clone https://kaka.farm/~git/spook
Log | Files | Refs | LICENSE

codegen.scm (7444B)


      1 ;;;; codegen.scm - code-generation for JS target
      2 
      3 
      4 (define (generate-header state)
      5   (let ((seal (test-option 'seal state)))
      6     (emit "/* CODE GENERATED BY SPOCK " spock-version " */")
      7     (when seal
      8       (emit "\n(function() {"))
      9     (when (test-option 'runtime state)
     10       (emit "\n")
     11       (read-library
     12        state
     13        (cond ((test-option 'debug state) "spock-runtime-debug.js")
     14 	     (else "spock-runtime.js"))
     15        copy-file-data))
     16     (let ((namespace (test-option 'namespace state)))
     17       (when namespace
     18 	(emit "\n" namespace " = SPOCK.module(\"" 
     19 	      namespace "\");")))))
     20 
     21 (define (generate-trailer state)
     22   (when (test-option 'seal state)
     23     (emit "\n})();"))
     24   (emit "\n/* END OF GENERATED CODE */\n"))
     25 
     26 (define (generate-code toplambdas state)
     27   (let ((nl "\n")
     28 	(loop-llist #f)
     29 	(debug-mode (test-option 'debug state))
     30 	(namespace (test-option 'namespace state)))
     31     (define (indent thunk)
     32       (let ((nlold nl))
     33 	(set! nl (string-append nl " "))
     34 	(let ((x (thunk)))
     35 	  (set! nl nlold)
     36 	  x)))
     37     (define (constant c)
     38       (with-output-to-string
     39 	(lambda ()
     40 	  (cond ((or (number? c) (string? c))
     41 		 (write c))
     42 		((char? c)
     43 		 (emit "new SPOCK.Char(")
     44 		 (write (string c))
     45 		 (emit ")"))
     46 		((boolean? c)
     47 		 (emit (if c "true" "false")))
     48 		((null? c) (emit "null"))
     49 		((symbol? c)
     50 		 (emit "SPOCK.intern(")
     51 		 (write (symbol->string c))
     52 		 (emit ")"))
     53 		((pair? c)
     54 		 (emit "new SPOCK.Pair(")
     55 		 (emit (constant (car c)))
     56 		 (emit ", ")
     57 		 (emit (constant (cdr c)))
     58 		 (emit ")"))
     59 		((vector? c)
     60 		 (emit "[")
     61 		 (unless (zero? (vector-length c))
     62 		   (emit (constant (vector-ref c 0)))
     63 		   (for-each
     64 		    (lambda (x)
     65 		      (emit ", ")
     66 		      (emit (constant x)))
     67 		    (cdr (vector->list c))))
     68 		 (emit "]"))
     69 		(else (fail "bad constant" c))))))
     70     (define (walk x dest loc)
     71       (match x
     72 	(('quote c)
     73 	 (if (or (number? c) (string? c) (boolean? c))
     74 	     (constant c)
     75 	     (let ((t1 (temp)))
     76 	       (emit nl "var " t1 " = ")
     77 	       (emit (constant c))
     78 	       (emit ";")
     79 	       t1)))
     80 	((? symbol?) x)
     81 	(('set! v x)
     82 	 (let ((t (walk x v loc)))
     83 	   (emit nl v " = " t ";\t// set! " v)
     84 	   'undefined))
     85 	(('lambda llist body)
     86 	 (let ((t1 (temp)))
     87 	   (match-let (((vars rest) (parse-llist llist)))
     88 	     (emit nl "var " t1 " = function " 
     89 		   ;(if (and debug-mode dest) (identifier dest) "")  <- gives trouble on IE
     90 		   "(")
     91 	     (emit-list vars)
     92 	     (emit ") {")
     93 	     (indent 
     94 	      (lambda ()
     95 		(when dest (emit "\t// " dest))
     96 		(when (and (pair? llist) (pair? (cdr llist))) ;XXX not really correct
     97 		  (emit nl "var r = SPOCK.count(arguments" 
     98 			(if (and debug-mode dest)
     99 			    (string-append ", " (constant (stringify dest)))
    100 			    "")
    101 			");")
    102 		  (emit nl "if(r) return r;"))
    103 		(when rest
    104 		  (emit nl "var " rest " = SPOCK.rest(arguments, "  (- (length vars) 1))
    105 		  (when (and debug-mode dest)
    106 		    (emit ", '" dest "'"))
    107 		  (emit ");"))
    108 		(fluid-let ((loop-llist #f))
    109 		  (walk body #f dest))))
    110 	     (emit nl "};")
    111 	     t1)))
    112 	(('%void) 'undefined)
    113 	(('%void? x)
    114 	 (let ((t (temp))
    115 	       (tx (walk x #f loc)))
    116 	   (emit nl "var " t " = " tx " === undefined;")
    117 	   t))
    118 	(('let (('%unused x)) body)
    119 	 (walk x #f loc)
    120 	 (walk body #f loc))
    121 	(('let ((v x)) body)
    122 	 (let ((t (walk x v loc)))
    123 	   (emit nl "var " v " = " t ";")
    124 	   (walk body v loc)))
    125 	(('if x y z)
    126 	 (let* ((t (temp))
    127 		(x (walk x #f loc)))
    128 	   (emit nl "var " t ";" nl "if(" x " !== false) {")
    129 	   (indent
    130 	    (lambda ()
    131 	      (let ((y (walk y dest loc)))
    132 		(unless (eq? y 'undefined) (emit nl t " = " y ";")))))
    133 	   (emit nl "}" nl "else {")
    134 	   (indent
    135 	    (lambda ()
    136 	      (let ((z (walk z dest loc)))
    137                 (unless (eq? z 'undefined) (emit nl t " = " z ";")))))
    138 	   (emit nl "}")
    139 	   t))
    140 	(('%host-ref name) name)
    141 	(('%host-set! name x)
    142 	 (let ((t (walk x #f loc)))
    143 	   (emit nl name " = " t)
    144 	   'undefined))
    145 	(('%property-ref name)
    146 	 (let ((t (temp))
    147 	       (k (temp "k")))
    148 	   (emit nl "var " t " = function(" k ", x) { return " k
    149 		 "(x." name "); }")
    150 	   t))
    151 	(('%property-ref name x)
    152 	 (let ((t (temp))
    153 	       (ta (walk x #f loc)))
    154 	   (emit nl "var " t " = " ta "." name ";")
    155 	   t))
    156 	(('%property-set! name x y)
    157 	 (let ((tx (walk x #f loc))
    158 	       (ty (walk y #f loc)))
    159 	   (emit nl tx "." name " = " ty ";")
    160 	   ty))
    161 	(('%check type x)
    162 	 (let ((t (temp))
    163 	       (tx (walk x dest loc)))
    164 	   (emit nl "var " t " = SPOCK.check(" tx ", ")
    165 	   (if (pair? type)
    166 	       (emit (car type))
    167 	       (emit "'" type "'"))
    168 	   (when (and loc debug-mode)
    169 	     (emit ", " (constant (stringify loc))))
    170 	   (emit ");")
    171 	   t))
    172 	(('%code code ...)
    173 	 (for-each (cut emit nl <>) code)
    174 	 'undefined)
    175 	(('%native-lambda code ...)
    176 	 (let ((t (temp)))
    177 	   (emit nl "var " t " = function(K) {")
    178 	   (indent
    179 	    (lambda ()
    180 	      ;;XXX this will not unwind, but at least decrease the counter
    181 	      (emit nl "SPOCK.count(arguments")
    182 	      (if dest
    183 		  (emit ", '" dest "');")
    184 		  (emit ");"))
    185 	      (for-each (cut emit nl <>) code)))
    186 	   (emit nl "};")
    187 	   t))
    188 	(('%inline name args ...)
    189 	 (let ((t (temp))
    190 	       (ta (map (cut walk <> #f loc) args)))
    191 	   (emit nl "var " t " = ")
    192 	   (cond ((pair? name)
    193 		  (for-each
    194 		   (lambda (x)
    195 		     (if (number? x)
    196 			 (emit "(" (list-ref ta (- x 1)) ")")
    197 			 (emit " " x " ")))
    198 		   name))
    199 		 ((char=? #\. (string-ref (stringify name) 0))
    200 		  (emit (car ta) name "(")
    201 		  (emit-list (cdr ta))
    202 		  (emit ")"))
    203 		 (else 
    204 		  (emit name "(")
    205 		  (emit-list ta)
    206 		  (emit ")")))
    207 	   (emit ";")
    208 	   t))
    209 	(('%new arg1 args ...)
    210 	 (let ((t1 (temp))
    211 	       (t2 (walk arg1 #f loc))
    212 	       (ta (map (cut walk <> #f loc) args)))
    213 	   (emit nl "var " t1 " = new " t2 "(")
    214 	   (emit-list ta)
    215 	   (emit ");")
    216 	   t1))
    217 	(('%global-ref v)
    218 	 (if namespace
    219 	     (string-append namespace "." (identifier v))
    220 	     (identifier v)))
    221 	(('%global-set! v x)
    222 	 (let ((t (walk x v loc)))
    223 	   (emit nl (if namespace (string-append namespace ".") "") 
    224 		 (identifier v) " = " t ";\t// set! " v)
    225 	   'undefined))
    226 	(('%loop llist body)
    227 	 (emit nl "loop: while(true) {")
    228 	 (fluid-let ((loop-llist llist))
    229 	   (let ((r (indent (cut walk body #f loc))))
    230 	     (emit nl "}")
    231 	     r)))
    232 	(('%continue op k args ...)
    233 	 (if loop-llist
    234 	     (let ((temps (map (lambda _ (temp)) args)))
    235 	       ;; bind arguments to temporaries
    236 	       (for-each
    237 		(lambda (t a)
    238 		  (let ((r (walk a #f loc)))
    239 		    (emit nl "var " t " = " r ";")))
    240 		temps args)
    241 	       ;; set argument variables to temporaries
    242 	       (let loop ((ll loop-llist) (temps temps))
    243 		 (cond ((pair? ll)	 ; normal argument?
    244 			(cond ((null? temps) ; missing arguments?
    245 			       (emit nl (car ll) " = undefined;")
    246 			       (loop (cdr ll) '()))
    247 			      (else
    248 			       (emit nl (car ll) " = " (car temps) ";")
    249 			       (loop (cdr ll) (cdr temps)))))
    250 		       ((symbol? ll)	; rest argument?
    251 			(emit nl ll " = SPOCK.list(")
    252 			(emit-list temps)
    253 			(emit ");"))
    254 		       (else
    255 			;; set any surplus args to undefined
    256 			(for-each
    257 			 (lambda (t) (emit nl t " = undefined;"))
    258 			 temps))))
    259 	       (emit nl "continue loop;")
    260 	       'undefined)
    261 	     (walk (cdr x) dest loc)))
    262 	((op args ...)
    263 	 (let* ((to (walk op #f loc))
    264 		(ta (map (cut walk <> #f loc) args))
    265 		(t (temp)))
    266 	   (emit nl "return " to "(")
    267 	   (emit-list ta)
    268 	   (emit ");")
    269 	   'undefined))))		; does not return
    270     (for-each
    271      (lambda (top)
    272        (let ((t (walk top #f #f)))
    273 	 (emit nl "SPOCK.run(" t ");")))
    274      toplambdas)
    275     (emit nl "SPOCK.flush();")))