lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]

On November 4, 2018 at 9:45:16 AM, Philippe Verdy ( wrote:

I propose to extend te syntax of table constructors like this:

{                                           -- starts a new local context (closure)
 [:      ref0 = 'k1'] = :      ref1 = v1,   -- ['k1'] = v1   and set external ref0 = 'k1'
 [:      ref2]        =               v2,   -- [1]    = v2   and set local    ref2 = 1
 [:local ref3]        =               v3,   -- [2]    = v3   and set local    ref3 = 2
 [:local ref4 = 'k2'] = :local ref5 = v4,   -- ['k2'] = v4   and set local    ref4 = 'k2', local    ref5 = v4
 [:local ref6 = 'k3'] = :      ref7 = v5,   -- ['k3'] = v5   and set local    ref6 = 'k2', external ref7 = v5
                 k4   =               ref0, -- ['k4'] = 'k1'
 [              'k5'] =               ref2, -- ['k5'] = v2
                                      ref1, -- [3]    = v1
}                                           -- close the local context

The idea is to allow constructors to use cyclic references by allowing to define "tracking" variables keeping the key (eventually the implicit numeric key) and the value assigned to a key; these variables can be defined in local scope (the scope is a closure, the local context of the table constructor itself) or in external scope (any enclosing scope, eventually outside any table constructor, or in another enclosing table constructor).

The syntax for keys is not changed, it still uses the [] to enclose an _expression_.

However the _expression_ allowed in a key (between []) or in a value (after the = if there's a key specified before) is extented by allowing one of these form to set variables:
 :local variable, (only for key expressions), or
 :local variable = _expression_, or
 :variable, (only for key expressions), or
 :variable = _expression_, or
 _expression_, or

* The first two forms define the variable in the current local scope (it creates a new variable in the local context, like with function closures, or local contexts of enuneration variables in "for", hiding any external variable defined with the same name).
* The last two forms can use any lvalue _expression_: if the external context does not have this variable defined, it will create the variable in the external context, otherwise it will replace its current value.

This syntax extension is simple to parse (no complex look-ahead or backtracking), it just starts with a leading ":" before the local or external variable, the '=' sign (optional if there's no _expression_ and permitted only in the key part, required in the value part) and the _expression_ (optional only in the key part because it implies an implicit numeric key)

You may prefer to change the syntax distinguishing the assignment of local and external variables (":local variable" versus ":variable"). For example:
- ":variable" for the a local variable (must be a "name" or "[_expression_]"), or
- "?variable" for setting an external variable (which can be any lvalue _expression_)

In all cases, the locality is limited to the scope of the table constructor, it is permitted to reassign the same variable in the same local or external scope, like in standard Lua assignment instructions, or in standard declarations of local variables in functional blocks.

No limit is set on the types of values that these variables can hold (only the nil value is invalid as the key of a table entry, but not invalid as the value of a table entry).

This allows to entirely serialize any table (including cyclic/recursive ones) in pure data-only format (the data-only format is independant of the context where it is used if all variables set inside the main table constructor are defined locally in the main table constructor (but external, non-local, variables can still be used in a sub-table).

It is even possible to serialize tables containing code (i.e. function blocks), with their defined lamda expressions, possibly even coroutines (only functions that are defined as external C-functions cannot be serialized with their code, they can only be referenced by their binding name; C functions and coroutines are most probably excluded from pure data-only table constructors, but lambda functions may be permitted if the code inside them does not any use external references to variables not in local scope of the main serialized table and does not use external C-functions or some restricted functions of the Lua library; some functions of the Lua library may still be allowed, including pcall(), that can locally handle exceptions)

I think you can use ltokenp to implement this / try it out.