mirror of
https://github.com/Reuh/candran.git
synced 2025-10-27 09:59:29 +00:00
Add destructuring assignement
This commit is contained in:
parent
851e9f89d6
commit
842536b561
10 changed files with 3823 additions and 2985 deletions
33
README.md
33
README.md
|
|
@ -42,6 +42,8 @@ end)
|
|||
|
||||
a.child?:method?() -- safe navigation operator
|
||||
|
||||
local {hey, method} = a -- destructuring assignement
|
||||
|
||||
local odd = [ -- table comprehension
|
||||
for i=1, 10 do
|
||||
if i%2 == 0 then
|
||||
|
|
@ -70,11 +72,11 @@ end
|
|||
Candran is released under the MIT License (see ```LICENSE``` for details).
|
||||
|
||||
#### Quick setup
|
||||
Install Candran automatically using LuaRocks: ```sudo luarocks install rockspec/candran-0.10.0-1.rockspec```.
|
||||
Install Candran automatically using LuaRocks: ```sudo luarocks install rockspec/candran-0.11.0-1.rockspec```.
|
||||
|
||||
Or manually install LPegLabel (```luarocks install lpeglabel```, version 1.5 or above), download this repository and use Candran through the scripts in ```bin/``` or use it as a library with the self-contained ```candran.lua```.
|
||||
|
||||
You can optionally install lua-linenoise (```luarocks install linenoise```) for an improved REPL. The rockspec does not install linenoise by default.
|
||||
You can optionally install lua-linenoise (```luarocks install linenoise```, version 0.9 or above) for an improved REPL. The rockspec will install linenoise by default.
|
||||
|
||||
You can register the Candran package searcher in your main Lua file (`require("candran").setup()`) and any subsequent `require` call in your project will automatically search for Candran modules.
|
||||
|
||||
|
|
@ -245,6 +247,33 @@ Values returned by the function will be inserted in the generated table in the o
|
|||
|
||||
The table generation function also have access to the `self` variable (and its alias `@`), which is the table which is being created, so you can set any of the table's field.
|
||||
|
||||
##### Destructuring assignement
|
||||
```lua
|
||||
t = { x = 1, y = 2, z = 3 }
|
||||
|
||||
{x, y, z} = t -- x, y, z = t.x, t.y, t.z
|
||||
|
||||
{x = o} = t -- o = t.x
|
||||
|
||||
{["x"] = o} = t -- o = t["x"]
|
||||
|
||||
-- Also works with local, let, for ... in, if with assignement, +=, etc.
|
||||
local {x, y} = t
|
||||
let {x, y} = t
|
||||
for i, {x, y} in ipairs{t} do end
|
||||
if {x, y} = t then end
|
||||
{x} += t -- x = x + t.x
|
||||
|
||||
-- Works as expected with multiple assignement.
|
||||
a, {x, y, z}, b = 1, t, 2
|
||||
|
||||
```
|
||||
|
||||
Destruturing assignement allows to quickly extract fields from a table into a variable.
|
||||
|
||||
This is done by replacing the variable name in any assignement with a table literal, where every item is the name of the field and assigned variable. It is possible to use a different field name than the variable name by naming the table item (`fieldName = var` or `[fieldExpression] = var`).
|
||||
|
||||
|
||||
##### Safe navigation operators
|
||||
```lua
|
||||
a = nil
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
#import("lib.lua-parser.parser")
|
||||
|
||||
local candran = {
|
||||
VERSION = "0.10.0"
|
||||
VERSION = "0.11.0"
|
||||
}
|
||||
|
||||
--- Default options.
|
||||
|
|
|
|||
5562
candran.lua
5562
candran.lua
File diff suppressed because it is too large
Load diff
|
|
@ -14,7 +14,7 @@ return function(code, ast, options)
|
|||
local r = options.newline..string.rep(options.indentation, indentLevel)
|
||||
if options.mapLines then
|
||||
local sub = code:sub(lastInputPos)
|
||||
local source, line = sub:sub(1, sub:find("\n")):match("%-%- (.-)%:(%d+)\n")
|
||||
local source, line = sub:sub(1, sub:find("\n")):match(".*%-%- (.-)%:(%d+)\n")
|
||||
|
||||
if source and line then
|
||||
lastSource = source
|
||||
|
|
@ -42,15 +42,31 @@ return function(code, ast, options)
|
|||
return newline()
|
||||
end
|
||||
|
||||
--- Module management
|
||||
local required = {} -- { ["module"] = true, ... }
|
||||
local requireStr = ""
|
||||
-- Add the module "mod" to the list of modules to require, and load its field "field" (or the whole module if nil) into the variable "name".
|
||||
local function addRequire(mod, name, field)
|
||||
if not required[mod] then
|
||||
requireStr ..= "local "..options.variablePrefix..name..(" = require(%q)"):format(mod)..(field and "."..field or "")..options.newline
|
||||
required[mod] = true
|
||||
--- State stacks
|
||||
-- Used for context-sensitive syntax.
|
||||
local states = {
|
||||
push = {}, -- push stack variable names
|
||||
destructuring = {}, -- list of variable that need to be assigned from a destructure {id = "parent variable", "field1", "field2"...}
|
||||
scope = {} -- list of variables defined in the current scope
|
||||
}
|
||||
-- Push a new value on top of the stack "name". Returns an empty string for chaining.
|
||||
local function push(name, state)
|
||||
table.insert(states[name], state)
|
||||
return ""
|
||||
end
|
||||
-- Remove the value on top of the stack "name". Returns an empty string for chaining.
|
||||
local function pop(name)
|
||||
table.remove(states[name])
|
||||
return ""
|
||||
end
|
||||
-- Set the value on top of the stack "name". Returns an empty string for chaining.
|
||||
local function set(name, state)
|
||||
states[name][#states[name]] = state
|
||||
return ""
|
||||
end
|
||||
-- Returns the value on top of the stack "name".
|
||||
local function peek(name)
|
||||
return states[name][#states[name]]
|
||||
end
|
||||
|
||||
--- Variable management
|
||||
|
|
@ -59,6 +75,26 @@ return function(code, ast, options)
|
|||
return options.variablePrefix..name
|
||||
end
|
||||
|
||||
-- Returns the prefixed temporary variable name.
|
||||
local function tmp()
|
||||
local scope = peek("scope")
|
||||
local var = "%s_%s":format(options.variablePrefix, #scope)
|
||||
table.insert(scope, var)
|
||||
return var
|
||||
end
|
||||
|
||||
--- Module management
|
||||
local required = {} -- { ["full require expression"] = true, ... }
|
||||
local requireStr = ""
|
||||
-- Add the module "mod" to the list of modules to require, and load its field "field" (or the whole module if nil) into the variable "name".
|
||||
local function addRequire(mod, name, field)
|
||||
local req = "require(%q)%s":format(mod, field and "."..field or "")
|
||||
if not required[req] then
|
||||
requireStr ..= "local %s = %s%s":format(var(name), req, options.newline)
|
||||
required[req] = true
|
||||
end
|
||||
end
|
||||
|
||||
--- AST traversal helpers
|
||||
local loop = { "While", "Repeat", "Fornum", "Forin", "WhileExpr", "RepeatExpr", "FornumExpr", "ForinExpr" } -- loops tags (can contain continue)
|
||||
local func = { "Function", "TableCompr", "DoExpr", "WhileExpr", "RepeatExpr", "IfExpr", "FornumExpr", "ForinExpr" } -- function scope tags (can contain push)
|
||||
|
|
@ -132,26 +168,6 @@ return function(code, ast, options)
|
|||
return true
|
||||
end
|
||||
|
||||
--- State stacks
|
||||
-- Used for context-sensitive syntax.
|
||||
local states = {
|
||||
push = {} -- push stack variable names
|
||||
}
|
||||
-- Push a new value on top of the stack "name". Returns an empty string for chaining.
|
||||
local function push(name, state)
|
||||
table.insert(states[name], state)
|
||||
return ""
|
||||
end
|
||||
-- Remove the value on top of the stack "name". Returns an empty string for chaining.
|
||||
local function pop(name)
|
||||
table.remove(states[name])
|
||||
return ""
|
||||
end
|
||||
-- Returns the value on top of the stack "name".
|
||||
local function peek(name)
|
||||
return states[name][#states[name]]
|
||||
end
|
||||
|
||||
--- Lua compiler
|
||||
local tags
|
||||
-- Recursively returns the compiled AST Lua code, set "forceTag" to override the tag type and pass additional arguments to the tag constructor if needed.
|
||||
|
|
@ -175,6 +191,43 @@ return function(code, ast, options)
|
|||
local CONTINUE_STOP = () -- at the start of loops using continue
|
||||
return unindent().."end"..newline().."::"..var"continue".."::"
|
||||
end
|
||||
local DESTRUCTURING_ASSIGN = (destructured, newlineAfter=false, noLocal=false) -- to define values from a destructuring assignement
|
||||
local vars = {}
|
||||
local values = {}
|
||||
for _, list in ipairs(destructured) do
|
||||
for _, v in ipairs(list) do
|
||||
local var, val
|
||||
if v.tag == "Id" then
|
||||
var = v
|
||||
val = { tag = "Index", { tag = "Id", list.id }, { tag = "String", v[1] } }
|
||||
elseif v.tag == "Pair" then
|
||||
var = v[2]
|
||||
val = { tag = "Index", { tag = "Id", list.id }, v[1] }
|
||||
else
|
||||
error("unknown destructuring element type: "..tostring(v.tag))
|
||||
end
|
||||
if destructured.rightOp and destructured.leftOp then
|
||||
val = { tag = "Op", destructured.rightOp, var, { tag = "Op", destructured.leftOp, val, var } }
|
||||
elseif destructured.rightOp then
|
||||
val = { tag = "Op", destructured.rightOp, var, val }
|
||||
elseif destructured.leftOp then
|
||||
val = { tag = "Op", destructured.leftOp, val, var }
|
||||
end
|
||||
table.insert(vars, lua(var))
|
||||
table.insert(values, lua(val))
|
||||
end
|
||||
end
|
||||
if #vars > 0 then
|
||||
local decl = noLocal and "" or "local "
|
||||
if newlineAfter then
|
||||
return decl..table.concat(vars, ", ").." = "..table.concat(values, ", ")..newline()
|
||||
else
|
||||
return newline()..decl..table.concat(vars, ", ").." = "..table.concat(values, ", ")
|
||||
end
|
||||
else
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
--- Tag constructors
|
||||
tags = setmetatable({
|
||||
|
|
@ -185,7 +238,7 @@ return function(code, ast, options)
|
|||
hasPush.tag = "Return"
|
||||
hasPush = false
|
||||
end
|
||||
local r = ""
|
||||
local r = push("scope", {})
|
||||
if hasPush then
|
||||
r ..= push("push", var"push").."local "..var"push".." = {}"..newline()
|
||||
end
|
||||
|
|
@ -198,7 +251,7 @@ return function(code, ast, options)
|
|||
if hasPush and (t[#t] and t[#t].tag ~= "Return") then -- add return only if needed
|
||||
r ..= newline().."return "..UNPACK(var"push")..pop("push")
|
||||
end
|
||||
return r
|
||||
return r..pop("scope")
|
||||
end,
|
||||
|
||||
-- stat --
|
||||
|
|
@ -209,28 +262,73 @@ return function(code, ast, options)
|
|||
end,
|
||||
-- Set{ {lhs+} (opid? = opid?)? {expr+} }
|
||||
Set = (t)
|
||||
if #t == 2 then
|
||||
return lua(t[1], "_lhs").." = "..lua(t[2], "_lhs")
|
||||
elseif #t == 3 then
|
||||
return lua(t[1], "_lhs").." = "..lua(t[3], "_lhs")
|
||||
-- extract vars and values
|
||||
local expr = t[#t]
|
||||
local vars, values = {}, {}
|
||||
local destructuringVars, destructuringValues = {}, {}
|
||||
for i, n in ipairs(t[1]) do
|
||||
if n.tag == "DestructuringId" then
|
||||
table.insert(destructuringVars, n)
|
||||
table.insert(destructuringValues, expr[i])
|
||||
else
|
||||
table.insert(vars, n)
|
||||
table.insert(values, expr[i])
|
||||
end
|
||||
end
|
||||
--
|
||||
if #t == 2 or #t == 3 then
|
||||
local r = ""
|
||||
if #vars > 0 then
|
||||
r = lua(vars, "_lhs").." = "..lua(values, "_lhs")
|
||||
end
|
||||
if #destructuringVars > 0 then
|
||||
local destructured = {}
|
||||
r ..= "local "..push("destructuring", destructured)..lua(destructuringVars, "_lhs")..pop("destructuring").." = "..lua(destructuringValues, "_lhs")
|
||||
return r..DESTRUCTURING_ASSIGN(destructured, nil, true)
|
||||
end
|
||||
return r
|
||||
elseif #t == 4 then
|
||||
if t[3] == "=" then
|
||||
local r = lua(t[1], "_lhs").." = "..lua({ t[2], t[1][1], { tag = "Paren", t[4][1] } }, "Op")
|
||||
for i=2, math.min(#t[4], #t[1]), 1 do
|
||||
r ..= ", "..lua({ t[2], t[1][i], { tag = "Paren", t[4][i] } }, "Op")
|
||||
local r = ""
|
||||
if #vars > 0 then
|
||||
r ..= lua(vars, "_lhs").." = "..lua({ t[2], vars[1], { tag = "Paren", values[1] } }, "Op")
|
||||
for i=2, math.min(#t[4], #vars), 1 do
|
||||
r ..= ", "..lua({ t[2], vars[i], { tag = "Paren", values[i] } }, "Op")
|
||||
end
|
||||
end
|
||||
if #destructuringVars > 0 then
|
||||
local destructured = { rightOp = t[2] }
|
||||
r ..= "local "..push("destructuring", destructured)..lua(destructuringVars, "_lhs")..pop("destructuring").." = "..lua(destructuringValues, "_lhs")
|
||||
return r..DESTRUCTURING_ASSIGN(destructured, nil, true)
|
||||
end
|
||||
return r
|
||||
else
|
||||
local r = lua(t[1], "_lhs").." = "..lua({ t[3], { tag = "Paren", t[4][1] }, t[1][1] }, "Op")
|
||||
local r = ""
|
||||
if #vars > 0 then
|
||||
r ..= lua(vars, "_lhs").." = "..lua({ t[3], { tag = "Paren", values[1] }, vars[1] }, "Op")
|
||||
for i=2, math.min(#t[4], #t[1]), 1 do
|
||||
r ..= ", "..lua({ t[3], { tag = "Paren", t[4][i] }, t[1][i] }, "Op")
|
||||
r ..= ", "..lua({ t[3], { tag = "Paren", values[i] }, vars[i] }, "Op")
|
||||
end
|
||||
end
|
||||
if #destructuringVars > 0 then
|
||||
local destructured = { leftOp = t[3] }
|
||||
r ..= "local "..push("destructuring", destructured)..lua(destructuringVars, "_lhs")..pop("destructuring").." = "..lua(destructuringValues, "_lhs")
|
||||
return r..DESTRUCTURING_ASSIGN(destructured, nil, true)
|
||||
end
|
||||
return r
|
||||
end
|
||||
else -- You are mad.
|
||||
local r = lua(t[1], "_lhs").." = "..lua({ t[2], t[1][1], { tag = "Op", t[4], { tag = "Paren", t[5][1] }, t[1][1] } }, "Op")
|
||||
local r = ""
|
||||
if #vars > 0 then
|
||||
r ..= lua(vars, "_lhs").." = "..lua({ t[2], vars[1], { tag = "Op", t[4], { tag = "Paren", values[1] }, vars[1] } }, "Op")
|
||||
for i=2, math.min(#t[5], #t[1]), 1 do
|
||||
r ..= ", "..lua({ t[2], t[1][i], { tag = "Op", t[4], { tag = "Paren", t[5][i] }, t[1][i] } }, "Op")
|
||||
r ..= ", "..lua({ t[2], vars[i], { tag = "Op", t[4], { tag = "Paren", values[i] }, vars[i] } }, "Op")
|
||||
end
|
||||
end
|
||||
if #destructuringVars > 0 then
|
||||
local destructured = { rightOp = t[2], leftOp = t[4] }
|
||||
r ..= "local "..push("destructuring", destructured)..lua(destructuringVars, "_lhs")..pop("destructuring").." = "..lua(destructuringValues, "_lhs")
|
||||
return r..DESTRUCTURING_ASSIGN(destructured, nil, true)
|
||||
end
|
||||
return r
|
||||
end
|
||||
|
|
@ -344,12 +442,13 @@ return function(code, ast, options)
|
|||
end,
|
||||
-- Forin{ {ident+} {expr+} block }
|
||||
Forin = (t)
|
||||
local destructured = {}
|
||||
local hasContinue = any(t[3], { "Continue" }, loop)
|
||||
local r = "for "..lua(t[1], "_lhs").." in "..lua(t[2], "_lhs").." do"..indent()
|
||||
local r = "for "..push("destructuring", destructured)..lua(t[1], "_lhs")..pop("destructuring").." in "..lua(t[2], "_lhs").." do"..indent()
|
||||
if hasContinue then
|
||||
r ..= CONTINUE_START()
|
||||
end
|
||||
r ..= lua(t[3])
|
||||
r ..= DESTRUCTURING_ASSIGN(destructured, true)..lua(t[3])
|
||||
if hasContinue then
|
||||
r ..= CONTINUE_STOP()
|
||||
end
|
||||
|
|
@ -357,15 +456,17 @@ return function(code, ast, options)
|
|||
end,
|
||||
-- Local{ {ident+} {expr+}? }
|
||||
Local = (t)
|
||||
local r = "local "..lua(t[1], "_lhs")
|
||||
local destructured = {}
|
||||
local r = "local "..push("destructuring", destructured)..lua(t[1], "_lhs")..pop("destructuring")
|
||||
if t[2][1] then
|
||||
r ..= " = "..lua(t[2], "_lhs")
|
||||
end
|
||||
return r
|
||||
return r..DESTRUCTURING_ASSIGN(destructured)
|
||||
end,
|
||||
-- Let{ {ident+} {expr+}? }
|
||||
Let = (t)
|
||||
local nameList = lua(t[1], "_lhs")
|
||||
local destructured = {}
|
||||
local nameList = push("destructuring", destructured)..lua(t[1], "_lhs")..pop("destructuring")
|
||||
local r = "local "..nameList
|
||||
if t[2][1] then
|
||||
if all(t[2], { "Nil", "Dots", "Boolean", "Number", "String" }) then -- predeclaration doesn't matter here
|
||||
|
|
@ -374,7 +475,7 @@ return function(code, ast, options)
|
|||
r ..= newline()..nameList.." = "..lua(t[2], "_lhs")
|
||||
end
|
||||
end
|
||||
return r
|
||||
return r..DESTRUCTURING_ASSIGN(destructured)
|
||||
end,
|
||||
-- Localrec{ {ident} {expr} }
|
||||
Localrec = (t)
|
||||
|
|
@ -658,6 +759,21 @@ return function(code, ast, options)
|
|||
Id = (t)
|
||||
return t[1]
|
||||
end,
|
||||
-- DestructuringId{ Id | Pair+ }
|
||||
DestructuringId = (t)
|
||||
if t.id then -- destructing already done before, use parent variable as id
|
||||
return t.id
|
||||
else
|
||||
local d = assert(peek("destructuring"), "DestructuringId not in a destructurable assignement")
|
||||
local vars = { id = tmp() }
|
||||
for j=1, #t, 1 do
|
||||
table.insert(vars, t[j])
|
||||
end
|
||||
table.insert(d, vars)
|
||||
t.id = vars.id
|
||||
return vars.id
|
||||
end
|
||||
end,
|
||||
-- Index{ expr expr }
|
||||
Index = (t)
|
||||
if t[1].tag == "String" or t[1].tag == "Table" then
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
targetName = "luajit"
|
||||
targetName = "LuaJIT"
|
||||
|
||||
UNPACK = (list, i, j)
|
||||
return "unpack(" .. list .. (i and (", " .. i .. (j and (", " .. j) or "")) or "") .. ")"
|
||||
|
|
|
|||
40
ideas.txt
40
ideas.txt
|
|
@ -6,7 +6,7 @@ To be implemented, theese need to:
|
|||
* are invalid vanilla Lua syntax.
|
||||
* are not ambigous with any vanilla Lua syntax.
|
||||
* be significantly useful compared to existing Candran/Lua code.
|
||||
* be useful without having to rewrite APIs specifically for Candran. Candran intends to make Lua easier, not supersede it.
|
||||
* be useful without having to rewrite APIs specifically for Candran. Candran intends to make Lua easier, not replace it.
|
||||
|
||||
Example rejected ideas:
|
||||
* Python-style function decorators (implemented in Candran 0.1.0):
|
||||
|
|
@ -37,6 +37,7 @@ end
|
|||
local a = new Thing()
|
||||
->
|
||||
(TODO: define how classes work. May even use ClassCommons)
|
||||
|
||||
Not very Lua-ey to impose how to make your classes?
|
||||
|
||||
* try / except|catch / finally / else / other keywords
|
||||
|
|
@ -48,6 +49,8 @@ finally
|
|||
clean()
|
||||
end
|
||||
|
||||
may be doable using if with assignement + pcall
|
||||
|
||||
* static type checking
|
||||
local a = externalFunc() -- unknown
|
||||
if a == "hey" then
|
||||
|
|
@ -70,20 +73,26 @@ local b = a[3:5:1]
|
|||
|
||||
is it actually useful? even in python I rarely use it, apart from extracting a row or column for a matrix (and we don't have >1D arrays in Lua so...)
|
||||
|
||||
OR return multiple value instead of a list?
|
||||
|
||||
or list of incices:
|
||||
local a, b, c = l[1, 2, 3]
|
||||
|
||||
how to handle hash table?
|
||||
local a, b, c = l.(a, b, c)
|
||||
or
|
||||
local a, b, c = l.a, .b, .c
|
||||
|
||||
but
|
||||
local a, b, c = l[1], [2], [3]
|
||||
conflicts with table comprehension: change or use .[n]?
|
||||
|
||||
or create some syntax akin to destructuring assignemnts but for numeric indexes:
|
||||
local [a, b, c] = t
|
||||
|
||||
* Destructuring assignment
|
||||
local pos = { x = 5, y = 12 }
|
||||
|
||||
local {x, y} = pos -- x, y = pos.x, pos.y
|
||||
local {a, b, x = x, y = y} = pos -- x, y = pos.x, pos.y, a = pos[1], b = pos[2]
|
||||
local {a, b, :x, :y} = pos -- shorthand for the above line. Or .x, .y
|
||||
local {:x.u} = pos OR {:x:u} OR {.x.u} -- u = pos.x.u
|
||||
local [x, y] = pos -- x, y = pos[0], pos[1]
|
||||
local x, y $= pos
|
||||
|
||||
And in implicit assignments:
|
||||
for i, {x, y} in ipairs(positions) do
|
||||
|
||||
Sounds useful, at least the key-value part.
|
||||
Allow recursive destructing assignements
|
||||
|
||||
* String interpolation
|
||||
Delimited by ``:
|
||||
|
|
@ -93,4 +102,7 @@ Also allows multi-line with this maybe?
|
|||
meh
|
||||
|
||||
* Other potential inspiration
|
||||
https://www.ruby-lang.org/fr/
|
||||
https://www.ruby-lang.org/
|
||||
|
||||
* Lua 5.4 stuff
|
||||
const, to-be-closed variables: they're fun but as of now the syntax is awful
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ apply:
|
|||
`Call{ expr expr* }
|
||||
| `SafeCall{ expr expr* }
|
||||
|
||||
lhs: `Id{ <string> } | `Index{ expr expr }
|
||||
lhs: `Id{ <string> } | `Index{ expr expr } | ˇDestructuringId{ Id | Pair+ }
|
||||
|
||||
opid: -- includes additional operators from Lua 5.3 and all relational operators
|
||||
'add' | 'sub' | 'mul' | 'div'
|
||||
|
|
@ -164,6 +164,10 @@ local labels = {
|
|||
{ "ErrExprFKey", "expected an expression after '[' for the table key" },
|
||||
{ "ErrCBracketFKey", "expected ']' to close the table key" },
|
||||
|
||||
{ "ErrCBraceDestructuring", "expected '}' to close the destructuring variable list" },
|
||||
{ "ErrDestructuringEqField", "expected '=' after the table key in destructuring variable list" },
|
||||
{ "ErrDestructuringExprField", "expected an identifier after '=' in destructuring variable list" },
|
||||
|
||||
{ "ErrCBracketTableCompr", "expected ']' to close the table comprehension" },
|
||||
|
||||
{ "ErrDigitHex", "expected one or more hexadecimal digits after '0x'" },
|
||||
|
|
@ -480,8 +484,9 @@ local G = { V"Lua",
|
|||
Block = tagC("Block", (V"Stat" + -V"BlockEnd" * throw("InvalidStat"))^0 * ((V"RetStat" + V"ImplicitPushStat") * sym(";")^-1)^-1);
|
||||
Stat = V"IfStat" + V"DoStat" + V"WhileStat" + V"RepeatStat" + V"ForStat"
|
||||
+ V"LocalStat" + V"FuncStat" + V"BreakStat" + V"LabelStat" + V"GoToStat"
|
||||
+ V"LetStat"
|
||||
+ V"FuncCall" + V"Assignment"
|
||||
+ V"LetStat" + V"ContinueStat" + V"PushStat"
|
||||
+ V"ContinueStat" + V"PushStat"
|
||||
+ sym(";");
|
||||
BlockEnd = P"return" + "end" + "elseif" + "else" + "until" + "]" + -1 + V"ImplicitPushStat" + V"Assignment";
|
||||
|
||||
|
|
@ -502,17 +507,19 @@ local G = { V"Lua",
|
|||
ForNum = tagC("Fornum", V"Id" * sym("=") * V"NumRange" * V"ForBody");
|
||||
NumRange = expect(V"Expr", "ExprFor1") * expect(sym(","), "CommaFor") *expect(V"Expr", "ExprFor2")
|
||||
* (sym(",") * expect(V"Expr", "ExprFor3"))^-1;
|
||||
ForIn = tagC("Forin", V"NameList" * expect(kw("in"), "InFor") * expect(V"ExprList", "EListFor") * V"ForBody");
|
||||
ForIn = tagC("Forin", V"DestructuringNameList" * expect(kw("in"), "InFor") * expect(V"ExprList", "EListFor") * V"ForBody");
|
||||
ForBody = expectBlockOrSingleStatWithStartEnd(kw("do"), "DoFor", "EndFor");
|
||||
|
||||
LocalStat = kw("local") * expect(V"LocalFunc" + V"LocalAssign", "DefLocal");
|
||||
LocalFunc = tagC("Localrec", kw("function") * expect(V"Id", "NameLFunc") * V"FuncBody") / fixFuncStat;
|
||||
LocalAssign = tagC("Local", V"NameList" * (sym("=") * expect(V"ExprList", "EListLAssign") + Ct(Cc())));
|
||||
LocalAssign = tagC("Local", V"NameList" * (sym("=") * expect(V"ExprList", "EListLAssign") + Ct(Cc())))
|
||||
+ tagC("Local", V"DestructuringNameList" * sym("=") * expect(V"ExprList", "EListLAssign"));
|
||||
|
||||
LetStat = kw("let") * expect(V"LetAssign", "DefLet");
|
||||
LetAssign = tagC("Let", V"NameList" * (sym("=") * expect(V"ExprList", "EListLAssign") + Ct(Cc())));
|
||||
LetAssign = tagC("Let", V"NameList" * (sym("=") * expect(V"ExprList", "EListLAssign") + Ct(Cc())))
|
||||
+ tagC("Let", V"DestructuringNameList" * sym("=") * expect(V"ExprList", "EListLAssign"));
|
||||
|
||||
Assignment = tagC("Set", V"VarList" * V"BinOp"^-1 * (P"=" / "=") * ((V"BinOp" - P"-") + #(P"-" * V"Space") * V"BinOp")^-1 * V"Skip" * expect(V"ExprList", "EListAssign"));
|
||||
Assignment = tagC("Set", (V"VarList" + V"DestructuringNameList") * V"BinOp"^-1 * (P"=" / "=") * ((V"BinOp" - P"-") + #(P"-" * V"Space") * V"BinOp")^-1 * V"Skip" * expect(V"ExprList", "EListAssign"));
|
||||
|
||||
FuncStat = tagC("Set", kw("function") * expect(V"FuncName", "FuncName") * V"FuncBody") / fixFuncStat;
|
||||
FuncName = Cf(V"Id" * (sym(".") * expect(V"StrId", "NameFunc1"))^0, insertIndex)
|
||||
|
|
@ -541,9 +548,15 @@ local G = { V"Lua",
|
|||
ImplicitPushStat = tagC("Push", commaSep(V"Expr", "RetList"));
|
||||
|
||||
NameList = tagC("NameList", commaSep(V"Id"));
|
||||
DestructuringNameList = tagC("NameList", commaSep(V"DestructuringId")),
|
||||
VarList = tagC("VarList", commaSep(V"VarExpr"));
|
||||
ExprList = tagC("ExpList", commaSep(V"Expr", "ExprList"));
|
||||
|
||||
DestructuringId = tagC("DestructuringId", sym("{") * V"DestructuringIdFieldList" * expect(sym("}"), "CBraceDestructuring")) + V"Id",
|
||||
DestructuringIdFieldList = sepBy(V"DestructuringIdField", V"FieldSep") * V"FieldSep"^-1;
|
||||
DestructuringIdField = tagC("Pair", V"FieldKey" * expect(sym("="), "DestructuringEqField") * expect(V"Id", "DestructuringExprField"))
|
||||
+ V"Id";
|
||||
|
||||
Expr = V"OrExpr";
|
||||
OrExpr = chainOp(V"AndExpr", V"OrOp", "OrExpr");
|
||||
AndExpr = chainOp(V"RelExpr", V"AndOp", "AndExpr");
|
||||
|
|
@ -564,7 +577,7 @@ local G = { V"Lua",
|
|||
+ tagC("Boolean", kw("true") * Cc(true))
|
||||
+ tagC("Dots", sym("..."))
|
||||
+ V"FuncDef"
|
||||
+ (when("lexpr") * tagC("LetExpr", V"NameList" * sym("=") * -sym("=") * expect(V"ExprList", "EListLAssign")))
|
||||
+ (when("lexpr") * tagC("LetExpr", V"DestructuringNameList" * sym("=") * -sym("=") * expect(V"ExprList", "EListLAssign")))
|
||||
+ V"ShortFuncDef"
|
||||
+ V"SuffixedExpr"
|
||||
+ V"StatExpr";
|
||||
|
|
|
|||
|
|
@ -304,6 +304,8 @@ function traverse_var (env, var)
|
|||
status, msg = traverse_exp(env, var[2])
|
||||
if not status then return status, msg end
|
||||
return true
|
||||
elseif tag == "DestructuringId" then
|
||||
return traverse_table(env, var)
|
||||
else
|
||||
error("expecting a variable, but got a " .. tag)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -19,12 +19,13 @@ description = {
|
|||
|
||||
source = {
|
||||
url = "git://github.com/Reuh/candran",
|
||||
tag = "v0.10.0"
|
||||
tag = "v0.11.0"
|
||||
}
|
||||
|
||||
dependencies = {
|
||||
"lua >= 5.1",
|
||||
"lpeglabel >= 1.5.0"
|
||||
"lpeglabel >= 1.5.0",
|
||||
"linenoise >= 0.9"
|
||||
}
|
||||
|
||||
build = {
|
||||
185
test/test.lua
185
test/test.lua
|
|
@ -10,6 +10,10 @@ local function test(name, candranCode, expectedResult, options)
|
|||
results[name] = { result = "not finished", message = "no info" }
|
||||
local self = results[name]
|
||||
|
||||
-- options
|
||||
options = options or {}
|
||||
options.chunkname = name
|
||||
|
||||
-- make code
|
||||
local success, code = pcall(candran.make, candranCode, options)
|
||||
if not success then
|
||||
|
|
@ -823,6 +827,187 @@ test("safe prefixes, random chaining", [[
|
|||
assert(f.l?:o?() == nil)
|
||||
]])
|
||||
|
||||
-- Destructuring assigments
|
||||
test("destructuring assignement with an expression", [[
|
||||
local {x, y} = { x = 5, y = 1 }
|
||||
return x + y
|
||||
]], 6)
|
||||
test("destructuring assignement with local", [[
|
||||
t = { x = 5, y = 1 }
|
||||
local {x, y} = t
|
||||
return x + y
|
||||
]], 6)
|
||||
test("destructuring assignement", [[
|
||||
t = { x = 5, y = 1 }
|
||||
{x, y} = t
|
||||
return x + y
|
||||
]], 6)
|
||||
test("destructuring assignement with +=", [[
|
||||
t = { x = 5, y = 1 }
|
||||
local x, y = 5, 9
|
||||
{x, y} += t
|
||||
return x + y
|
||||
]], 20)
|
||||
test("destructuring assignement with =-", [[
|
||||
t = { x = 5, y = 1 }
|
||||
local x, y = 5, 9
|
||||
{x, y} =- t
|
||||
return x + y
|
||||
]], -8)
|
||||
test("destructuring assignement with +=-", [[
|
||||
t = { x = 5, y = 1 }
|
||||
local x, y = 5, 9
|
||||
{x, y} +=- t
|
||||
return x + y
|
||||
]], 6)
|
||||
test("destructuring assignement with =-", [[
|
||||
t = { x = 5, y = 1 }
|
||||
local x, y = 5, 9
|
||||
{x, y} =- t
|
||||
return x + y
|
||||
]], -8)
|
||||
test("destructuring assignement with let", [[
|
||||
t = { x = 5, y = 1 }
|
||||
let {x, y} = t
|
||||
return x + y
|
||||
]], 6)
|
||||
test("destructuring assignement with for in", [[
|
||||
t = {{ x = 5, y = 1 }}
|
||||
for k, {x, y} in pairs(t) do
|
||||
return x + y
|
||||
end
|
||||
]], 6)
|
||||
test("destructuring assignement with if with assignement", [[
|
||||
t = { x = 5, y = 1 }
|
||||
if {x, y} = t then
|
||||
return x + y
|
||||
end
|
||||
]], 6)
|
||||
test("destructuring assignement with if-elseif with assignement", [[
|
||||
t = { x = 5, y = 1 }
|
||||
if ({u} = t) and u then
|
||||
return 0
|
||||
elseif {x, y} = t then
|
||||
return x + y
|
||||
end
|
||||
]], 6)
|
||||
|
||||
test("destructuring assignement with an expression with custom name", [[
|
||||
local {o = x, y} = { o = 5, y = 1 }
|
||||
return x + y
|
||||
]], 6)
|
||||
test("destructuring assignement with local with custom name", [[
|
||||
t = { o = 5, y = 1 }
|
||||
local {o = x, y} = t
|
||||
return x + y
|
||||
]], 6)
|
||||
test("destructuring assignement with custom name", [[
|
||||
t = { o = 5, y = 1 }
|
||||
{o = x, y} = t
|
||||
return x + y
|
||||
]], 6)
|
||||
test("destructuring assignement with += with custom name", [[
|
||||
t = { o = 5, y = 1 }
|
||||
local x, y = 5, 9
|
||||
{o = x, y} += t
|
||||
return x + y
|
||||
]], 20)
|
||||
test("destructuring assignement with =- with custom name", [[
|
||||
t = { o = 5, y = 1 }
|
||||
local x, y = 5, 9
|
||||
{o = x, y} =- t
|
||||
return x + y
|
||||
]], -8)
|
||||
test("destructuring assignement with +=- with custom name", [[
|
||||
t = { o = 5, y = 1 }
|
||||
local x, y = 5, 9
|
||||
{o = x, y} +=- t
|
||||
return x + y
|
||||
]], 6)
|
||||
test("destructuring assignement with let with custom name", [[
|
||||
t = { o = 5, y = 1 }
|
||||
let {o = x, y} = t
|
||||
return x + y
|
||||
]], 6)
|
||||
test("destructuring assignement with for in with custom name", [[
|
||||
t = {{ o = 5, y = 1 }}
|
||||
for k, {o = x, y} in pairs(t) do
|
||||
return x + y
|
||||
end
|
||||
]], 6)
|
||||
test("destructuring assignement with if with assignement with custom name", [[
|
||||
t = { o = 5, y = 1 }
|
||||
if {o = x, y} = t then
|
||||
return x + y
|
||||
end
|
||||
]], 6)
|
||||
test("destructuring assignement with if-elseif with assignement with custom name", [[
|
||||
t = { o = 5, y = 1 }
|
||||
if ({x} = t) and x then
|
||||
return 0
|
||||
elseif {o = x, y} = t then
|
||||
return x + y
|
||||
end
|
||||
]], 6)
|
||||
|
||||
test("destructuring assignement with an expression with expression as key", [[
|
||||
local {[1] = x, y} = { 5, y = 1 }
|
||||
return x + y
|
||||
]], 6)
|
||||
test("destructuring assignement with local with expression as key", [[
|
||||
t = { 5, y = 1 }
|
||||
local {[1] = x, y} = t
|
||||
return x + y
|
||||
]], 6)
|
||||
test("destructuring assignement with expression as key", [[
|
||||
t = { 5, y = 1 }
|
||||
{[1] = x, y} = t
|
||||
return x + y
|
||||
]], 6)
|
||||
test("destructuring assignement with += with expression as key", [[
|
||||
t = { 5, y = 1 }
|
||||
local x, y = 5, 9
|
||||
{[1] = x, y} += t
|
||||
return x + y
|
||||
]], 20)
|
||||
test("destructuring assignement with =- with expression as key", [[
|
||||
t = { 5, y = 1 }
|
||||
local x, y = 5, 9
|
||||
{[1] = x, y} =- t
|
||||
return x + y
|
||||
]], -8)
|
||||
test("destructuring assignement with +=- with expression as key", [[
|
||||
t = { 5, y = 1 }
|
||||
local x, y = 5, 9
|
||||
{[1] = x, y} +=- t
|
||||
return x + y
|
||||
]], 6)
|
||||
test("destructuring assignement with let with expression as key", [[
|
||||
t = { 5, y = 1 }
|
||||
let {[1] = x, y} = t
|
||||
return x + y
|
||||
]], 6)
|
||||
test("destructuring assignement with for in with expression as key", [[
|
||||
t = {{ 5, y = 1 }}
|
||||
for k, {[1] = x, y} in pairs(t) do
|
||||
return x + y
|
||||
end
|
||||
]], 6)
|
||||
test("destructuring assignement with if with assignement with expression as key", [[
|
||||
t = { 5, y = 1 }
|
||||
if {[1] = x, y} = t then
|
||||
return x + y
|
||||
end
|
||||
]], 6)
|
||||
test("destructuring assignement with if-elseif with assignement with expression as key", [[
|
||||
t = { 5, y = 1 }
|
||||
if ({x} = t) and x then
|
||||
return 0
|
||||
elseif {[1] = x, y} = t then
|
||||
return x + y
|
||||
end
|
||||
]], 6)
|
||||
|
||||
-- results
|
||||
local resultCounter = {}
|
||||
local testCounter = 0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue