Variable macros are expanded simply by replacing their name with their value.
-After a pmacro has been expanded, if the result of the pmacro is another
-pmacro invocation, it is in turn processed. This happens just once,
-not repeatedly.
+After a pmacro has been expanded, if the result is a symbol that names
+another pmacro, it is in turn processed. This happens just once,
+not repeatedly.@footnote{This behaviour will go away eventually, do not
+rely on it.}
Here is a simple example that uses pmacros to simplify the
definition of several instructions.
appear in the ``top level'' list.
Another way to accomplish the same thing that doesn't require
-@code{.splice}, @code{.unsplice} is to use @code{.for-each}, @code{.eval}.
+@code{.splice}, @code{.unsplice} is to use @code{.for-each}, @code{.exec}.
@code{.for-each} is only used for its side-effects, it does not
return a result. Therefore, in order to actually cause the
@file{.cpu} file reader to see any definitions we need to use
-@code{.eval} to pass the dnis to the reader.
+@code{.exec} to pass the dnis to the reader.
@smallexample
(define-pmacro (define-arith-insn ispec)
- (.eval (dni (.ref ispec 0)
+ (.exec (dni (.ref ispec 0)
(.str (.ref ispec 0) " reg/reg")
()
(.str (.ref ispec 0) " $dr,$sr")
(set dr ((.ref ispec 0) dr sr))
()
))
- (.eval (dni (.ref ispec 0)
+ (.exec (dni (.ref ispec 0)
(.str (.ref ispec 0) " reg/imm")
()
(.str (.ref ispec 0) " $dr,$imm")
There is no result, or rather the result is always the empty list ().
Note that this macro is therefore useless for macro expansion.
It's purpose is to process @code{macro} for its side-effects.
-The @code{.eval} builtin pmacro is useful here.
+The @code{.exec} builtin pmacro is useful here.
@node Conditional macros
@section Conditional macros
@node Pmacro utilities
@section Pmacro utilities
-Macros for working with pmacros.
+Pmacros for working with pmacros.
@menu
-* Immediate evaluation of a macro:: The @code{.eval} builtin
-* Applying a macro to a list:: The @code{.apply} builtin
-* Defining a pmacro inline:: The @code{.pmacro} builtin
-* Passing macros as arguments:: Passing a macro to another macro
-* Defining a block of locals:: The @code{.let}, @code{.let*} builtins
-* A block of statements:: The @code{.begin} builtin
+* Re-evaluating an expression:: The @code{.eval} builtin
+* Immediate execution of a command:: The @code{.exec} builtin
+* Applying a pmacro to a list:: The @code{.apply} builtin
+* Defining a pmacro inline:: The @code{.pmacro} builtin
+* Passing pmacros as arguments:: Passing a pmacro to another pmacro
+* Defining a block of locals:: The @code{.let}, @code{.let*} builtins
+* A block of statements:: The @code{.begin} builtin
@end menu
-@node Immediate evaluation of a macro
-@subsection Immediate evaluation of a macro
+@node Re-evaluating an expression
+@subsection Re-evaluating an expression
@cindex .eval
Syntax: @samp{(.eval expr)}
+Sometimes one wishes to build up an expression in non-trivial ways
+and then have the expression evaluated.
+Use the @code{.eval} builtin pmacro for this purpose,
+it re-evaluates @samp{expr}, invoking any pmacros contained therein.
+
+A perhaps contrived example is when one wants to construct the pmacro's
+name from a set of parameters.
+
+Example:
+
+@smallexample
+(define-pmacro (do-foo a b) (foo a b))
+(define-pmacro (do-bar a b) (bar a b))
+(define-pmacro (doer what a b) (.eval (.list (.sym do- what) a b)))
+(doer foo 1 2) ;; --> (foo 1 2)
+(doer bar 3 4) ;; --> (bar 3 4)
+@end smallexample
+
+@node Immediate execution of a command
+@subsection Immediate execution of a command
+@cindex .exec
+
+Syntax: @samp{(.exec expr)}
+
Sometimes one wishes to pass an expression to the @file{.cpu} file reader
immediately, rather than waiting for it to process the expression
that is the result of a pmacro. This typically happens with the
@code{.for-each} builtin pmacro.
-Use the @code{.eval} builtin pmacro for this purpose.
+Use the @code{.exec} builtin pmacro for this purpose.
It immediately passes @samp{expr} to the @file{.cpu} file reader
-and returns @code{()} as a result.
+for processing and returns @code{()} as a result.
-@node Applying a macro to a list
-@subsection Applying a macro to a list
+@node Applying a pmacro to a list
+@subsection Applying a pmacro to a list
@cindex .apply
Invoke a macro with each argument coming from an element of a list,
The contents of a @code{.pmacro} are not evaluated until the pmacro
is invoked.
-@node Passing macros as arguments
-@subsection Passing macros as arguments
+@node Passing pmacros as arguments
+@subsection Passing pmacros as arguments
Macros may be passed to other macros.
; pmacro-debug - expand all pmacros in an expression,
; printing various debugging messages.
-; This does not process .eval.
+; This does not process .exec.
;
; (pmacro-debug expression)
; (.upcase string)
; (.downcase string)
; (.substring string start end) - get part of a string
-; (.splice a b (.unsplice c) d e ...) - quasi-quote/unquote-splicing
+; (.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) - process expr immediately
+; (.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
; (.let (var-list) expr1 . expr-rest) - akin to let in Scheme
)
; Expand any pmacros in EXPR, printing various debugging messages.
-; This does not process .eval.
+; This does not process .exec.
(define (pmacro-debug expr)
; FIXME: Need unwind protection.
; (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 we have quasi-quote,
-; unquote, unquote-splicing. Here we have splice, unsplice.
+; 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.
(define -pmacro-builtin-splice
(lambda arg-list
- ; ??? Not the most efficient implementation, but will the difference
- ; ever be measureable?
+ ; ??? Not the most efficient implementation.
(let loop ((arg-list arg-list) (result '()))
(cond ((null? arg-list) result)
((and (pair? (car arg-list)) (eq? '.unsplice (caar arg-list)))
)
; (.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.
+
+(define (-pmacro-builtin-eval loc env expr)
+ ;; -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.
+ (-pmacro-expand (-pmacro-expand expr env loc) env loc)
+)
+
+; (.exec expr)
-(define (-pmacro-builtin-eval expr)
- ;; If we're expanding pmacros for debugging purposes, don't eval,
+(define (-pmacro-builtin-exec expr)
+ ;; If we're expanding pmacros for debugging purposes, don't execute,
;; just return unchanged.
(if -pmacro-debug?
- (list '.eval expr)
+ (list '.exec expr)
(begin
(reader-process-expanded! expr)
nil)) ;; need to return something the reader will accept
(list '.iota '(count . start-incr) #f -pmacro-builtin-iota "iota number generator")
(list '.map '(pmacro list1 . rest) #f -pmacro-builtin-map "map a pmacro over a list of arguments")
(list '.for-each '(pmacro list1 . rest) #f -pmacro-builtin-for-each "execute a pmacro over a list of arguments")
- (list '.eval '(expr) #f -pmacro-builtin-eval "process expr immediately")
+ (list '.eval '(expr) #t -pmacro-builtin-eval "expand(evaluate) expr")
+ (list '.exec '(expr) #f -pmacro-builtin-exec "execute expr immediately")
(list '.apply '(pmacro arg-list) #f -pmacro-builtin-apply "apply a pmacro to a list of arguments")
(list '.pmacro '(params expansion) #t -pmacro-builtin-pmacro "create a pmacro on-the-fly")
(list '.let '(locals expr1 . rest) #t -pmacro-builtin-let "create a binding context, let-style")