diff options
-rw-r--r-- | ChangeLog | 16 | ||||
-rw-r--r-- | eval.c | 68 | ||||
-rw-r--r-- | parser.y | 4 | ||||
-rw-r--r-- | txr.1 | 60 |
4 files changed, 125 insertions, 23 deletions
@@ -1,3 +1,19 @@ +2012-02-01 Kaz Kylheku <kaz@kylheku.com> + + * eval.c (lookup_sym_lisp1): New function. + (do_eval, do_eval_args): New static functions. + (eval, eval_args): Become wrappers for do_eval and do_eval_args, + respectively. + (eval_lisp1, eval_args_lisp1): New static functions. + (dwim_loc, op_dwim): Use eval_lisp1 and eval_args_lisp1 instead + of eval and eval_args. + + * parser.y (meta_expr): Bugfix: expand the whole dwim expression, + rather than its arguments, which are not an expression. + + * txr.1: Updated with notes that dwim really does Lisp-1 style + evaluation. + 2012-01-29 Kaz Kylheku <kaz@kylheku.com> * txr.vim: More color categories. More accurate matching of @ elements. @@ -171,6 +171,31 @@ val lookup_fun(val env, val sym) } } +static val lookup_sym_lisp1(val env, val sym) +{ + uses_or2; + + if (nullp(env)) { + val bind = gethash(top_vb, sym); + if (cobjp(bind)) { + struct c_var *cv = (struct c_var *) cptr_get(bind); + cv->bind->c.cdr = *cv->loc; + return cv->bind; + } + return or2(bind, gethash(top_fb, sym)); + } else { + type_check(env, ENV); + + { + val binding = or2(assoc(sym, env->e.vbindings), + assoc(sym, env->e.fbindings)); + if (binding) + return binding; + return lookup_sym_lisp1(env->e.up_env, sym); + } + } +} + static val bind_args(val env, val params, val args, val ctx_form) { val new_bindings = nil; @@ -300,11 +325,15 @@ static val apply_intrinsic(val fun, val args) return apply(fun, args, cons(apply_s, nil)); } -static val eval_args(val form, val env, val ctx_form) +static val do_eval(val form, val env, val ctx_form, + val (*lookup)(val env, val sym)); + +static val do_eval_args(val form, val env, val ctx_form, + val (*lookup)(val env, val sym)) { list_collect_decl (values, ptail); for (; form; form = cdr(form)) - list_collect(ptail, eval(car(form), env, ctx_form)); + list_collect(ptail, do_eval(car(form), env, ctx_form, lookup)); return values; } @@ -324,7 +353,8 @@ static val eval_intrinsic(val form, val env) return eval(form, or2(env, make_env(nil, nil, env)), form); } -val eval(val form, val env, val ctx_form) +static val do_eval(val form, val env, val ctx_form, + val (*lookup)(val env, val sym)) { debug_enter; @@ -337,7 +367,7 @@ val eval(val form, val env, val ctx_form) if (!bindable(form)) { debug_return (form); } else { - val binding = lookup_var(env, form); + val binding = lookup(env, form); if (binding) debug_return (cdr(binding)); eval_error(ctx_form, lit("unbound variable ~s"), form, nao); @@ -353,7 +383,7 @@ val eval(val form, val env, val ctx_form) val fbinding = lookup_fun(env, oper); if (fbinding) { - val args = eval_args(rest(form), env, form); + val args = do_eval_args(rest(form), env, form, lookup); debug_frame(oper, args, nil, env, nil, nil, nil); debug_return (apply(cdr(fbinding), args, form)); debug_end; @@ -376,6 +406,26 @@ val eval(val form, val env, val ctx_form) debug_leave; } +val eval(val form, val env, val ctx_form) +{ + return do_eval(form, env, ctx_form, &lookup_var); +} + +static val eval_lisp1(val form, val env, val ctx_form) +{ + return do_eval(form, env, ctx_form, &lookup_sym_lisp1); +} + +static val eval_args(val form, val env, val ctx_form) +{ + return do_eval_args(form, env, ctx_form, &lookup_var); +} + +static val eval_args_lisp1(val form, val env, val ctx_form) +{ + return do_eval_args(form, env, ctx_form, &lookup_sym_lisp1); +} + val bindable(val obj) { return (obj && symbolp(obj) && obj != t && !keywordp(obj)) ? t : nil; @@ -639,8 +689,8 @@ static val op_defun(val form, val env) static val *dwim_loc(val form, val env, val op, val newval, val *retval) { - val obj = eval(second(form), env, form); - val args = eval_args(rest(rest(form)), env, form); + val obj = eval_lisp1(second(form), env, form); + val args = eval_args_lisp1(rest(rest(form)), env, form); if (!obj) eval_error(form, lit("[~s ]: cannot assign nil"), obj, nao); @@ -936,8 +986,8 @@ static val op_return_from(val form, val env) static val op_dwim(val form, val env) { - val obj = eval(second(form), env, form); - val args = eval_args(rest(rest(form)), env, form); + val obj = eval_lisp1(second(form), env, form); + val args = eval_args_lisp1(rest(rest(form)), env, form); if (!obj) return nil; @@ -677,8 +677,8 @@ list : '(' exprs ')' { $$ = rl($2, num($1)); } meta_expr : METAPAR exprs ')' { $$ = rlcp(cons(expr_s, expand($2)), $2); } | METABKT exprs ']' { $$ = rlcp(cons(expr_s, - cons(dwim_s, - expand($2))), $2); } + expand(cons(dwim_s, $2))), + $2); } | METAPAR ')' { $$ = rl(cons(expr_s, nil), num(lineno)); } | METABKT ']' { $$ = rl(cons(expr_s, cons(dwim_s, nil)), num(lineno)); } @@ -4431,6 +4431,9 @@ can be used to call it, instead of (call foo 3). If foo is a vector, then [foo 3] retrieves the fourth element, like (vecref foo 3). Indexing over lists, strings and hash tables is possible, and the notation is assignable. +Furthermore, any arguments enclosed in [] which are symbols are treated +according to a modified namespace lookup rule. + More details are given in the documentation for the dwim operator. .SS Lisp Operators @@ -4867,9 +4870,21 @@ The notation [...] is a shorthand equivalent to (dwim ...) and is the preferred way for writing dwim expressions. The dwim operator takes a variable number of arguments, which are -all evaluated in the same way. How many are required depends on the type of -object to which the first argument expression evaluates: of the first argument. -The possibilities are: +all evaluated in the same way: the first argument is not evaluated different +from the remaining arguments. + +Furthermore, the evaluation of symbols is done differently: all of the +enclosing scopes are considered as if the function and variable namespaces are +collapsed into a single namespace, in which variable names take precedence over +functions if there exist mutiple bindings. This allows functions to be +referenced without the fun operator. + +All forms which are not symbols are evaluated using the normal evaluation rules. + +The first argument may not be an operator such as let, et cetera. + +How many are required depends on the type of object to which the first argument +expression evaluates: of the first argument. The possibilities are: .IP [<function> <argument>*] Call the given the function object to the given arguments. @@ -4919,6 +4934,8 @@ See the section on Indexing below. Retrieve a value from the hash table corresponding to <key>, or <default-value> if there is no such entry. +The first argument may not be an operator such as let, only a function. + The places denoted by the dwim operator can be assigned. There are some restrictions. List, string and vector ranges can only be replaced using the set operator. The other operators like push do not apply. @@ -4957,21 +4974,40 @@ The dwim operator allows for a Lisp-1 flavor of programming in TXR Lisp, which is normally Lisp-2, with some useful extensions. A Lisp-1 dialect is one in which an expression like (a b) treats both a and b -as expressions with the same evaluation rules. Thus in a Lisp-1, named -functions do not exist as such: they are just variable bindings. -In a Lisp-1 (car 1 2) means that there is a variable called car, -which holds a function. In a Lisp-2 (car 1 2) means that there is -a function called car, and so (car car car) is possible, because +as expressions with the same evaluation rules. The symbols a and b are looked +up in a variable namespace. A function call occurs if the value of variable +a is a function object. Thus in a Lisp-1, named functions do not exist as +such: they are just variable bindings. In a Lisp-1 (car 1 2) means that there +is a variable called car, which holds a function. In a Lisp-2 (car 1 2) means +that there is a function called car, and so (car car car) is possible, because there can be also a variable called car. The Lisp-1 design has certain disadvantages, which are avoided in TXR Lisp by -confining the Lisp-1 expressivity inside the [...] notation. When round -parentheses are used, the normal Lisp-2 rules apply. A "best of both worlds" -situation is achieved. +confining the Lisp-1 expressivity inside the [...] notation, in which operators +are not allowed. When round parentheses are used, the normal Lisp-2 rules +apply. A "best of both worlds" situation is achieved. The square brackets +are just as convenient as parentheses and at the same time visually distinct, +making it clear that different rules apply. + +The Lisp-1 is useful for functional programming, because it eliminates +occurences of the call and fun operators. For instance: -Lisp-1 dialects can provide useful extensions by giving a meaning + ;; regular notation + + (funcall foo (fun second) '((1 a) (2 b))) + + ;; [] notation + + [foo second '((1 a) (2 b))] -> (a b) + +Lisp-1 dialects can also provide useful extensions by giving a meaning to objects other than functions in the first position of a form, and the dwim/[...] syntax does exactly this. +However, unlike Lisp-1 dialects, the [] syntax does not allow operators. +It +.B is +an operator: (dwim ...). + .SS Operators for and for* |