URL
https://opencores.org/ocsvn/scarts/scarts/trunk
Subversion Repositories scarts
[/] [scarts/] [trunk/] [toolchain/] [scarts-binutils/] [binutils-2.19.1/] [cgen/] [pmacros.scm] - Rev 6
Compare with Previous | Blame | View Log
; Preprocessor-like macro support. ; Copyright (C) 2000, 2009 Red Hat, Inc. ; This file is part of CGEN. ; See file COPYING.CGEN for details. ; TODO: ; - Like C preprocessor macros, there is no scoping [one can argue ; there should be]. Maybe in time (??? Hmmm... done?) ; - Support for multiple macro tables. ; Non-standard required routines: ; Provided by Guile: ; make-hash-table, hashq-ref, hashq-set!, symbol-append, ; source-properties ; Provided by CGEN: ; location-property, location-property-set!, ; source-properties-location->string, ; single-location->string, location-top, unspecified-location, ; reader-process-expanded!, num-args-ok?, *UNSPECIFIED*. ; The convention we use says `-' begins "local" objects. ; At some point this might also use the Guile module system. ; This uses Guile's source-properties system to track source location. ; The chain of macro invocations is tracked and stored in the result as ; object property "location-property". ; Exported routines: ; ; pmacro-init! - initialize the pmacro system ; ; define-pmacro - define a symbolic or procedural pmacro ; ; (define-pmacro symbol ["comment"] expansion) ; (define-pmacro (symbol [args]) ["comment"] (expansion)) ; ; ARGS is a list of `symbol' or `(symbol default-value)' elements. ; ; pmacro-expand - expand all pmacros in an expression ; ; (pmacro-expand expression loc) ; ; pmacro-trace - same as pmacro-expand, but trace macro expansion ; Output is sent to current-error-port. ; ; (pmacro-trace expression loc) ; ; pmacro-dump - expand all pmacros in an expression, for debugging purposes ; ; (pmacro-dump expression) ; pmacro-debug - expand all pmacros in an expression, ; printing various debugging messages. ; This does not process .exec. ; ; (pmacro-debug expression) ; Builtin pmacros: ; ; (.sym symbol1 symbol2 ...) - symbolstr-append ; (.str string1 string2 ...) - stringsym-append ; (.hex number [width]) - convert to hex string ; (.upcase string) ; (.downcase string) ; (.substring string start end) - get part of a string ; (.splice a b (.unsplice c) d e ...) - splice list into another list ; (.iota count [start [increment]]) - number generator ; (.map pmacro arg1 . arg-rest) ; (.for-each pmacro arg1 . arg-rest) ; (.eval expr) - expand (or evaluate it) expr ; (.exec expr) - execute expr immediately ; (.apply pmacro-name arg) ; (.pmacro (arg-list) expansion) - akin go lambda in Scheme ; (.pmacro? arg) ; (.let (var-list) expr1 . expr-rest) - akin to let in Scheme ; (.let* (var-list) expr1 . expr-rest) - akin to let* in Scheme ; (.if expr then [else]) ; (.case expr ((case-list1) stmt) [case-expr-stmt-list] [(else stmt)]) ; (.cond (expr stmt) [(cond-expr-stmt-list)] [(else stmt)]) ; (.begin . stmt-list) ; (.print . exprs) - for debugging messages ; (.dump expr) - dump expr in readable format ; (.error . message) - print error message and exit ; (.list . exprs) ; (.ref l n) - extract the n'th element of list l ; (.length x) - length of symbol, string, or list ; (.replicate n expr) - return list of expr replicated n times ; (.find pred l) - return elements of list l matching pred ; (.equals x y) - deep comparison ; (.andif expr . rest) - && in C ; (.orif expr . rest) - || in C ; (.not expr) - ! in C ; (.eq x y) ; (.ne x y) ; (.lt x y) ; (.gt x y) ; (.le x y) ; (.ge x y) ; (.add x y) ; (.sub x y) ; (.mul x y) ; (.div x y) - integer division ; (.rem x y) - integer remainder ; (.sll x n) - shift left logical ; (.srl x n) - shift right logical ; (.sra x n) - shift right arithmetic ; (.and x y) - bitwise and ; (.or x y) - bitwise or ; (.xor x y) - bitwise xor ; (.inv x) - bitwise invert ; (.car l) ; (.cdr l) ; (.caar l) ; (.cadr l) ; (.cdar l) ; (.cddr l) ; (.internal-test expr) - testsuite internal use only ; ; NOTE: .cons currently absent on purpose ; ; .sym and .str convert numbers to symbols/strings as necessary (base 10). ; ; .pmacro is for constructing pmacros on-the-fly, like lambda, and is currently ; only valid as arguments to other pmacros or assigned to a local in a {.let} ; or {.let*}. ; ; NOTE: While Scheme requires tail recursion to be implemented as a loop, ; we do not. We might some day, but not today. ; ; ??? Methinks .foo isn't a valid R5RS symbol. May need to change ; to something else. ; True if doing pmacro expansion via pmacro-debug. ; True if doing pmacro expansion via pmacro-trace. ; The pmacro table. ; A copy of syntactic pmacros is kept separately. ; Marker to indicate a value is a pmacro. ; NOTE: Naming this "<pmacro>" is intentional. It makes them look like ; objects of class <pmacro>. However we don't use COS in part to avoid ; a dependency on COS and in part because displaying COS objects isn't well ; supported (displaying them in debugging dumps adds a lot of noise). ; Utilities to create and access pmacros. ; Cover functions to manage an "environment" in case a need or desire for ; another method arises. ; Error message generator. "<input>"":"":"":"; Issue an error where a number was expected. "invalid arg for "", expected number"; Verify N is a number. ; Issue an error where an integer was expected. "invalid arg for "", expected integer"; Verify N is an integer. ; Issue an error where a non-negative integer was expected. "invalid arg for "", expected non-negative integer"; Verify N is a non-negative integer. ; Expand a list of expressions, in order. ; The result is the value of the last one. ; Process list of keyword/value specified arguments. ; Build a list of default values, then override ones specified in ARGS, ; done "not an argument name""missing argument to #:keyword""bad keyword/value argument list"; Ensure each element has a value. ; done "argument value not specified"; If varargs pmacro, adjust result. ; not varargs ; Process a pmacro argument list. ; ARGS is either a fully specified position dependent argument list, ; or is a list of keyword/value pairs with missing values coming from ; DEFAULT-VALUES. ; Subroutine of -pmacro-apply/-smacro-apply to simplify them. ; Process the arguments, verify the correct number is present. "wrong number of arguments to pmacro "; Invoke a pmacro. ; Invoke a syntactic-form pmacro. ; ENV, LOC are handed down from -pmacro-expand. ;; Expand expression EXP using ENV, an alist of variable assignments. ;; LOC is the location stack thus far. ;; If the symbol is in `env', return its value. ;; Otherwise see if symbol is a globally defined pmacro. ;; Otherwise return the symbol unchanged. ;; cdr is value of (name . value) pair ;; Symbol is a pmacro. ;; If this is a procedural pmacro, let caller perform expansion. ;; Otherwise, return the pmacro's value. ;; Return symbol unchanged. ;; See if (car exp) is a pmacro. ;; Return pmacro or #f. "Checking for pmacro: ";; Subroutine of scan-list to simplify it. ;; Macro expand EXP which is known to be a non-null list. ;; LOC is the location stack thus far. ;; Check for syntactic forms. ;; They are handled differently in that we leave it to the transformer ;; routine to evaluate the arguments. ;; Note that we also don't support passing syntactic form functions ;; as arguments: We look up (car exp) here, not its expansion. ;; ??? Is it useful to trace these? ;; Not a syntactic form. ;; See if we have a pmacro. Do this before evaluating all the ;; arguments (even though we will eventually evaluate all the ;; arguments before invoking the pmacro) so that tracing is more ;; legible (we print the expression we're about to evaluate *before* ;; we evaluate its arguments). ;; Trace expansion here, we know we have a pmacro. ;; We use `write' to display `exp' to see strings quoted. "Expanding: "" env: "" location: ";; Evaluate all the arguments before invoking the pmacro. " result: ";; Not a pmacro. ;; Macro expand EXP which is known to be a non-null list. ;; LOC is the location stack thus far. ;; ;; This uses scan-list1 to do the real work, this handles location tracking. ;; pair? -> cheap non-null-list? ;; Copy source location to new expression. ;; Scan EXP, an arbitrary value. ;; LOC is the location stack thus far. ;; pair? -> cheap non-null-list? ;; Not a symbol or expression, return unchanged. ;; Re-examining `result' to see if it is another pmacro invocation ;; allows doing things like ((.sym a b c) arg1 arg2) ;; where `abc' is a pmacro. Scheme doesn't work this way, but then ;; this is CGEN. ; Return the argument spec from ARGS. ; ARGS is a [possibly improper] list of `symbol' or `(symbol default-value)' ; elements. For varargs pmacros, ARGS must be an improper list ; (e.g. (a b . c)) with the last element being a symbol. "argument not `symbol' or `(symbol . default-value)'""argument not `symbol' or `(symbol . default-value)'"; Return the default values specified in ARGS. ; The result is an alist of (#:arg-name . default-value) elements. ; ARGS is a [possibly improper] list of `symbol' or `(symbol . default-value)' ; elements. For varargs pmacros, ARGS must be an improper list ; (e.g. (a b . c)) with the last element being a symbol. ; Unspecified default values are recorded as #f. "argument not `symbol' or `(symbol . default-value)'""argument not `symbol' or `(symbol . default-value)'"; Build a procedure that performs a pmacro expansion. ; Earlier version, doesn't work with LOC as a <location> object, ; COS objects don't pass through eval1. ;(define (-pmacro-build-lambda prev-env params expansion) ; (eval1 `(lambda ,params ; (-pmacro-expand ',expansion ; (-pmacro-env-make ',prev-env ; ',params (list ,@params)))) ;) ; While using `define-macro' seems preferable, boot-9.scm uses it and ; I'd rather not risk a collision. I could of course make the association ; during parsing, maybe later. ; On the other hand, calling them pmacros removes all ambiguity. ; In the end the ambiguity removal is the deciding win. ; ; The syntax is one of: ; (define-pmacro symbol expansion) ; (define-pmacro symbol ["comment"] expansion) ; (define-pmacro (name args ...) expansion) ; (define-pmacro (name args ...) "documentation" expansion) ; ; If `expansion' is the name of a pmacro, its value is used (rather than its ; name). ; ??? The goal here is to follow Scheme's define/lambda, but not all variants ; are supported yet. There's also the difference that we treat undefined ; symbols as being themselves (i.e. "self quoting" so-to-speak). ; ; ??? We may want user-definable "syntactic" pmacros some day. Later. "invalid pmacro header""";;(if (> (length arg-rest) 1) ;;(-pmacro-error "extraneous arguments to define-pmacro" (cdr arg-rest))) ;;(if (not (string? comment)) ;;(-pmacro-error "invalid pmacro comment, expected string" comment)) ; syntactic-form? ; Expand any pmacros in EXPR. ; LOC is the <location> of EXPR. ; Debugging routine to trace pmacro expansion. ; FIXME: Need unwind protection. ;; We use `write' to display `expr' to see strings quoted. "Pmacro expanding: ";;(display "Top level env: " cep) (display nil cep) (newline cep) "Pmacro location: ""Pmacro result: "; Debugging utility to expand a pmacro, with no initial source location. ; Expand any pmacros in EXPR, printing various debugging messages. ; This does not process .exec. ; FIXME: Need unwind protection. ; Builtin pmacros. ; (.sym symbol1 symbol2 ...) - symbol-append, auto-convert numbers "invalid argument to .str"; (.str string1 string2 ...) - string-append, auto-convert numbers "invalid argument to .str"; (.hex number [width]) - convert number to hex string ; WIDTH, if present, is the number of characters in the result, beginning ; from the least significant digit. "wrong number of arguments to .hex"; (.upcase string) - convert a string or symbol to uppercase "invalid argument to .upcase"; (.downcase string) - convert a string or symbol to lowercase "invalid argument to .downcase"; (.substring string start end) - get part of a string ; `end' can be the symbol `end'. ;; FIXME: non-negative-integer "start not an integer""end not an integer nor symbol `end'""invalid argument to .substring"; .splice - splicing support ; Splice lists into the outer list. ; ; E.g. (define-pmacro '(splice-test a b c) '(.splice a (.unsplice b) c)) ; (pmacro-expand '(splice-test (1 (2) 3))) --> (1 2 3) ; ; Similar to `(1 ,@'(2) 3) in Scheme, though the terminology is slightly ; different (??? may need to revisit). In Scheme there's quasi-quote, ; unquote, unquote-splicing. Here we have splice, unsplice; with the proviso ; that pmacros don't have the concept of "quoting", thus all subexpressions ; are macro-expanded first, before performing any unsplicing. ; [??? Some may want a quoting facility, but I'd like to defer adding it as ; long as possible (and ideally never add it).] ; ; NOTE: The implementation relies on .unsplice being undefined so that ; (.unsplice (42)) is expanded unchanged. ; ??? Not the most efficient implementation. "argument to .unsplice must be a list""wrong number of arguments to .unsplice"; .iota ; Usage: ; (.iota count) ; start=0, incr=1 ; (.iota count start) ; incr=1 ; (.iota count start incr) "wrong number of arguments to .iota""count must be non-negative"; (.map pmacro arg1 . arg-rest) "not a pmacro""not a procedural pmacro"; (.for-each pmacro arg1 . arg-rest) "not a pmacro""not a procedural pmacro"; need to return something the reader will accept and ignore ; (.eval expr) ; NOTE: This is implemented as a syntactic form in order to get ENV and LOC. ; That's an implementation detail, and this is not really a syntactic form. ; ; ??? I debated whether to call this .expand, .eval has been a source of ; confusion/headaches. ;; -pmacro-expand is invoked twice because we're implemented as a syntactic ;; form: We *want* to be passed an evaluated expression, and then we ;; re-evaluate it. But syntactic forms pass parameters unevaluated, so we ;; have to do the first one ourselves. ; (.exec expr) ;; If we're expanding pmacros for debugging purposes, don't execute, ;; just return unchanged. ;; need to return something the reader will accept and ignore ; (.apply pmacro-name arg) "not a pmacro""not a procedural pmacro"; (.pmacro (arg-list) expansion) ; NOTE: syntactic form ;; ??? Prohibiting improper lists seems unnecessarily restrictive here. ;; e.g. (define (foo bar . baz) ...) ".pmacro parameter-spec is not a list"""; (.pmacro? arg) ; (.let (var-list) expr1 . expr-rest) ; NOTE: syntactic form "locals is not a list""syntax error in locals list"; (.let* (var-list) expr1 . expr-rest) ; NOTE: syntactic form "locals is not a list""syntax error in locals list"; (.if expr then [else]) ; NOTE: syntactic form "too many elements in else-clause, expecting 0 or 1"; (.case expr ((case-list1) stmt) [case-expr-stmt-list] [(else stmt)]) ; NOTE: syntactic form ; NOTE: this uses "member" for case comparison (Scheme uses memq I think) "case statement not a list""case statement has case but no expr""case must be \"else\" or list of choices"; (.cond (expr stmt) [(cond-expr-stmt-list)] [(else stmt)]) ; NOTE: syntactic form ; (.begin . stmt-list) ; NOTE: syntactic form ; (.print . expr) ; Strings have quotes removed. ; need to return something the reader will accept and ignore ; (.dump expr) ; Strings do not have quotes removed. ; need to return something the reader will accept and ignore ; (.error . expr) ; (.list expr1 ...) ; (.ref expr index) "invalid arg for .ref, expected list";; FIXME: call non-negative-integer? "invalid arg for .ref, expected non-negative integer"; (.length x) "invalid arg for .length, expected symbol, string, or list"; (.replicate n expr) ;; FIXME: call non-negative-integer? "invalid arg for .replicate, expected non-negative integer"; (.find pred l) "not a pmacro""not a list""not a procedural macro"; (.equals x y) ; (.andif . rest) ; NOTE: syntactic form ; Elements of EXPRS are evaluated one at a time. ; Unprocessed elements are not evaluated. ; (.orif . rest) ; NOTE: syntactic form ; Elements of EXPRS are evaluated one at a time. ; Unprocessed elements are not evaluated. ; (.not expr) ; Verify x,y are compatible for eq/ne comparisons. ; (.eq expr) "incompatible args for .eq, expected symbol""incompatible args for .eq, expected string""incompatible args for .eq, expected number""unsupported args for .eq"; (.ne expr) "incompatible args for .ne, expected symbol""incompatible args for .ne, expected string""incompatible args for .ne, expected number""unsupported args for .ne"; (.lt expr) ".lt"".lt"; (.gt expr) ".gt"".gt"; (.le expr) ".le"".le"; (.ge expr) ".ge"".ge"; (.add x y) ".add"".add"; (.sub x y) ".sub"".sub"; (.mul x y) ".mul"".mul"; (.div x y) - integer division ".div"".div"; (.rem x y) - integer remainder ; ??? Need to decide behavior. ".rem"".rem"; (.sll x n) - shift left logical ".sll"".sll"; (.srl x n) - shift right logical ; X must be non-negative, otherwise behavior is undefined. ; [Unless we introduce a size argument: How do you logical shift right ; an arbitrary precision negative number?] ".srl"".srl"; (.sra x n) - shift right arithmetic ".sra"".sra"; (.and x y) - bitwise and ".and"".and"; (.or x y) - bitwise or ".or"".or"; (.xor x y) - bitwise xor ".xor"".xor"; (.inv x) - bitwise invert ".inv"; (.car expr) "invalid arg for .car, expected pair"; (.cdr expr) "invalid arg for .cdr, expected pair"; (.caar expr) "invalid arg for .caar"; (.cadr expr) "invalid arg for .cadr"; (.cdar expr) "invalid arg for .cdar"; (.cddr expr) "invalid arg for .cddr"; (.internal-test expr) ; This is an internal builtin for use by the testsuite. ; EXPR is a Scheme expression that is executed to verify proper ; behaviour of something. It must return #f for FAIL, non-#f for PASS. ; The result is #f for FAIL, #t for PASS. ; This must be used in an expression, it is not sufficient to do ; (.internal-test mumble) because the reader will see #f or #t and complain. ; Initialization. ; Some "predefined" pmacros. ;; name arg-spec syntactic? function description "symbol-append""string-append""convert to -hex, with optional width""string-upcase""string-downcase""get start of a string""splice lists into the outer list""iota number generator""map a pmacro over a list of arguments""execute a pmacro over a list of arguments""expand(evaluate) expr""execute expr immediately""apply a pmacro to a list of arguments""create a pmacro on-the-fly""return true if arg is a pmacro""create a binding context, let-style""create a binding context, let*-style""if expr is true, process then, else else""process statement that matches expr""process first statement whose expr succeeds""process a sequence of statements""print exprs, for debugging purposes""dump expr, for debugging purposes""print error message and exit""return a list of exprs""return n'th element of list l""return length of symbol, string, or list""return list of expr replicated n times""return elements of list l matching pred""deep comparison of x and y""return first #f element, otherwise return last element""return first non-#f element found, otherwise #f""return !x""return true if x == y""return true if x != y""return true if x < y""return true if x > y""return true if x <= y""return true if x >= y""return x + y""return x - y""return x * y""return x / y""return x % y""return logical x << n""return logical x >> n""return arithmetic x >> n""return x & y""return x | y""return x ^ y""return ~x""return (car x)""return (cdr x)""return (caar x)""return (cadr x)""return (cdar x)""return (cddr x)""testsuite use only"; Initialize so we're ready to use after loading.