diff options
-rw-r--r-- | ChangeLog | 14 | ||||
-rw-r--r-- | eval.c | 19 | ||||
-rw-r--r-- | parser.y | 38 | ||||
-rw-r--r-- | txr.1 | 46 |
4 files changed, 78 insertions, 39 deletions
@@ -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. @@ -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)); @@ -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); @@ -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 |