summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog14
-rw-r--r--eval.c19
-rw-r--r--parser.y38
-rw-r--r--txr.146
4 files changed, 78 insertions, 39 deletions
diff --git a/ChangeLog b/ChangeLog
index f070e9da..c35467ff 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2011-12-04 Kaz Kylheku <kaz@kylheku.com>
+
+ * eval.c (op_qquote_error, op_unquote_error): New static functions.
+ (expand_qquote): Bugfix: missing case added to handle directly quoted
+ quasiquote.
+ (eval_init): Error-catching pseudo-operators registered in
+ op_table.
+
+ * parser.y (force_regular_quotes): New function.
+ (list): Quotes within unquotes and splices are regular.
+
+ * txr.1: Clarified new rules. Removed description of ,'form and ,*'form
+ special syntax.
+
2011-12-03 Kaz Kylheku <kaz@kylheku.com>
Expose lazy lists in TXR Lisp.
diff --git a/eval.c b/eval.c
index c2ae69ce..e6ea4909 100644
--- a/eval.c
+++ b/eval.c
@@ -353,6 +353,19 @@ static val op_quote(val form, val env)
return second(form);
}
+static val op_qquote_error(val form, val env)
+{
+ eval_error(form, lit("unexpanded quasiquote encountered"), nao);
+ return second(form);
+}
+
+static val op_unquote_error(val form, val env)
+{
+ eval_error(form, lit("unquote/splice without matching quote"), nao);
+ return second(form);
+}
+
+
static val bindings_helper(val vars, val env, val sequential, val ctx_form)
{
val iter;
@@ -739,6 +752,9 @@ static val expand_qquote(val qquoted_form)
second(qquoted_form), nao);
} else if (sym == unquote_s) {
return expand(second(qquoted_form));
+ } else if (sym == qquote_s) {
+ return rlcp(expand_qquote(expand_qquote(second(qquoted_form))),
+ qquoted_form);
} else {
val f = car(qquoted_form);
val r = cdr(qquoted_form);
@@ -1004,6 +1020,9 @@ void eval_init(void)
apply_s = intern(lit("apply"), user_package);
sethash(op_table, quote_s, cptr((mem_t *) op_quote));
+ sethash(op_table, qquote_s, cptr((mem_t *) op_qquote_error));
+ sethash(op_table, unquote_s, cptr((mem_t *) op_unquote_error));
+ sethash(op_table, splice_s, cptr((mem_t *) op_unquote_error));
sethash(op_table, let_s, cptr((mem_t *) op_let));
sethash(op_table, let_star_s, cptr((mem_t *) op_let));
sethash(op_table, lambda_s, cptr((mem_t *) op_lambda));
diff --git a/parser.y b/parser.y
index 82fd9101..e4f954ce 100644
--- a/parser.y
+++ b/parser.y
@@ -50,6 +50,7 @@ static val define_transform(val define_form);
static val lit_char_helper(val litchars);
static val optimize_text(val text_form);
static val choose_quote(val quoted_form);
+static val force_regular_quotes(val form);
static wchar_t char_from_name(wchar_t *name);
static val parsed_spec;
@@ -607,16 +608,14 @@ var_op : '*' { $$ = list(t, nao); }
list : '(' exprs ')' { $$ = rl($2, num($1)); }
| '(' ')' { $$ = nil; }
- | ',' expr { val expr = $2;
- if (consp(expr) && first(expr) == qquote_s)
- expr = cons(quote_s, rest(expr));
- $$ = rlcp(list(unquote_s, expr, nao), $2); }
+ | ',' expr { $$ = rlcp(list(unquote_s,
+ force_regular_quotes($2), nao),
+ $2); }
| '\'' expr { $$ = rlcp(list(choose_quote($2),
$2, nao), $2); }
- | SPLICE expr { val expr = $2;
- if (consp(expr) && first(expr) == qquote_s)
- expr = cons(quote_s, rest(expr));
- $$ = rlcp(list(splice_s, expr, nao), $2); }
+ | SPLICE expr { $$ = rlcp(list(splice_s,
+ force_regular_quotes($2), nao),
+ $2); }
| '(' error { $$ = nil;
yybadtoken(yychar, lit("list expression")); }
;
@@ -912,6 +911,29 @@ static val choose_quote(val quoted_form)
return unquotes_occur(quoted_form) ? qquote_s : quote_s;
}
+static val force_regular_quotes(val form)
+{
+ if (atom(form)) {
+ return form;
+ } else {
+ val sym = car(form);
+ val body = cdr(form);
+
+ if (sym == qquote_s) {
+ return rlcp(cons(quote_s, force_regular_quotes(body)), form);
+ } if (sym == unquote_s || sym == splice_s) {
+ return form;
+ } else {
+ val car_sub = force_regular_quotes(sym);
+ val cdr_sub = force_regular_quotes(body);
+
+ if (car_sub == sym && cdr_sub == body)
+ return form;
+ return rlcp(cons(car_sub, cdr_sub), form);
+ }
+ }
+}
+
val rl(val form, val lineno)
{
sethash(form_to_ln_hash, form, lineno);
diff --git a/txr.1 b/txr.1
index 0ad576c7..7a21a2b3 100644
--- a/txr.1
+++ b/txr.1
@@ -4223,11 +4223,16 @@ value of 'a is the symbol a itself, whereas the value of a is the value
of the variable a.
Note that TXR Lisp does not have a distinct quote and backquote syntax.
-There is only one quote, which supports unquoting.
+There is only one quote, which supports unquoting. The quote behaves
+either like a quasiquote or a regular quote according to these rules:
-A quoted form which contains no unquotes codifies an ordinary quote.
+1. A quoted form which contains no unquotes or splices is an ordinary quote.
-A quoted form which contains unquotes expresses a quasiquote.
+2. Quotes occuring within forms that are unquoted or spliced are ordinary
+quotes, not quasiquotes.
+
+3. Quotes which contain unquotes and splices and are not within unquotes
+or splices are quasiquotes.
.IP ,form
@@ -4239,34 +4244,13 @@ in the quote stands for itself, except for the ,(+ 2 2) which is evaluated.
.IP ,*form
-The comma-star operator is used within a quoted list to denote a splicing unquote.
-Wheras the quote suppresses evaluation, the comma introduces an exception:
-the form which follows ,* must evaluate to a list. That list is spliced into
-the quoted list. For example: '(a b c ,*(list (+ 3 3) (+ 4 4) d) evaluates
-to (a b c 6 8 d). The expression (list (+ 3 3) (+ 4 4)) is evaluated
+The comma-star operator is used within a quoted list to denote a splicing
+unquote. Wheras the quote suppresses evaluation, the comma introduces an
+exception: the form which follows ,* must evaluate to a list. That list is
+spliced into the quoted list. For example: '(a b c ,*(list (+ 3 3) (+ 4 4) d)
+evaluates to (a b c 6 8 d). The expression (list (+ 3 3) (+ 4 4)) is evaluated
to produce the list (6 8), and this list is spliced into the quoted template.
-.IP ,'form
-
-The comma-quote combination has a special meaning: the quote always
-behaves as a regular quote and not a quasiquote, even if form contains
-unquotes. Therefore, it does not "capture" these unquotes: they cannot
-"belong" to this quote. The comma and quote "cancel out", so the only effect
-of comma-quote is to add one level of unquoting. So for instance, whereas in
-'(a b c '(,d)), the subsitution of d belongs to the inner quote (it is unquoted
-by the leftmost comma which belongs to the innermost quote) by contrast,
-in '(a b c '(,',d)) the d is now one comma removed from the leftmost comma and
-thus the substitution of d belongs to the outer quote.
-In other dialects of Lisp, this would be written `(a b c `(,',d)), making it
-explicit which kind of quote is being specified. TXR Lisp works out which
-kind of quote to use internally.
-
-.IP ,*'form
-
-The comma-splice form is analogous to comma-quote (see above). Like in the
-,' combination, in the ,*' combination, the quote behaves as a regular quote
-and not a quasiquote.
-
.PP
.SS Nested Quotes
@@ -4279,8 +4263,8 @@ quote, which protects it from evaluation. To get the (+ 1 2) value "through"
to the inner quote, the unquote syntax must also be nested using multiple
commas, like this: '(1 2 3 '(4 5 6 ,',(+ 1 2))). The leftmost comma goes
with the innermost quote. The quote between the commas protects the (+ 1 2)
-from repeated evaluations: the two unquotes call for two evaluations, but
-we only want (+ 1 2) to be evaluated once.
+from repeated evaluations. According to rule 2 above, it is a regular
+quote, and so it does not capture the following comma.
.SS Lisp Operators