1 ; Preprocessor-like macro support.
2 ; Copyright (C) 2000, 2009 Red Hat, Inc.
3 ; This file is part of CGEN.
4 ; See file COPYING.CGEN for details.
7 ; - Like C preprocessor macros, there is no scoping [one can argue
8 ; there should be]. Maybe in time (??? Hmmm... done?)
9 ; - Support for multiple macro tables.
11 ; Non-standard required routines:
13 ; make-hash-table, hashq-ref, hashq-set!, symbol-append,
16 ; location-property, location-property-set!,
17 ; source-properties-location->string,
18 ; single-location->string, location-top, unspecified-location,
19 ; reader-process-expanded!, num-args-ok?, *UNSPECIFIED*.
21 ; The convention we use says `-' begins "local" objects.
22 ; At some point this might also use the Guile module system.
24 ; This uses Guile's source-properties system to track source location.
25 ; The chain of macro invocations is tracked and stored in the result as
26 ; object property "location-property".
30 ; pmacro-init! - initialize the pmacro system
32 ; define-pmacro - define a symbolic or procedural pmacro
34 ; (define-pmacro symbol ["comment"] expansion)
35 ; (define-pmacro (symbol [args]) ["comment"] (expansion))
37 ; ARGS is a list of `symbol' or `(symbol default-value)' elements.
39 ; pmacro-expand - expand all pmacros in an expression
41 ; (pmacro-expand expression loc)
43 ; pmacro-trace - same as pmacro-expand, but trace macro expansion
44 ; Output is sent to current-error-port.
46 ; (pmacro-trace expression loc)
48 ; pmacro-dump - expand all pmacros in an expression, for debugging purposes
50 ; (pmacro-dump expression)
52 ; pmacro-debug - expand all pmacros in an expression,
53 ; printing various debugging messages.
54 ; This does not process $exec.
56 ; (pmacro-debug expression)
60 ; ($sym symbol1 symbol2 ...) - symbolstr-append
61 ; ($str string1 string2 ...) - stringsym-append
62 ; ($hex number [width]) - convert to hex string
65 ; ($substring string start end) - get part of a string
66 ; ($splice a b ($unsplice c) d e ...) - splice list into another list
67 ; ($iota count [start [increment]]) - number generator
68 ; ($map pmacro arg1 . arg-rest)
69 ; ($for-each pmacro arg1 . arg-rest)
70 ; ($eval expr) - expand (or evaluate it) expr
71 ; ($exec expr) - execute expr immediately
72 ; ($apply pmacro-name arg)
73 ; ($pmacro (arg-list) expansion) - akin go lambda in Scheme
75 ; ($let (var-list) expr1 . expr-rest) - akin to let in Scheme
76 ; ($let* (var-list) expr1 . expr-rest) - akin to let* in Scheme
77 ; ($if expr then [else])
78 ; ($case expr ((case-list1) stmt) [case-expr-stmt-list] [(else stmt)])
79 ; ($cond (expr stmt) [(cond-expr-stmt-list)] [(else stmt)])
80 ; ($begin . stmt-list)
81 ; ($print . exprs) - for debugging messages
82 ; ($dump expr) - dump expr in readable format
83 ; ($error . message) - print error message and exit
85 ; ($ref l n) - extract the n'th element of list l
86 ; ($length x) - length of symbol, string, or list
87 ; ($replicate n expr) - return list of expr replicated n times
88 ; ($find pred l) - return elements of list l matching pred
89 ; ($equal? x y) - deep comparison
90 ; ($andif expr . rest) - && in C
91 ; ($orif expr . rest) - || in C
92 ; ($not expr) - ! in C
102 ; ($div x y) - integer division
103 ; ($rem x y) - integer remainder
104 ; ($sll x n) - shift left logical
105 ; ($srl x n) - shift right logical
106 ; ($sra x n) - shift right arithmetic
107 ; ($and x y) - bitwise and
108 ; ($or x y) - bitwise or
109 ; ($xor x y) - bitwise xor
110 ; ($inv x) - bitwise invert
117 ; ($internal-test expr) - testsuite internal use only
119 ; NOTE: $cons currently absent on purpose
121 ; $sym and $str convert numbers to symbols/strings as necessary (base 10).
123 ; $pmacro is for constructing pmacros on-the-fly, like lambda, and is currently
124 ; only valid as arguments to other pmacros or assigned to a local in a {$let}
127 ; NOTE: While Scheme requires tail recursion to be implemented as a loop,
128 ; we do not. We might some day, but not today.
130 ; ??? Methinks .foo isn't a valid R5RS symbol. May need to change
133 ; True if doing pmacro expansion via pmacro-debug.
134 (define /pmacro-debug? #f)
135 ; True if doing pmacro expansion via pmacro-trace.
136 (define /pmacro-trace? #f)
139 (define /pmacro-table #f)
140 (define (/pmacro-lookup name) (hashq-ref /pmacro-table name #f))
141 (define (/pmacro-set! name val) (hashq-set! /pmacro-table name val))
143 ; A copy of syntactic pmacros is kept separately.
144 (define /smacro-table #f)
145 (define (/smacro-lookup name) (hashq-ref /smacro-table name #f))
146 (define (/smacro-set! name val) (hashq-set! /smacro-table name val))
148 ; Marker to indicate a value is a pmacro.
149 ; NOTE: Naming this "<pmacro>" is intentional. It makes them look like
150 ; objects of class <pmacro>. However we don't use COS in part to avoid
151 ; a dependency on COS and in part because displaying COS objects isn't well
152 ; supported (displaying them in debugging dumps adds a lot of noise).
153 (define /pmacro-marker '<pmacro>)
155 ; Utilities to create and access pmacros.
156 (define (/pmacro-make name arg-spec default-values
157 syntactic-form? transformer comment)
158 (vector /pmacro-marker name arg-spec default-values
159 syntactic-form? transformer comment)
161 (define (/pmacro? x) (and (vector? x) (eq? (vector-ref x 0) /pmacro-marker)))
162 (define (/pmacro-name pmac) (vector-ref pmac 1))
163 (define (/pmacro-arg-spec pmac) (vector-ref pmac 2))
164 (define (/pmacro-default-values pmac) (vector-ref pmac 3))
165 (define (/pmacro-syntactic-form? pmac) (vector-ref pmac 4))
166 (define (/pmacro-transformer pmac) (vector-ref pmac 5))
167 (define (/pmacro-comment pmac) (vector-ref pmac 6))
169 ;; Create a new environment, prepending NAMES to PREV-ENV.
171 (define (/pmacro-env-make loc prev-env names values)
172 (if (= (length names) (length values))
173 (append! (map cons names values) prev-env)
174 (/pmacro-loc-error loc
175 (string-append "invalid number of parameters, expected "
176 (number->string (length names)))
180 ;; Look up NAME in ENV.
182 (define (/pmacro-env-ref env name) (assq name env))
184 ; Error message generator.
186 (define (/pmacro-error msg expr)
187 (error (string-append
188 (or (port-filename (current-input-port)) "<input>")
190 (number->string (port-line (current-input-port)))
197 ; Error message generator when we have a location.
199 (define (/pmacro-loc-error loc errmsg expr)
200 (let* ((top-sloc (location-top loc))
201 (intro "During pmacro expansion")
202 (text (string-append "Error: " errmsg)))
203 (error (simple-format
205 "\n~A:\n@ ~A:\n\n~A: ~A:"
207 (location->string loc)
208 (single-location->simple-string top-sloc)
213 ; Issue an error where a number was expected.
215 (define (/pmacro-expected-number op n)
216 (/pmacro-error (string-append "invalid arg for " op ", expected number") n)
219 ; Verify N is a number.
221 (define (/pmacro-verify-number op n)
222 (if (not (number? n))
223 (/pmacro-expected-number op n))
226 ; Issue an error where an integer was expected.
228 (define (/pmacro-expected-integer op n)
229 (/pmacro-error (string-append "invalid arg for " op ", expected integer") n)
232 ; Verify N is an integer.
234 (define (/pmacro-verify-integer op n)
235 (if (not (integer? n))
236 (/pmacro-expected-integer op n))
239 ; Issue an error where a non-negative integer was expected.
241 (define (/pmacro-expected-non-negative-integer op n)
242 (/pmacro-error (string-append "invalid arg for " op ", expected non-negative integer") n)
245 ; Verify N is a non-negative integer.
247 (define (/pmacro-verify-non-negative-integer op n)
248 (if (or (not (integer? n))
250 (/pmacro-expected-non-negative-integer op n))
253 ; Expand a list of expressions, in order.
254 ; The result is the value of the last one.
256 (define (/pmacro-expand-expr-list exprs env loc)
258 (for-each (lambda (expr)
259 (set! result (/pmacro-expand expr env loc)))
264 ; Process list of keyword/value specified arguments.
266 (define (/pmacro-process-keyworded-args arg-spec default-values args)
267 ; Build a list of default values, then override ones specified in ARGS,
268 (let ((result-alist (alist-copy default-values)))
269 (let loop ((args args))
272 ((and (pair? args) (keyword? (car args)))
273 (let ((elm (assq (car args) result-alist)))
275 (/pmacro-error "not an argument name" (car args)))
276 (if (null? (cdr args))
277 (/pmacro-error "missing argument to #:keyword" (car args)))
278 (set-cdr! elm (cadr args))
281 (/pmacro-error "bad keyword/value argument list" args))))
283 ; Ensure each element has a value.
284 (let loop ((to-scan result-alist))
288 (if (not (cdar to-scan))
289 (/pmacro-error "argument value not specified" (caar to-scan)))
290 (loop (cdr to-scan)))))
292 ; If varargs pmacro, adjust result.
294 (map cdr result-alist) ; not varargs
295 (let ((nr-args (length (result-alist))))
296 (append! (map cdr (list-head result-alist (- nr-args 1)))
297 (cdr (list-tail result-alist (- nr-args 1)))))))
300 ; Process a pmacro argument list.
301 ; ARGS is either a fully specified position dependent argument list,
302 ; or is a list of keyword/value pairs with missing values coming from
305 (define (/pmacro-process-args-1 arg-spec default-values args)
306 (if (and (pair? args) (keyword? (car args)))
307 (/pmacro-process-keyworded-args arg-spec default-values args)
311 ; Subroutine of /pmacro-apply,/smacro-apply to simplify them.
312 ; Process the arguments, verify the correct number is present.
314 (define (/pmacro-process-args macro args)
315 (let ((arg-spec (/pmacro-arg-spec macro))
316 (default-values (/pmacro-default-values macro)))
317 (let ((processed-args (/pmacro-process-args-1 arg-spec default-values args)))
318 (if (not (num-args-ok? (length processed-args) arg-spec))
319 (/pmacro-error (string-append
320 "wrong number of arguments to pmacro "
321 (with-output-to-string
323 (write (cons (/pmacro-name macro)
324 (/pmacro-arg-spec macro))))))
331 (define (/pmacro-apply macro args)
332 (apply (/pmacro-transformer macro)
333 (/pmacro-process-args macro args))
336 ; Invoke a syntactic-form pmacro.
337 ; ENV, LOC are handed down from /pmacro-expand.
339 (define (/smacro-apply macro args env loc)
340 (apply (/pmacro-transformer macro)
341 (cons loc (cons env (/pmacro-process-args macro args))))
344 ;; Expand expression EXP using ENV, an alist of variable assignments.
345 ;; LOC is the location stack thus far.
347 (define (/pmacro-expand exp env loc)
349 (define cep (current-error-port))
351 ;; If the symbol is in `env', return its value.
352 ;; Otherwise see if symbol is a globally defined pmacro.
353 ;; Otherwise return the symbol unchanged.
355 (define (scan-symbol sym)
356 (let ((val (/pmacro-env-ref env sym)))
358 (cdr val) ;; cdr is value of (name . value) pair
359 (let ((val (/pmacro-lookup sym)))
361 ;; Symbol is a pmacro.
362 ;; If this is a procedural pmacro, let caller perform expansion.
363 ;; Otherwise, return the pmacro's value.
364 (if (procedure? (/pmacro-transformer val))
366 (/pmacro-transformer val))
367 ;; Return symbol unchanged.
370 ;; See if (car exp) is a pmacro.
371 ;; Return pmacro or #f.
373 (define (check-pmacro exp)
376 (display "Checking for pmacro: " cep)
379 (and (/pmacro? (car exp)) (car exp)))
381 ;; Subroutine of scan-list to simplify it.
382 ;; Macro expand EXP which is known to be a non-null list.
383 ;; LOC is the location stack thus far.
385 (define (scan-list1 exp loc)
386 ;; Check for syntactic forms.
387 ;; They are handled differently in that we leave it to the transformer
388 ;; routine to evaluate the arguments.
389 ;; Note that we also don't support passing syntactic form functions
390 ;; as arguments: We look up (car exp) here, not its expansion.
391 (let ((sform (/smacro-lookup (car exp))))
394 ;; ??? Is it useful to trace these?
395 (/smacro-apply sform (cdr exp) env loc))
396 ;; Not a syntactic form.
397 ;; See if we have a pmacro. Do this before evaluating all the
398 ;; arguments (even though we will eventually evaluate all the
399 ;; arguments before invoking the pmacro) so that tracing is more
400 ;; legible (we print the expression we're about to evaluate *before*
401 ;; we evaluate its arguments).
402 (let ((scanned-car (scan (car exp) loc)))
403 (if (/pmacro? scanned-car)
405 ;; Trace expansion here, we know we have a pmacro.
407 (let ((src-props (source-properties exp))
408 (indent (spaces (* 2 (length (location-list loc))))))
409 ;; We use `write' to display `exp' to see strings quoted.
411 (display "Expanding: " cep)
415 (display " env: " cep)
418 (if (not (null? src-props))
421 (display " location: " cep)
422 (display (source-properties-location->string src-props) cep)
424 ;; Evaluate all the arguments before invoking the pmacro.
425 (let* ((scanned-args (map (lambda (e) (scan e loc))
427 (result (if (procedure? (/pmacro-transformer scanned-car))
428 (/pmacro-apply scanned-car scanned-args)
429 (cons (/pmacro-transformer scanned-car) scanned-args))))
431 (let ((indent (spaces (* 2 (length (location-list loc))))))
433 (display " result: " cep)
438 (cons scanned-car (map (lambda (e) (scan e loc))
441 ;; Macro expand EXP which is known to be a non-null list.
442 ;; LOC is the location stack thus far.
444 ;; This uses scan-list1 to do the real work, this handles location tracking.
446 (define (scan-list exp loc)
447 (let ((src-props (source-properties exp))
449 (if (not (null? src-props))
450 (let ((file (assq-ref src-props 'filename))
451 (line (assq-ref src-props 'line))
452 (column (assq-ref src-props 'column)))
453 (set! new-loc (location-push-single loc file line column #f))))
454 (let ((result (scan-list1 exp new-loc)))
455 (if (pair? result) ;; pair? -> cheap non-null-list?
457 ;; Copy source location to new expression.
458 (if (null? (source-properties result))
459 (set-source-properties! result src-props))
460 (let ((loc-prop (location-property result)))
462 (location-property-set! result (location-push new-loc loc-prop))
463 (location-property-set! result new-loc)))))
466 ;; Scan EXP, an arbitrary value.
467 ;; LOC is the location stack thus far.
469 (define (scan exp loc)
470 (let ((result (cond ((symbol? exp)
472 ((pair? exp) ;; pair? -> cheap non-null-list?
474 ;; Not a symbol or expression, return unchanged.
477 ;; Re-examining `result' to see if it is another pmacro invocation
478 ;; allows doing things like (($sym a b c) arg1 arg2)
479 ;; where `abc' is a pmacro. Scheme doesn't work this way, but then
481 (if (symbol? result) (scan-symbol result) result)))
486 ; Return the argument spec from ARGS.
487 ; ARGS is a [possibly improper] list of `symbol' or `(symbol default-value)'
488 ; elements. For varargs pmacros, ARGS must be an improper list
489 ; (e.g. (a b . c)) with the last element being a symbol.
491 (define (/pmacro-get-arg-spec args)
496 ((and (pair? arg) (symbol? (car arg)))
499 (/pmacro-error "argument not `symbol' or `(symbol . default-value)'"
503 (letrec ((parse-improper-list
505 (cond ((symbol? args)
508 (cons (parse-arg (car args))
509 (parse-improper-list (cdr args))))
511 (/pmacro-error "argument not `symbol' or `(symbol . default-value)'"
513 (parse-improper-list args))))
516 ; Return the default values specified in ARGS.
517 ; The result is an alist of (#:arg-name . default-value) elements.
518 ; ARGS is a [possibly improper] list of `symbol' or `(symbol . default-value)'
519 ; elements. For varargs pmacros, ARGS must be an improper list
520 ; (e.g. (a b . c)) with the last element being a symbol.
521 ; Unspecified default values are recorded as #f.
523 (define (/pmacro-get-default-values args)
527 (cons (symbol->keyword arg) #f))
528 ((and (pair? arg) (symbol? (car arg)))
529 (cons (symbol->keyword (car arg)) (cdr arg)))
531 (/pmacro-error "argument not `symbol' or `(symbol . default-value)'"
535 (letrec ((parse-improper-list
537 (cond ((symbol? args)
538 (cons (parse-arg args) nil))
540 (cons (parse-arg (car args))
541 (parse-improper-list (cdr args))))
543 (/pmacro-error "argument not `symbol' or `(symbol . default-value)'"
545 (parse-improper-list args))))
548 ; Build a procedure that performs a pmacro expansion.
550 ; Earlier version, doesn't work with LOC as a <location> object,
551 ; COS objects don't pass through eval1.
552 ;(define (/pmacro-build-lambda prev-env params expansion)
553 ; (eval1 `(lambda ,params
554 ; (/pmacro-expand ',expansion
555 ; (/pmacro-env-make ',prev-env
556 ; ',params (list ,@params))))
559 (define (/pmacro-build-lambda loc prev-env params expansion)
561 (/pmacro-expand expansion
562 (/pmacro-env-make loc prev-env params args)
566 ; While using `define-macro' seems preferable, boot-9.scm uses it and
567 ; I'd rather not risk a collision. I could of course make the association
568 ; during parsing, maybe later.
569 ; On the other hand, calling them pmacros removes all ambiguity.
570 ; In the end the ambiguity removal is the deciding win.
572 ; The syntax is one of:
573 ; (define-pmacro symbol expansion)
574 ; (define-pmacro symbol ["comment"] expansion)
575 ; (define-pmacro (name args ...) expansion)
576 ; (define-pmacro (name args ...) "documentation" expansion)
578 ; If `expansion' is the name of a pmacro, its value is used (rather than its
580 ; ??? The goal here is to follow Scheme's define/lambda, but not all variants
581 ; are supported yet. There's also the difference that we treat undefined
582 ; symbols as being themselves (i.e. "self quoting" so-to-speak).
584 ; ??? We may want user-definable "syntactic" pmacros some day. Later.
586 (define (define-pmacro header arg1 . arg-rest)
587 (if (and (not (symbol? header))
588 (not (list? header)))
589 (/pmacro-error "invalid pmacro header" header))
590 (let ((name (if (symbol? header) header (car header)))
591 (arg-spec (if (symbol? header) #f (/pmacro-get-arg-spec (cdr header))))
592 (default-values (if (symbol? header) #f (/pmacro-get-default-values (cdr header))))
593 (comment (if (null? arg-rest) "" arg1))
594 (expansion (if (null? arg-rest) arg1 (car arg-rest))))
595 ;;(if (> (length arg-rest) 1)
596 ;;(/pmacro-error "extraneous arguments to define-pmacro" (cdr arg-rest)))
597 ;;(if (not (string? comment))
598 ;;(/pmacro-error "invalid pmacro comment, expected string" comment))
600 (if (symbol? expansion)
601 (let ((maybe-pmacro (/pmacro-lookup expansion)))
605 (/pmacro-arg-spec maybe-pmacro)
606 (/pmacro-default-values maybe-pmacro)
608 (/pmacro-transformer maybe-pmacro)
610 (/pmacro-set! name (/pmacro-make name #f #f #f expansion comment))))
611 (/pmacro-set! name (/pmacro-make name #f #f #f expansion comment)))
613 (/pmacro-make name arg-spec default-values #f
614 (/pmacro-build-lambda (current-reader-location)
622 ; Expand any pmacros in EXPR.
623 ; LOC is the <location> of EXPR.
625 (define (pmacro-expand expr loc)
626 (/pmacro-expand expr '() loc)
629 ; Debugging routine to trace pmacro expansion.
631 (define (pmacro-trace expr loc)
632 ; FIXME: Need unwind protection.
633 (let ((old-trace /pmacro-trace?)
634 (src-props (and (pair? expr) (source-properties expr)))
635 (cep (current-error-port)))
636 (set! /pmacro-trace? #t)
637 ;; We use `write' to display `expr' to see strings quoted.
638 (display "Pmacro expanding: " cep) (write expr cep) (newline cep)
639 ;;(display "Top level env: " cep) (display nil cep) (newline cep)
640 (display "Pmacro location: " cep)
641 (if (and src-props (not (null? src-props)))
642 (display (source-properties-location->string src-props) cep)
643 (display (single-location->string (location-top loc)) cep))
645 (let ((result (/pmacro-expand expr '() loc)))
646 (display "Pmacro result: " cep) (write result cep) (newline cep)
647 (set! /pmacro-trace? old-trace)
651 ; Debugging utility to expand a pmacro, with no initial source location.
653 (define (pmacro-dump expr)
654 (/pmacro-expand expr '() (unspecified-location))
657 ; Expand any pmacros in EXPR, printing various debugging messages.
658 ; This does not process $exec.
660 (define (pmacro-debug expr)
661 ; FIXME: Need unwind protection.
662 (let ((old-debug /pmacro-debug?))
663 (set! /pmacro-debug? #t)
664 (let ((result (pmacro-trace expr (unspecified-location))))
665 (set! /pmacro-debug? old-debug)
671 ; ($sym symbol1 symbol2 ...) - symbol-append, auto-convert numbers
673 (define /pmacro-builtin-sym
678 (cond ((number? elm) (number->string elm))
679 ((symbol? elm) (symbol->string elm))
682 (/pmacro-error "invalid argument to $sym" elm))))
686 ; ($str string1 string2 ...) - string-append, auto-convert numbers
688 (define /pmacro-builtin-str
692 (cond ((number? elm) (number->string elm))
693 ((symbol? elm) (symbol->string elm))
696 (/pmacro-error "invalid argument to $str" elm))))
700 ; ($hex number [width]) - convert number to hex string
701 ; WIDTH, if present, is the number of characters in the result, beginning
702 ; from the least significant digit.
704 (define (/pmacro-builtin-hex num . width)
705 (if (> (length width) 1)
706 (/pmacro-error "wrong number of arguments to $hex"
707 (cons '$hex (cons num width))))
708 (let ((str (number->string num 16)))
711 (let ((len (string-length str)))
712 (substring (string-append (make-string (car width) #\0) str)
713 len (+ len (car width))))))
716 ; ($upcase string) - convert a string or symbol to uppercase
718 (define (/pmacro-builtin-upcase str)
720 ((string? str) (string-upcase str))
721 ((symbol? str) (string->symbol (string-upcase (symbol->string str))))
722 (else (/pmacro-error "invalid argument to $upcase" str)))
725 ; ($downcase string) - convert a string or symbol to lowercase
727 (define (/pmacro-builtin-downcase str)
729 ((string? str) (string-downcase str))
730 ((symbol? str) (string->symbol (string-downcase (symbol->string str))))
731 (else (/pmacro-error "invalid argument to $downcase" str)))
734 ; ($substring string start end) - get part of a string
735 ; `end' can be the symbol `end'.
737 (define (/pmacro-builtin-substring str start end)
738 (if (not (integer? start)) ;; FIXME: non-negative-integer
739 (/pmacro-error "start not an integer" start))
740 (if (and (not (integer? end))
741 (not (eq? end 'end)))
742 (/pmacro-error "end not an integer nor symbol `end'" end))
745 (substring str start)
746 (substring str start end)))
749 (string->symbol (substring (symbol->string str) start))
750 (string->symbol (substring (symbol->string str) start end))))
752 (/pmacro-error "invalid argument to $substring" str)))
755 ; $splice - splicing support
756 ; Splice lists into the outer list.
758 ; E.g. (define-pmacro '(splice-test a b c) '($splice a ($unsplice b) c))
759 ; (pmacro-expand '(splice-test (1 (2) 3))) --> (1 2 3)
761 ; Similar to `(1 ,@'(2) 3) in Scheme, though the terminology is slightly
762 ; different (??? may need to revisit). In Scheme there's quasi-quote,
763 ; unquote, unquote-splicing. Here we have splice, unsplice; with the proviso
764 ; that pmacros don't have the concept of "quoting", thus all subexpressions
765 ; are macro-expanded first, before performing any unsplicing.
766 ; [??? Some may want a quoting facility, but I'd like to defer adding it as
767 ; long as possible (and ideally never add it).]
769 ; NOTE: The implementation relies on $unsplice being undefined so that
770 ; ($unsplice (42)) is expanded unchanged.
772 (define /pmacro-builtin-splice
774 ; ??? Not the most efficient implementation.
775 (let* ((unsplice-str (if (rtl-version-at-least? 0 9) "$unsplice" ".unsplice"))
776 (unsplice-sym (string->symbol unsplice-str)))
777 (let loop ((arg-list arg-list) (result '()))
778 (cond ((null? arg-list) result)
779 ((and (pair? (car arg-list)) (eq? unsplice-sym (caar arg-list)))
780 (if (= (length (car arg-list)) 2)
781 (if (list? (cadar arg-list))
782 (loop (cdr arg-list) (append result (cadar arg-list)))
783 (/pmacro-error (string-append "argument to " unsplice-str " must be a list")
785 (/pmacro-error (string-append "wrong number of arguments to " unsplice-str)
788 (loop (cdr arg-list) (append result (list (car arg-list)))))))))
793 ; ($iota count) ; start=0, incr=1
794 ; ($iota count start) ; incr=1
795 ; ($iota count start incr)
797 (define (/pmacro-builtin-iota count . start-incr)
798 (if (> (length start-incr) 2)
799 (/pmacro-error "wrong number of arguments to $iota"
800 (cons '$iota (cons count start-incr))))
802 (/pmacro-error "count must be non-negative"
803 (cons '$iota (cons count start-incr))))
804 (let ((start (if (pair? start-incr) (car start-incr) 0))
805 (incr (if (= (length start-incr) 2) (cadr start-incr) 1)))
806 (let loop ((i start) (count count) (result '()))
809 (loop (+ i incr) (- count 1) (cons i result)))))
812 ; ($map pmacro arg1 . arg-rest)
814 (define (/pmacro-builtin-map pmacro arg1 . arg-rest)
815 (if (not (/pmacro? pmacro))
816 (/pmacro-error "not a pmacro" pmacro))
817 (let ((transformer (/pmacro-transformer pmacro)))
818 (if (not (procedure? transformer))
819 (/pmacro-error "not a procedural pmacro" pmacro))
820 (apply map (cons transformer (cons arg1 arg-rest))))
823 ; ($for-each pmacro arg1 . arg-rest)
825 (define (/pmacro-builtin-for-each pmacro arg1 . arg-rest)
826 (if (not (/pmacro? pmacro))
827 (/pmacro-error "not a pmacro" pmacro))
828 (let ((transformer (/pmacro-transformer pmacro)))
829 (if (not (procedure? transformer))
830 (/pmacro-error "not a procedural pmacro" pmacro))
831 (apply for-each (cons transformer (cons arg1 arg-rest)))
832 nil) ; need to return something the reader will accept and ignore
836 ; NOTE: This is implemented as a syntactic form in order to get ENV and LOC.
837 ; That's an implementation detail, and this is not really a syntactic form.
839 ; ??? I debated whether to call this $expand, $eval has been a source of
840 ; confusion/headaches.
842 (define (/pmacro-builtin-eval loc env expr)
843 ;; /pmacro-expand is invoked twice because we're implemented as a syntactic
844 ;; form: We *want* to be passed an evaluated expression, and then we
845 ;; re-evaluate it. But syntactic forms pass parameters unevaluated, so we
846 ;; have to do the first one ourselves.
847 (/pmacro-expand (/pmacro-expand expr env loc) env loc)
852 (define (/pmacro-builtin-exec expr)
853 ;; If we're expanding pmacros for debugging purposes, don't execute,
854 ;; just return unchanged.
858 (reader-process-expanded! expr)
859 nil)) ;; need to return something the reader will accept and ignore
862 ; ($apply pmacro-name arg)
864 (define (/pmacro-builtin-apply pmacro arg-list)
865 (if (not (/pmacro? pmacro))
866 (/pmacro-error "not a pmacro" pmacro))
867 (let ((transformer (/pmacro-transformer pmacro)))
868 (if (not (procedure? transformer))
869 (/pmacro-error "not a procedural pmacro" pmacro))
870 (apply transformer arg-list))
873 ; ($pmacro (arg-list) expansion)
874 ; NOTE: syntactic form
876 (define (/pmacro-builtin-pmacro loc env params expansion)
877 ;; ??? Prohibiting improper lists seems unnecessarily restrictive here.
878 ;; e.g. (define (foo bar . baz) ...)
879 (if (not (list? params))
880 (/pmacro-error "$pmacro parameter-spec is not a list" params))
881 (/pmacro-make '$anonymous params #f #f
882 (/pmacro-build-lambda loc env params expansion) "")
887 (define (/pmacro-builtin-pmacro? arg)
891 ; ($let (var-list) expr1 . expr-rest)
892 ; NOTE: syntactic form
894 (define (/pmacro-builtin-let loc env locals expr1 . expr-rest)
895 (if (not (list? locals))
896 (/pmacro-error "locals is not a list" locals))
897 (if (not (all-true? (map (lambda (l)
902 (/pmacro-error "syntax error in locals list" locals))
903 (let* ((evald-locals (map (lambda (l)
904 (cons (car l) (/pmacro-expand (cadr l) env loc)))
906 (new-env (append! evald-locals env)))
907 (/pmacro-expand-expr-list (cons expr1 expr-rest) new-env loc))
910 ; ($let* (var-list) expr1 . expr-rest)
911 ; NOTE: syntactic form
913 (define (/pmacro-builtin-let* loc env locals expr1 . expr-rest)
914 (if (not (list? locals))
915 (/pmacro-error "locals is not a list" locals))
916 (if (not (all-true? (map (lambda (l)
921 (/pmacro-error "syntax error in locals list" locals))
922 (let loop ((locals locals) (new-env env))
924 (/pmacro-expand-expr-list (cons expr1 expr-rest) new-env loc)
925 (loop (cdr locals) (acons (caar locals)
926 (/pmacro-expand (cadar locals) new-env loc)
930 ; ($if expr then [else])
931 ; NOTE: syntactic form
933 (define (/pmacro-builtin-if loc env expr then-clause . else-clause)
934 (case (length else-clause)
935 ((0) (if (/pmacro-expand expr env loc)
936 (/pmacro-expand then-clause env loc)
938 ((1) (if (/pmacro-expand expr env loc)
939 (/pmacro-expand then-clause env loc)
940 (/pmacro-expand (car else-clause) env loc)))
941 (else (/pmacro-error "too many elements in else-clause, expecting 0 or 1" else-clause)))
944 ; ($case expr ((case-list1) stmt) [case-expr-stmt-list] [(else stmt)])
945 ; NOTE: syntactic form
946 ; NOTE: this uses "member" for case comparison (Scheme uses memq I think)
948 (define (/pmacro-builtin-case loc env expr case1 . rest)
949 (let ((evald-expr (/pmacro-expand expr env loc)))
950 (let loop ((cases (cons case1 rest)))
954 (if (not (list? (car cases)))
955 (/pmacro-error "case statement not a list" (car cases)))
956 (if (= (length (car cases)) 1)
957 (/pmacro-error "case statement has case but no expr" (car cases)))
958 (if (and (not (eq? (caar cases) 'else))
959 (not (list? (caar cases))))
960 (/pmacro-error "case must be \"else\" or list of choices" (caar cases)))
961 (cond ((eq? (caar cases) 'else)
962 (/pmacro-expand-expr-list (cdar cases) env loc))
963 ((member evald-expr (caar cases))
964 (/pmacro-expand-expr-list (cdar cases) env loc))
966 (loop (cdr cases))))))))
969 ; ($cond (expr stmt) [(cond-expr-stmt-list)] [(else stmt)])
970 ; NOTE: syntactic form
972 (define (/pmacro-builtin-cond loc env expr1 . rest)
973 (let loop ((exprs (cons expr1 rest)))
976 ((eq? (car exprs) 'else)
977 (/pmacro-expand-expr-list (cdar exprs) env loc))
979 (let ((evald-expr (/pmacro-expand (caar exprs) env loc)))
981 (/pmacro-expand-expr-list (cdar exprs) env loc)
982 (loop (cdr exprs)))))))
985 ; ($begin . stmt-list)
986 ; NOTE: syntactic form
988 (define (/pmacro-builtin-begin loc env . rest)
989 (/pmacro-expand-expr-list rest env loc)
993 ; Strings have quotes removed.
995 (define (/pmacro-builtin-print . exprs)
996 (apply message exprs)
997 nil ; need to return something the reader will accept and ignore
1001 ; Strings do not have quotes removed.
1003 (define (/pmacro-builtin-dump expr)
1004 (write expr (current-error-port))
1005 nil ; need to return something the reader will accept and ignore
1010 (define (/pmacro-builtin-error . exprs)
1016 (define (/pmacro-builtin-list . exprs)
1022 (define (/pmacro-builtin-ref l n)
1024 (/pmacro-error "invalid arg for $ref, expected list" l))
1025 (if (not (integer? n)) ;; FIXME: call non-negative-integer?
1026 (/pmacro-error "invalid arg for $ref, expected non-negative integer" n))
1032 (define (/pmacro-builtin-length x)
1033 (cond ((symbol? x) (string-length (symbol->string x)))
1034 ((string? x) (string-length x))
1035 ((list? x) (length x))
1037 (/pmacro-error "invalid arg for $length, expected symbol, string, or list" x)))
1040 ; ($replicate n expr)
1042 (define (/pmacro-builtin-replicate n expr)
1043 (if (not (integer? n)) ;; FIXME: call non-negative-integer?
1044 (/pmacro-error "invalid arg for $replicate, expected non-negative integer" n))
1050 (define (/pmacro-builtin-find pred l)
1051 (if (not (/pmacro? pred))
1052 (/pmacro-error "not a pmacro" pred))
1054 (/pmacro-error "not a list" l))
1055 (let ((transformer (/pmacro-transformer pred)))
1056 (if (not (procedure? transformer))
1057 (/pmacro-error "not a procedural macro" pred))
1058 (find transformer l))
1063 (define (/pmacro-builtin-equal? x y)
1068 ; NOTE: syntactic form
1069 ; Elements of EXPRS are evaluated one at a time.
1070 ; Unprocessed elements are not evaluated.
1072 (define (/pmacro-builtin-andif loc env . exprs)
1075 (let loop ((exprs exprs))
1076 (let ((evald-expr (/pmacro-expand (car exprs) env loc)))
1077 (cond ((null? (cdr exprs)) evald-expr)
1078 (evald-expr (loop (cdr exprs)))
1083 ; NOTE: syntactic form
1084 ; Elements of EXPRS are evaluated one at a time.
1085 ; Unprocessed elements are not evaluated.
1087 (define (/pmacro-builtin-orif loc env . exprs)
1088 (let loop ((exprs exprs))
1091 (let ((evald-expr (/pmacro-expand (car exprs) env loc)))
1094 (loop (cdr exprs))))))
1099 (define (/pmacro-builtin-not x)
1103 ; Verify x,y are compatible for eq/ne comparisons.
1105 (define (/pmacro-compatible-for-equality x y)
1106 (or (and (symbol? x) (symbol? y))
1107 (and (string? x) (string? y))
1108 (and (number? x) (number? y)))
1113 (define (/pmacro-builtin-eq x y)
1117 (/pmacro-error "incompatible args for $eq, expected symbol" y)))
1121 (/pmacro-error "incompatible args for $eq, expected string" y)))
1125 (/pmacro-error "incompatible args for $eq, expected number" y)))
1127 (/pmacro-error "unsupported args for $eq" (list x y))))
1132 (define (/pmacro-builtin-ne x y)
1136 (/pmacro-error "incompatible args for $ne, expected symbol" y)))
1139 (not (string=? x y))
1140 (/pmacro-error "incompatible args for $ne, expected string" y)))
1144 (/pmacro-error "incompatible args for $ne, expected number" y)))
1146 (/pmacro-error "unsupported args for $ne" (list x y))))
1151 (define (/pmacro-builtin-lt x y)
1152 (/pmacro-verify-number "$lt" x)
1153 (/pmacro-verify-number "$lt" y)
1159 (define (/pmacro-builtin-gt x y)
1160 (/pmacro-verify-number "$gt" x)
1161 (/pmacro-verify-number "$gt" y)
1167 (define (/pmacro-builtin-le x y)
1168 (/pmacro-verify-number "$le" x)
1169 (/pmacro-verify-number "$le" y)
1175 (define (/pmacro-builtin-ge x y)
1176 (/pmacro-verify-number "$ge" x)
1177 (/pmacro-verify-number "$ge" y)
1183 (define (/pmacro-builtin-add x y)
1184 (/pmacro-verify-number "$add" x)
1185 (/pmacro-verify-number "$add" y)
1191 (define (/pmacro-builtin-sub x y)
1192 (/pmacro-verify-number "$sub" x)
1193 (/pmacro-verify-number "$sub" y)
1199 (define (/pmacro-builtin-mul x y)
1200 (/pmacro-verify-number "$mul" x)
1201 (/pmacro-verify-number "$mul" y)
1205 ; ($div x y) - integer division
1207 (define (/pmacro-builtin-div x y)
1208 (/pmacro-verify-integer "$div" x)
1209 (/pmacro-verify-integer "$div" y)
1213 ; ($rem x y) - integer remainder
1214 ; ??? Need to decide behavior.
1216 (define (/pmacro-builtin-rem x y)
1217 (/pmacro-verify-integer "$rem" x)
1218 (/pmacro-verify-integer "$rem" y)
1222 ; ($sll x n) - shift left logical
1224 (define (/pmacro-builtin-sll x n)
1225 (/pmacro-verify-integer "$sll" x)
1226 (/pmacro-verify-non-negative-integer "$sll" n)
1230 ; ($srl x n) - shift right logical
1231 ; X must be non-negative, otherwise behavior is undefined.
1232 ; [Unless we introduce a size argument: How do you logical shift right
1233 ; an arbitrary precision negative number?]
1235 (define (/pmacro-builtin-srl x n)
1236 (/pmacro-verify-non-negative-integer "$srl" x)
1237 (/pmacro-verify-non-negative-integer "$srl" n)
1241 ; ($sra x n) - shift right arithmetic
1243 (define (/pmacro-builtin-sra x n)
1244 (/pmacro-verify-integer "$sra" x)
1245 (/pmacro-verify-non-negative-integer "$sra" n)
1249 ; ($and x y) - bitwise and
1251 (define (/pmacro-builtin-and x y)
1252 (/pmacro-verify-integer "$and" x)
1253 (/pmacro-verify-integer "$and" y)
1257 ; ($or x y) - bitwise or
1259 (define (/pmacro-builtin-or x y)
1260 (/pmacro-verify-integer "$or" x)
1261 (/pmacro-verify-integer "$or" y)
1265 ; ($xor x y) - bitwise xor
1267 (define (/pmacro-builtin-xor x y)
1268 (/pmacro-verify-integer "$xor" x)
1269 (/pmacro-verify-integer "$xor" y)
1273 ; ($inv x) - bitwise invert
1275 (define (/pmacro-builtin-inv x)
1276 (/pmacro-verify-integer "$inv" x)
1282 (define (/pmacro-builtin-car l)
1285 (/pmacro-error "invalid arg for $car, expected pair" l))
1290 (define (/pmacro-builtin-cdr l)
1293 (/pmacro-error "invalid arg for $cdr, expected pair" l))
1298 (define (/pmacro-builtin-caar l)
1299 (if (and (pair? l) (pair? (car l)))
1301 (/pmacro-error "invalid arg for $caar" l))
1306 (define (/pmacro-builtin-cadr l)
1307 (if (and (pair? l) (pair? (cdr l)))
1309 (/pmacro-error "invalid arg for $cadr" l))
1314 (define (/pmacro-builtin-cdar l)
1315 (if (and (pair? l) (pair? (car l)))
1317 (/pmacro-error "invalid arg for $cdar" l))
1322 (define (/pmacro-builtin-cddr l)
1323 (if (and (pair? l) (pair? (cdr l)))
1325 (/pmacro-error "invalid arg for $cddr" l))
1328 ; ($internal-test expr)
1329 ; This is an internal builtin for use by the testsuite.
1330 ; EXPR is a Scheme expression that is executed to verify proper
1331 ; behaviour of something. It must return #f for FAIL, non-#f for PASS.
1332 ; The result is #f for FAIL, #t for PASS.
1333 ; This must be used in an expression, it is not sufficient to do
1334 ; ($internal-test mumble) because the reader will see #f or #t and complain.
1336 (define (/pmacro-builtin-internal-test expr)
1337 (and (eval1 expr) #t)
1342 (define (pmacros-init!)
1343 (set! /pmacro-table (make-hash-table 127))
1344 (set! /smacro-table (make-hash-table 41))
1346 ; Some "predefined" pmacros.
1349 ;; name arg-spec syntactic? function description
1351 (list 'sym 'symbols #f /pmacro-builtin-sym "symbol-append")
1352 (list 'str 'strings #f /pmacro-builtin-str "string-append")
1353 (list 'hex '(number . width) #f /pmacro-builtin-hex "convert to -hex, with optional width")
1354 (list 'upcase '(string) #f /pmacro-builtin-upcase "string-upcase")
1355 (list 'downcase '(string) #f /pmacro-builtin-downcase "string-downcase")
1356 (list 'substring '(string start end) #f /pmacro-builtin-substring "get start of a string")
1357 (list 'splice 'arg-list #f /pmacro-builtin-splice "splice lists into the outer list")
1358 (list 'iota '(count . start-incr) #f /pmacro-builtin-iota "iota number generator")
1359 (list 'map '(pmacro list1 . rest) #f /pmacro-builtin-map "map a pmacro over a list of arguments")
1360 (list 'for-each '(pmacro list1 . rest) #f /pmacro-builtin-for-each "execute a pmacro over a list of arguments")
1361 (list 'eval '(expr) #t /pmacro-builtin-eval "expand(evaluate) expr")
1362 (list 'exec '(expr) #f /pmacro-builtin-exec "execute expr immediately")
1363 (list 'apply '(pmacro arg-list) #f /pmacro-builtin-apply "apply a pmacro to a list of arguments")
1364 (list 'pmacro '(params expansion) #t /pmacro-builtin-pmacro "create a pmacro on-the-fly")
1365 (list 'pmacro? '(arg) #f /pmacro-builtin-pmacro? "return true if arg is a pmacro")
1366 (list 'let '(locals expr1 . rest) #t /pmacro-builtin-let "create a binding context, let-style")
1367 (list 'let* '(locals expr1 . rest) #t /pmacro-builtin-let* "create a binding context, let*-style")
1368 (list 'if '(expr then . else) #t /pmacro-builtin-if "if expr is true, process then, else else")
1369 (list 'case '(expr case1 . rest) #t /pmacro-builtin-case "process statement that matches expr")
1370 (list 'cond '(expr1 . rest) #t /pmacro-builtin-cond "process first statement whose expr succeeds")
1371 (list 'begin 'rest #t /pmacro-builtin-begin "process a sequence of statements")
1372 (list 'print 'exprs #f /pmacro-builtin-print "print exprs, for debugging purposes")
1373 (list 'dump '(expr) #f /pmacro-builtin-dump "dump expr, for debugging purposes")
1374 (list 'error 'message #f /pmacro-builtin-error "print error message and exit")
1375 (list 'list 'exprs #f /pmacro-builtin-list "return a list of exprs")
1376 (list 'ref '(l n) #f /pmacro-builtin-ref "return n'th element of list l")
1377 (list 'length '(x) #f /pmacro-builtin-length "return length of symbol, string, or list")
1378 (list 'replicate '(n expr) #f /pmacro-builtin-replicate "return list of expr replicated n times")
1379 (list 'find '(pred l) #f /pmacro-builtin-find "return elements of list l matching pred")
1380 (list 'equal? '(x y) #f /pmacro-builtin-equal? "deep comparison of x and y")
1381 (list 'andif 'rest #t /pmacro-builtin-andif "return first #f element, otherwise return last element")
1382 (list 'orif 'rest #t /pmacro-builtin-orif "return first non-#f element found, otherwise #f")
1383 (list 'not '(x) #f /pmacro-builtin-not "return !x")
1384 (list 'eq '(x y) #f /pmacro-builtin-eq "return true if x == y")
1385 (list 'ne '(x y) #f /pmacro-builtin-ne "return true if x != y")
1386 (list 'lt '(x y) #f /pmacro-builtin-lt "return true if x < y")
1387 (list 'gt '(x y) #f /pmacro-builtin-gt "return true if x > y")
1388 (list 'le '(x y) #f /pmacro-builtin-le "return true if x <= y")
1389 (list 'ge '(x y) #f /pmacro-builtin-ge "return true if x >= y")
1390 (list 'add '(x y) #f /pmacro-builtin-add "return x + y")
1391 (list 'sub '(x y) #f /pmacro-builtin-sub "return x - y")
1392 (list 'mul '(x y) #f /pmacro-builtin-mul "return x * y")
1393 (list 'div '(x y) #f /pmacro-builtin-div "return x / y")
1394 (list 'rem '(x y) #f /pmacro-builtin-rem "return x % y")
1395 (list 'sll '(x n) #f /pmacro-builtin-sll "return logical x << n")
1396 (list 'srl '(x n) #f /pmacro-builtin-srl "return logical x >> n")
1397 (list 'sra '(x n) #f /pmacro-builtin-sra "return arithmetic x >> n")
1398 (list 'and '(x y) #f /pmacro-builtin-and "return x & y")
1399 (list 'or '(x y) #f /pmacro-builtin-or "return x | y")
1400 (list 'xor '(x y) #f /pmacro-builtin-xor "return x ^ y")
1401 (list 'inv '(x) #f /pmacro-builtin-inv "return ~x")
1402 (list 'car '(x) #f /pmacro-builtin-car "return (car x)")
1403 (list 'cdr '(x) #f /pmacro-builtin-cdr "return (cdr x)")
1404 (list 'caar '(x) #f /pmacro-builtin-caar "return (caar x)")
1405 (list 'cadr '(x) #f /pmacro-builtin-cadr "return (cadr x)")
1406 (list 'cdar '(x) #f /pmacro-builtin-cdar "return (cdar x)")
1407 (list 'cddr '(x) #f /pmacro-builtin-cddr "return (cddr x)")
1408 (list 'internal-test '(expr) #f /pmacro-builtin-internal-test "testsuite use only")
1410 (for-each (lambda (x)
1411 (let ((name (string->symbol (string-append "." (symbol->string (list-ref x 0)))))
1412 (arg-spec (list-ref x 1))
1413 (syntactic? (list-ref x 2))
1414 (pmacro (list-ref x 3))
1415 (comment (list-ref x 4)))
1417 (/pmacro-make name arg-spec #f syntactic? pmacro comment))
1420 (/pmacro-make name arg-spec #f syntactic? pmacro comment)))))
1424 ; Initialize so we're ready to use after loading.