The Wayback Machine - https://web.archive.org/web/20210518114103/http://lua-users.org/lists/lua-l//2013-02/msg00733.html
[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: parser hacking: conditional fields
- From: Sven Olsen <sven2718@...>
- Date: Thu, 28 Feb 2013 15:47:43 -0800
Hey list,
I've been playing around with another parser hack, and it's starting to seem like it may be worth a list post.�
Like most Lua programmers, I'm in the habit of�using 'or' to specify a list of possible fallback values. �For example:
� �color = object.icon.color or�object.color or white
Sometimes I'll want to check for fallback values stored inside tables that may themselves be undefined. �If not all�objects�have icons, the example above gets a bit more complex, becoming something like:
� �color = (object.icon�and object.icon.color) or object.color or white�
And if the hierarchy of optional subtables�is several levels deep, then properly defining color becomes a real chore. �(For example, consider writing a function that has color default to object.icon.glow.color, but doesn't crash if any of the referenced tables are missing.)
Thus my latest�excursion�into lparser.c. �The idea is to allow ~ to denote a "conditional field reference". �When evaluating 'table~key', the result is 'table.key' provided that 'table' exists. But if 'table' doesn't exist, then 'table~key' evaluates to 'table'. �I fall back on the nonexistent case when 'table' equals nil or false, so the semantics match the behavior of more standard 'and/or' tricks.
By chaining ~, I can write statements like the following, which are safe even when 'object' or any of it's subtables have a chance of being nil:
� color = object~icon~glow~color or white
Extending the sugar so that table~[key] becomes a conditional version of table[key] is easy enough to do, and makes the hack useful in a wider range of situations.
Like the "stringification" hack, my current feeling is that this is a piece of sugar that's useful enough to justify its obscurity -- though I'm sure some members of the list will consider it a lollipop�:) �
Once again, I'll ask if any of the more�knowledgeable�members of the list know what this kind of shorthand ought to be called. �"Conditional fields" is the best name I can come up with, but, if there's a standard term out there, it would be good to know it.
-Sven
PS: �For those of you who enjoy messing around with parser hacks, the entry point's a new case in lparser:primaryexp()
� � switch (ls->t.token) {
� � case '~':�
� � � � luaX_next(ls);
� � � � conditional_field(ls,v);
� � � � break;
The implementation is messier than I'd like. �I'd hoped to simply piggyback on OP_AND, but couldn't figure out how to do so without requiring multiple evaluations of the table _expression_. �The implementation I ended up with takes a lower level approach. �It seems to be pretty safe -- though I haven't made a close enough study of Lua's bytecode generation to be entirely confident in it. �I'm also doing some stack�maintenance�that's probably unnecessarily�paranoid.
static void conditional_field(LexState *ls, expdesc *v) {
� � FuncState *fs = ls->fs;
� � luaK_exp2nextreg(fs, v);
� � luaK_codeABC(fs,OP_TEST, v->
u.info, NO_REG, 0 );
� � {
� � int old_free=fs->freereg; � � � � � ��
� � int j = luaK_codeAsBx(fs, OP_JMP, 0, NO_JUMP);
� � expdesc key;
� � switch(ls->t.token) {
� � case '[':
� � � � yindex(ls, &key);
� � � � luaK_indexed(fs, v, &key);
� � � � luaK_exp2nextreg(fs, v);
� � � � break; � � � �
� � default:
� � � � checkname(ls, &key);
� � � � luaK_indexed(fs, v, &key);
� � }
� � luaK_exp2nextreg(fs, v);
� � fs->freereg=old_free;
� � /* i think this next check is unnecessary, as any complex key
� � � expressions should be courteous enough to leave the top of
� � � the stack where they found it. */
� � � � luaK_codeABC(fs,OP_MOVE, vreg, v->
u.info, 0 );
� � }
� � SETARG_sBx(fs->f->code[j], fs->pc-j-1);
� � }
}