summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2012-01-25 09:59:40 -0800
committerKaz Kylheku <kaz@kylheku.com>2012-01-25 09:59:40 -0800
commit11173ced6d65339869fe74fbc9c4452a75e3fe26 (patch)
tree6c7e21fa0f7ebab6d5b4af9dc960fd96660682be
parent8b4578f295cc022e8bf0bb62d1a8cf8673636f27 (diff)
downloadtxr-11173ced6d65339869fe74fbc9c4452a75e3fe26.tar.gz
txr-11173ced6d65339869fe74fbc9c4452a75e3fe26.tar.bz2
txr-11173ced6d65339869fe74fbc9c4452a75e3fe26.zip
* eval.c (dwim_s): New symbol variable.
(dwim_loc, op_dwim): New static functions. (op_modplace): Support assignment to dwim forms with the help of dwim_loc. (expand_place): Handle dwim places. (eval_init): Initialize dwim_s. Register dwim operator in op_table. * eval.h (dwim_s): Declared. * lib.c (chr_str, chr_str_set): Allow negative indices to index backwards from end of string. (vecref, vecref_l): Allow negative indices to index from rear of array. (obj_print, obj_pprint): Render (dwim ...) forms as [...]. * parser.l: Peoduce new METABKT token type for @[, and '[', ']' tokens. * parser.y (METABKT): New token. %type declaration for '['. (list): Support square-bracket style of list, translated into dwim form. (meta_expr): Support @[...] variant. (yybadtoken): Handle METABKT in switch. * txr.1: Documented [...] syntax and dwim operator. * txr.vim: Updated.
-rw-r--r--ChangeLog31
-rw-r--r--eval.c149
-rw-r--r--eval.h2
-rw-r--r--lib.c78
-rw-r--r--parser.l10
-rw-r--r--parser.y14
-rw-r--r--txr.1108
-rw-r--r--txr.vim10
8 files changed, 360 insertions, 42 deletions
diff --git a/ChangeLog b/ChangeLog
index 677ca263..010c6676 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,34 @@
+2012-01-25 Kaz Kylheku <kaz@kylheku.com>
+
+ * eval.c (dwim_s): New symbol variable.
+ (dwim_loc, op_dwim): New static functions.
+ (op_modplace): Support assignment to dwim forms
+ with the help of dwim_loc.
+ (expand_place): Handle dwim places.
+ (eval_init): Initialize dwim_s. Register dwim operator
+ in op_table.
+
+ * eval.h (dwim_s): Declared.
+
+ * lib.c (chr_str, chr_str_set): Allow negative indices to index
+ backwards from end of string.
+ (vecref, vecref_l): Allow negative indices to index from
+ rear of array.
+ (obj_print, obj_pprint): Render (dwim ...) forms as [...].
+
+ * parser.l: Peoduce new METABKT token type for @[,
+ and '[', ']' tokens.
+
+ * parser.y (METABKT): New token. %type declaration for '['.
+ (list): Support square-bracket style of list, translated
+ into dwim form.
+ (meta_expr): Support @[...] variant.
+ (yybadtoken): Handle METABKT in switch.
+
+ * txr.1: Documented [...] syntax and dwim operator.
+
+ * txr.vim: Updated.
+
2012-01-21 Kaz Kylheku <kaz@kylheku.com>
Version 54
diff --git a/eval.c b/eval.c
index f2bc2b47..f8acd8aa 100644
--- a/eval.c
+++ b/eval.c
@@ -56,7 +56,7 @@ val top_vb, top_fb;
val op_table;
val eval_error_s;
-val progn_s, prog1_s, let_s, let_star_s, lambda_s, call_s;
+val dwim_s, progn_s, prog1_s, let_s, let_star_s, lambda_s, call_s;
val cond_s, if_s, defvar_s, defun_s;
val inc_s, dec_s, push_s, pop_s, flip_s, gethash_s, car_s, cdr_s, vecref_s;
val for_s, for_star_s, each_s, each_star_s, collect_each_s, collect_each_star_s;
@@ -637,6 +637,67 @@ static val op_defun(val form, val env)
return name;
}
+static val *dwim_loc(val form, val env)
+{
+ val obj = eval(second(form), env, form);
+ val args = eval_args(rest(rest(form)), env, form);
+
+ if (!obj)
+ eval_error(form, lit("[~s ]: cannot assign nil"), obj, nao);
+
+ switch (type(obj)) {
+ case LIT:
+ case STR:
+ case LSTR:
+ eval_error(form, lit("[~s ...]: string element assignment not implemented!"),
+ obj, nao);
+ case SYM:
+ case FUN:
+ eval_error(form, lit("[~s ...]: assigning through function not implemented!"),
+ obj, nao);
+ case VEC:
+ if (rest(args))
+ eval_error(form, lit("[~s ...]: vector indexing needs one arg"),
+ obj, nao);
+ return vecref_l(obj, first(args));
+ case CONS:
+ case LCONS:
+ if (rest(args))
+ eval_error(form, lit("[~s ...]: list indexing needs one arg"),
+ obj, nao);
+ {
+ val index = first(args);
+ val cell = obj;
+ if (!bignump(index) && !fixnump(index))
+ eval_error(form, lit("[~s ~s]: index must be integer"),
+ cell, index, nao);
+ for (; gt(index, zero); index = minus(index, one))
+ cell = cdr(cell);
+ if (lt(index, zero) || !cell)
+ eval_error(form, lit("[~s ~s]: cannot assign nonexistent location"),
+ cell, first(args), nao);
+ return car_l(cell);
+ }
+ case COBJ:
+ {
+ if (hashp(obj)) {
+ val new_p, *loc;
+ if (length(args) != two)
+ eval_error(form, lit("[~s ...]: hash indexing needs two args"),
+ obj, nao);
+ loc = gethash_l(obj, first(args), &new_p);
+ if (new_p)
+ *loc = second(args);
+ return loc;
+ }
+ }
+ default:
+ eval_error(form, lit("object ~s not supported by [] notation"), obj, nao);
+ }
+
+ return 0;
+}
+
static val op_modplace(val form, val env)
{
uses_or2;
@@ -665,7 +726,9 @@ static val op_modplace(val form, val env)
} else if (consp(place)) {
/* TODO: dispatch these with hash table. */
val sym = car(place);
- if (sym == gethash_s) {
+ if (sym == dwim_s) {
+ loc = dwim_loc(place, env);
+ } else if (sym == gethash_s) {
val hash = eval(second(place), env, form);
val key = eval(third(place), env, form);
val new_p;
@@ -675,7 +738,7 @@ static val op_modplace(val form, val env)
} else if (sym == car_s) {
val cons = eval(second(place), env, form);
loc = car_l(cons);
- } else if (sym == car_s) {
+ } else if (sym == cdr_s) {
val cons = eval(second(place), env, form);
loc = cdr_l(cons);
} else if (sym == vecref_s) {
@@ -813,6 +876,72 @@ static val op_return_from(val form, val env)
abort();
}
+static val op_dwim(val form, val env)
+{
+ val obj = eval(second(form), env, form);
+ val args = eval_args(rest(rest(form)), env, form);
+
+ if (!obj)
+ return nil;
+
+ switch (type(obj)) {
+ case LIT:
+ case STR:
+ case LSTR:
+ if (rest(args))
+ eval_error(form, lit("[~s ...]: string indexing needs one arg"),
+ obj, nao);
+ return chr_str(obj, first(args));
+ case SYM:
+ {
+ val fbinding = lookup_fun(env, obj);
+
+ if (!fbinding)
+ eval_error(form, lit("[~s ...]: no function exists named ~s"),
+ obj, obj, nao);
+
+ return apply(cdr(fbinding), args, form);
+ }
+ case FUN:
+ return apply(obj, args, form);
+ case VEC:
+ if (rest(args))
+ eval_error(form, lit("[~s ...]: vector indexing needs one arg"),
+ obj, nao);
+ return vecref(obj, first(args));
+ case CONS:
+ case LCONS:
+ if (rest(args))
+ eval_error(form, lit("[~s ...]: list indexing needs one arg"),
+ obj, nao);
+ {
+ val index = first(args);
+ if (!bignump(index) && !fixnump(index))
+ eval_error(form, lit("[~s ~s]: index must be integer"),
+ obj, index, nao);
+ if (lt(index, zero))
+ return nil;
+ for (; gt(index, zero); index = minus(index, one))
+ obj = cdr(obj);
+ return car(obj);
+ }
+ case COBJ:
+ {
+ if (hashp(obj)) {
+ if (length(args) != two)
+ eval_error(form, lit("[~s ...]: hash indexing needs two args"),
+ obj, nao);
+ return gethash_n(obj, first(args), second(args));
+ }
+ }
+ /* fallthrough */
+ default:
+ eval_error(form, lit("object ~s not supported by [] notation"), obj, nao);
+ }
+
+ return nil;
+}
+
static val subst_vars(val forms, val env)
{
list_collect_decl(out, iter);
@@ -914,7 +1043,15 @@ static val expand_place(val place)
return place;
} else {
val sym = first(place);
- if (sym == gethash_s) {
+ if (sym == dwim_s) {
+ val args = rest(place);
+ val args_ex = expand_forms(args);
+
+ if (args == args_ex)
+ return place;
+
+ return rlcp(cons(sym, args_ex), place);
+ } if (sym == gethash_s) {
val hash = second(place);
val key = third(place);
val dfl_val = fourth(place);
@@ -1195,7 +1332,7 @@ val expand(val form)
} else {
/* funtion call
also handles: progn, prog1, call, if, and, or,
- unwind-protect, return */
+ unwind-protect, return, dwim */
val args = rest(form);
val args_ex = expand_forms(args);
@@ -1477,6 +1614,7 @@ void eval_init(void)
top_vb = make_hash(t, nil, nil);
op_table = make_hash(nil, nil, nil);
+ dwim_s = intern(lit("dwim"), user_package);
progn_s = intern(lit("progn"), user_package);
prog1_s = intern(lit("prog1"), user_package);
let_s = intern(lit("let"), user_package);
@@ -1548,6 +1686,7 @@ void eval_init(void)
sethash(op_table, block_s, cptr((mem_t *) op_block));
sethash(op_table, return_s, cptr((mem_t *) op_return));
sethash(op_table, return_from_s, cptr((mem_t *) op_return_from));
+ sethash(op_table, dwim_s, cptr((mem_t *) op_dwim));
sethash(op_table, quasi_s, cptr((mem_t *) op_quasi_lit));
reg_fun(cons_s, func_n2(cons));
diff --git a/eval.h b/eval.h
index 90eca613..a2f7e177 100644
--- a/eval.h
+++ b/eval.h
@@ -24,6 +24,8 @@
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
+extern val dwim_s;
+
val make_env(val fbindings, val vbindings, val up_env);
val env_fbind(val env, val sym, val fun);
val env_vbind(val env, val sym, val obj);
diff --git a/lib.c b/lib.c
index a05ead7e..7496385b 100644
--- a/lib.c
+++ b/lib.c
@@ -1630,27 +1630,41 @@ val chr_num(val num)
return chr(n);
}
-val chr_str(val str, val index)
+val chr_str(val str, val ind)
{
- bug_unless (length_str_gt(str, index));
+ cnum index = c_num(ind);
+
+ if (index < 0) {
+ ind = plus(length_str(str), ind);
+ index = c_num(ind);
+ }
+
+ bug_unless (index >= 0 && length_str_gt(str, ind));
if (lazy_stringp(str)) {
- lazy_str_force_upto(str, index);
- return chr(c_str(str->ls.prefix)[c_num(index)]);
+ lazy_str_force_upto(str, ind);
+ return chr(c_str(str->ls.prefix)[index]);
} else {
- return chr(c_str(str)[c_num(index)]);
+ return chr(c_str(str)[index]);
}
}
-val chr_str_set(val str, val index, val chr)
+val chr_str_set(val str, val ind, val chr)
{
- bug_unless (length_str_gt(str, index));
+ cnum index = c_num(ind);
+
+ if (index < 0) {
+ ind = plus(length_str(str), ind);
+ index = c_num(ind);
+ }
+
+ bug_unless (index >= 0 && length_str_gt(str, ind));
if (lazy_stringp(str)) {
- lazy_str_force_upto(str, index);
- str->ls.prefix->st.str[c_num(index)] = c_chr(chr);
+ lazy_str_force_upto(str, ind);
+ str->ls.prefix->st.str[index] = c_chr(chr);
} else {
- str->st.str[c_num(index)] = c_chr(chr);
+ str->st.str[index] = c_chr(chr);
}
return chr;
@@ -2493,16 +2507,20 @@ val vec_set_length(val vec, val length)
val vecref(val vec, val ind)
{
- type_check(vec, VEC);
- range_bug_unless (c_num(ind) < c_num(vec->v.vec[vec_length]));
- return vec->v.vec[c_num(ind)];
+ cnum index = c_num(ind);
+ cnum len = c_num(length_vec(vec));
+ if (index < 0)
+ index = len + index;
+ range_bug_unless (index >= 0 && index < len);
+ return vec->v.vec[index];
}
val *vecref_l(val vec, val ind)
{
- type_check(vec, VEC);
- range_bug_unless (c_num(ind) < c_num(vec->v.vec[vec_length]));
- return vec->v.vec + c_num(ind);
+ cnum index = c_num(ind);
+ cnum len = c_num(length_vec(vec));
+ range_bug_unless (index >= 0 && index < len);
+ return vec->v.vec + index;
}
val vec_push(val vec, val item)
@@ -3385,17 +3403,25 @@ val obj_print(val obj, val out)
obj_print(second(obj), out);
} else {
val iter;
- put_char(out, chr('('));
+ val closepar = chr(')');
+ if (sym == dwim_s && consp(cdr(obj))) {
+ put_char(out, chr('['));
+ obj = cdr(obj);
+ closepar = chr(']');
+ } else {
+ put_char(out, chr('('));
+ }
+
for (iter = obj; consp(iter); iter = cdr(iter)) {
obj_print(car(iter), out);
if (nullp(cdr(iter))) {
- put_char(out, chr(')'));
+ put_char(out, closepar);
} else if (consp(cdr(iter))) {
put_char(out, chr(' '));
} else {
put_string(out, lit(" . "));
obj_print(cdr(iter), out);
- put_char(out, chr(')'));
+ put_char(out, closepar);
}
}
}
@@ -3524,17 +3550,25 @@ val obj_pprint(val obj, val out)
obj_pprint(second(obj), out);
} else {
val iter;
- put_char(out, chr('('));
+ val closepar = chr(')');
+ if (sym == dwim_s && consp(cdr(obj))) {
+ put_char(out, chr('['));
+ obj = cdr(obj);
+ closepar = chr(']');
+ } else {
+ put_char(out, chr('('));
+ }
+
for (iter = obj; consp(iter); iter = cdr(iter)) {
obj_pprint(car(iter), out);
if (nullp(cdr(iter))) {
- put_char(out, chr(')'));
+ put_char(out, closepar);
} else if (consp(cdr(iter))) {
put_char(out, chr(' '));
} else {
put_string(out, lit(" . "));
obj_pprint(cdr(iter), out);
- put_char(out, chr(')'));
+ put_char(out, closepar);
}
}
}
diff --git a/parser.l b/parser.l
index 59e7767e..7875f81b 100644
--- a/parser.l
+++ b/parser.l
@@ -358,12 +358,12 @@ UONLY {U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U}
return FINALLY;
}
-<NESTED>@\( |
-<SPECIAL,NESTED>\{|\( {
+<NESTED>@[\(\[] |
+<SPECIAL,NESTED>[{(\[] {
yy_push_state(NESTED);
if (yytext[0] == '@') {
- yylval.chr = '(';
- return METAPAR;
+ yylval.chr = yytext[1];
+ return yytext[1] == '(' ? METAPAR : METABKT;
}
yylval.lineno = lineno;
return yytext[0];
@@ -379,7 +379,7 @@ UONLY {U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U}
return yytext[0];
}
-<SPECIAL,NESTED>\}|\) {
+<SPECIAL,NESTED>[})\]] {
yy_pop_state();
if (yy_top_state() == INITIAL
|| yy_top_state() == QSILIT)
diff --git a/parser.y b/parser.y
index 21571920..8a3e8bbb 100644
--- a/parser.y
+++ b/parser.y
@@ -77,7 +77,7 @@ static val parsed_spec;
%token <val> NUMBER
%token <chr> REGCHAR LITCHAR
-%token <chr> METAPAR SPLICE
+%token <chr> METAPAR METABKT SPLICE
%type <val> spec clauses clauses_opt clause
%type <val> all_clause some_clause none_clause maybe_clause
@@ -93,7 +93,7 @@ static val parsed_spec;
%type <val> regterm regclass regclassterm regrange
%type <val> strlit chrlit quasilit quasi_items quasi_item litchars
%type <chr> regchar
-%type <lineno> '('
+%type <lineno> '(' '['
%nonassoc LOW /* used for precedence assertion */
%right IDENT '{' '}'
@@ -656,6 +656,8 @@ vector : '#' list { $$ = rlcp(vector_list($2), $2); }
list : '(' exprs ')' { $$ = rl($2, num($1)); }
| '(' ')' { $$ = nil; }
+ | '[' exprs ']' { $$ = rl(cons(dwim_s, $2), num($1)); }
+ | '[' ']' { $$ = rl(cons(dwim_s, nil), num($1)); }
| ',' expr { val expr = $2;
if (consp(expr) && first(expr) == qquote_s)
expr = cons(quote_s, rest(expr));
@@ -668,10 +670,17 @@ list : '(' exprs ')' { $$ = rl($2, num($1)); }
$$ = rlcp(list(splice_s, expr, nao), $2); }
| '(' error { $$ = nil;
yybadtoken(yychar, lit("list expression")); }
+ | '[' error { $$ = nil;
+ yybadtoken(yychar, lit("DWIM expression")); }
;
meta_expr : METAPAR exprs ')' { $$ = rlcp(cons(expr_s, expand($2)), $2); }
+ | METABKT exprs ']' { $$ = rlcp(cons(expr_s,
+ cons(dwim_s,
+ expand($2))), $2); }
| METAPAR ')' { $$ = rl(cons(expr_s, nil), num(lineno)); }
+ | METABKT ']' { $$ = rl(cons(expr_s, cons(dwim_s, nil)),
+ num(lineno)); }
| METAPAR error { $$ = nil;
yybadtoken(yychar, lit("meta expression")); }
;
@@ -1054,6 +1063,7 @@ void yybadtoken(int tok, val context)
case REGCHAR: problem = lit("regular expression character"); break;
case LITCHAR: problem = lit("string literal character"); break;
case METAPAR: problem = lit("@("); break;
+ case METABKT: problem = lit("@["); break;
}
if (problem != 0)
diff --git a/txr.1 b/txr.1
index 6176e5d3..ff753b36 100644
--- a/txr.1
+++ b/txr.1
@@ -4273,10 +4273,10 @@ Define several Lisp functions using @(do):
TXR Lisp is a small and simple dialect, like Scheme, but much more similar to
Common Lisp than Scheme. It has separate value and function binding namespaces,
-like Common Lisp, and represents boolean true and false with the symbols t and
-nil (but note the case sensitivity of identifiers denoting symbols!)
-Furthermore, the symbol nil is also the empty list, which terminates nonempty
-lists.
+like Common Lisp (and thus is a Lisp-2 type dialect), and represents boolean
+true and false with the symbols t and nil (but note the case sensitivity of
+identifiers denoting symbols!) Furthermore, the symbol nil is also the empty
+list, which terminates nonempty lists.
Function and variable Bindings are dynamically scoped in TXR Lisp. However,
closures do capture variables.
@@ -4363,6 +4363,21 @@ 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.
+.SS The DWIM Brackets
+
+.IP [...]
+
+TXR Lisp has a square bracket notation. The syntax [...] is a shorthand
+way of writing (dwim ...). The [] syntax is useful for situations
+where the expressive style of a Lisp-1 dialect is useful.
+
+For instance if foo is a variable which holds a function object, then [foo 3]
+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.
+
+More details are given in the documentation for the dwim operator.
+
.SS Lisp Operators
When the first element of a compound expression is an operator symbol,
@@ -4753,6 +4768,11 @@ Currently, these forms are recognized as places:
(vecref <vector> <index>)
+ (dwim <obj> ...)
+
+ [<obj> ...] ;; equivalent to (dwim <obj> ...)
+
+
A <symbol> place denotes a variable. If the variable does not exist, it is an
error.
@@ -4769,6 +4789,86 @@ determine the initial value of the place. Otherwise it is ignored.
The vecref place denotes a vector element, allowing vector elements
to be treated as assignment places.
+The dwim/[] place denotes a vector element, list element, or hash table,
+depending on the type of obj.
+
+.SS Operator dwim
+
+.TP
+Syntax:
+
+ (dwim <argument>*)
+
+ [<argument>*]
+
+.TP
+Description:
+
+The dwim operator's name is an acronym: DWIM may be taken to mean
+"Do What I Mean", or alternatively, "Dispatch, in a Way that is
+Intelligent and Meaningful".
+
+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:
+
+.IP [<function> <argument>*]
+Call the given the function object to the given arguments.
+
+.IP [<symbol> <argument>*]
+If the first expression evaluates to a symbol, that symbol
+is resolved in the function namespace, and then
+the resulting function, if found, is called with the
+given arguments.
+
+.IP [<list> <index>]
+Retrieve the specified element from the specified list. Index zero
+refers to the first element. Indexed list access does not throw exceptions.
+Negative indices yield nil, and indices beyond the end of a list
+yield nil. (However assignment to a nonexistent list element throws.)
+
+.IP [<vector> <index>]
+Retrieve the specified element of a vector. This is equivalent to
+(vecref <vector> <index>).
+
+.IP [<string> <index>]
+Retrieve the specified element of a string. This is equivalent to
+(chr-str <string> <index>).
+
+.IP [<hash-table> <key> <default-value>]
+Retrieve a value from the hash table corresponding to <key>,
+or <default-value> if there is no such entry.
+
+The list, vector and hash table forms of dwim denote places
+that can be assigned.
+
+.TP
+Notes:
+
+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
+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.
+
+Lisp-1 dialects can 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.
+
.SS Operators for and for*
.TP
diff --git a/txr.vim b/txr.vim
index ae777790..c7ace6a5 100644
--- a/txr.vim
+++ b/txr.vim
@@ -101,13 +101,15 @@ syn match txr_ncomment ";.*" contained
syn match txr_ident "[a-zA-Z0-9!$%&*+\-<=>?\\^_~]\+" contained
syn match txr_num "[+-]\?[0-9]\+" contained
-syn region txr_bracevar matchgroup=Delimiter start="@[ \t]*[*]\?{" matchgroup=Delimiter end="}" contains=txr_num,txr_ident,xr_string,txr_list,txr_regex,txr_quasilit,txr_chr
+syn region txr_bracevar matchgroup=Delimiter start="@[ \t]*[*]\?{" matchgroup=Delimiter end="}" contains=txr_num,txr_ident,txr_string,txr_dwim,txr_list,txr_regex,txr_quasilit,txr_chr
-syn region txr_directive matchgroup=Delimiter start="@[ \t]*(" matchgroup=Delimiter end=")" contains=txr_keyword,txr_string,txr_list,txr_meta,txr_quasilit,txr_num,txr_ident,txr_regex,txr_string,txr_variable,txr_chr,txr_hash,txr_ncomment
+syn region txr_directive matchgroup=Delimiter start="@[ \t]*(" matchgroup=Delimiter end=")" contains=txr_keyword,txr_string,txr_list,txr_dwim,txr_meta,txr_quasilit,txr_num,txr_ident,txr_regex,txr_string,txr_variable,txr_chr,txr_hash,txr_ncomment
-syn region txr_list contained matchgroup=Delimiter start="(" matchgroup=Delimiter end=")" contains=txl_keyword,txr_string,txr_regex,txr_num,txr_ident,txr_variable,txr_meta,txr_list,txr_quasilit,txr_chr,txr_hash,txr_quote,txr_ncomment
+syn region txr_list contained matchgroup=Delimiter start="(" matchgroup=Delimiter end=")" contains=txl_keyword,txr_string,txr_regex,txr_num,txr_ident,txr_variable,txr_meta,txr_list,txr_dwim,txr_quasilit,txr_chr,txr_hash,txr_quote,txr_ncomment
-syn region txr_meta contained matchgroup=Delimiter start="@[ \t]*(" matchgroup=Delimiter end=")" contains=txl_keyword,txr_string,txr_list,txr_regex,txr_num,txr_ident,txr_variable,txr_quasilit,txr_chrb,txr_hash,txr_quote,txr_ncomment
+syn region txr_dwim contained matchgroup=Delimiter start="\[" matchgroup=Delimiter end="\]" contains=txl_keyword,txr_string,txr_regex,txr_num,txr_ident,txr_variable,txr_meta,txr_list,txr_dwim,txr_dwim,txr_quasilit,txr_chr,txr_hash,txr_quote,txr_ncomment
+
+syn region txr_meta contained matchgroup=Delimiter start="@[ \t]*(" matchgroup=Delimiter end=")" contains=txl_keyword,txr_string,txr_list,txr_dwim,txr_regex,txr_num,txr_ident,txr_variable,txr_quasilit,txr_chrb,txr_hash,txr_quote,txr_ncomment
syn region txr_string contained oneline start=+"+ skip=+\\\\\|\\"+ end=+"+
syn region txr_quasilit contained oneline start=+`+ skip=+\\\\\|\\`+ end=+`+ contains=txr_directive,txr_variable,txr_bracevar