summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog9
-rw-r--r--share/txr/stdlib/ifa.tl16
-rw-r--r--txr.1145
3 files changed, 166 insertions, 4 deletions
diff --git a/ChangeLog b/ChangeLog
index 293d2f3f..a103ae57 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,14 @@
2015-06-19 Kaz Kylheku <kaz@kylheku.com>
+ * txr.1: Documented ifa.
+
+ * share/txr/stdlib/ifa.tl: Tightened up the tests for
+ situations when the macro is ill-formed, following
+ the improved specification. Also, eval-error is thrown
+ instead of just error.
+
+2015-06-19 Kaz Kylheku <kaz@kylheku.com>
+
Test ifa macro.
* Makefile (TEST_OUT): Include .tl files.
diff --git a/share/txr/stdlib/ifa.tl b/share/txr/stdlib/ifa.tl
index f7c4fcb0..180e5231 100644
--- a/share/txr/stdlib/ifa.tl
+++ b/share/txr/stdlib/ifa.tl
@@ -29,15 +29,23 @@
(cond
((or (atom test) (null (cdr test))) ^(let ((it ,test))
(if it ,then ,else)))
- ((member (first test) '(not null)) ^(ifa ,(second test) ,else ,then))
+ ((member (first test) '(not null false))
+ (unless (eql (length test) 2)
+ (throwf 'eval-error "ifa: wrong number of arguments to ~s"
+ (first test)))
+ ^(ifa ,(second test) ,else ,then))
(t (let* ((sym (first test))
(args (rest test))
(n-candidate-args [count-if candidate-p args])
(pos-candidate (or [pos-if candidate-p args] 0)))
- (unless (fboundp sym)
- (error "ifa: only works with global functions."))
+ (unless (or (lexical-fun-p e sym)
+ (and (or (functionp (symbol-function sym))
+ (null (symbol-function sym)))))
+ (throwf 'eval-error "ifa: test expression must be \
+ \ a simple function call"))
(when (> n-candidate-args 1)
- (error "ifa: ambiguous situation: not clear what can be \"it\"."))
+ (throwf 'eval-error "ifa: ambiguous situation: \
+ \ not clear what can be \"it\""))
(let* ((temps (mapcar (ret (gensym)) args))
(it-temp [temps pos-candidate]))
^(let* (,*(zip temps args) (it ,it-temp))
diff --git a/txr.1 b/txr.1
index 419b0b7b..c41867b3 100644
--- a/txr.1
+++ b/txr.1
@@ -10796,6 +10796,151 @@ is returned.
(format t "frobosity value ~a exceeds 150\en" fv))
.cble
+.coNP Macro @ ifa
+.synb
+.mets (ifa < cond < then <> [ else ])
+.syne
+.desc
+The
+.code ifa
+macro provides a anaphoric conditional operator resembling the
+.code if
+operator. Around the evaluation of the
+.meta then
+and
+.meta else
+forms, the symbol
+.code it
+is implicitly bound to the value of a subexpression of
+.metn cond ,
+providing a reference to that value, similar to the word
+"it" in the English language, and similar anaphoric pronouns
+in other languages.
+
+The
+.code ifa
+macro imposes several restrictions on the
+.meta cond
+expression. Firstly, the
+.meta cond
+expression must be either an atom, or a function call form:
+a compound expression with a symbol in the leftmost position
+which resolves to a function. Otherwise the
+.code ifa
+macro invocation is ill-formed.
+
+Secondly, if the
+.meta cond
+expression is a function call with more than two arguments,
+at most one of them may be an it-candidate: an expression
+viable for having its value bound to the
+.code it
+symbol. If there are two or more it-candidates, the
+.code ifa
+expression is ill-formed.
+
+If
+.meta cond
+is an atom, or a function call expression with no arguments,
+then the
+.code it
+symbol is not bound. Effectively,
+.code ifa
+macro behaves like the ordinary
+.code if
+operator.
+
+If
+.meta cond
+is an invocation of the functions
+.code not
+or
+.codn null ,
+
+If
+.meta cond
+is a function call with exactly one argument, then the
+.code it
+variable is bound to the value of that argument, except when
+the function being called is
+.codn not ,
+.code null ,
+or
+.codn false .
+That special situation is rewritten according to the following pattern:
+
+.cblk
+.mets (ifa (not << expr ) < then << else ) -> (ifa < expr < else << then )
+.cble
+
+Note the reversal of
+.meta then
+and
+.metn else .
+
+If
+.meta cond
+is a function call with two or more arguments, then it is only
+well-formed if zero or one of those arguments are it-candidates.
+If there is an argument which is an it-candidate, then the
+.code it
+variable is bound to its value. Otherwise the variable is bound
+to the value of the leftmost argument expression, regardless of whether that
+argument expression is an it-candidate.
+
+An it-candidate is any expression which is not a constant expression
+according to the
+.code constantp
+function, and not a symbol.
+
+In all other regards, the
+.code ifa
+macro behaves similarly to
+.codn if .
+
+The
+.meta cond
+expression is evaluated, and, if applicable,
+the value of the appropriate argument is captured and bound to
+the variable
+.code it
+whose scope extends over the
+.meta then
+form, as well as over
+.metn else ,
+if present.
+
+If
+.meta cond
+yields a true value, then
+.meta then
+is evaluated and the resulting value is returned, otherwise
+.meta else
+is evaluated if present and its value is returned.
+A missing
+.meta else
+is treated as if it were the
+.code nil
+form.
+
+.TP* Examples:
+.cblk
+ (ifa t 1 0) -> 1
+
+ (let ((x 6) (y 49))
+ (ifa (> y (* x x)) ;; it binds to (* x x)
+ (list it)))
+ -> (36)
+
+ (ifa (evenp 4)
+ (list it))
+ -> (4)
+
+ (ifa (not (oddp 4))
+ (list it))
+ -> (4)
+.cble
+
.coNP Macro @ dotimes
.synb
.mets (dotimes >> ( var < count-form <> [ result-form ]) << body-form *)