1
0
Fork 0
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:
Étienne Fildadut 2020-01-15 20:42:27 +01:00
parent 851e9f89d6
commit 842536b561
10 changed files with 3823 additions and 2985 deletions

View file

@ -42,6 +42,8 @@ end)
a.child?:method?() -- safe navigation operator a.child?:method?() -- safe navigation operator
local {hey, method} = a -- destructuring assignement
local odd = [ -- table comprehension local odd = [ -- table comprehension
for i=1, 10 do for i=1, 10 do
if i%2 == 0 then if i%2 == 0 then
@ -70,11 +72,11 @@ end
Candran is released under the MIT License (see ```LICENSE``` for details). Candran is released under the MIT License (see ```LICENSE``` for details).
#### Quick setup #### 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```. 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. 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. 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 ##### Safe navigation operators
```lua ```lua
a = nil a = nil

View file

@ -11,7 +11,7 @@
#import("lib.lua-parser.parser") #import("lib.lua-parser.parser")
local candran = { local candran = {
VERSION = "0.10.0" VERSION = "0.11.0"
} }
--- Default options. --- Default options.

File diff suppressed because it is too large Load diff

View file

@ -14,7 +14,7 @@ return function(code, ast, options)
local r = options.newline..string.rep(options.indentation, indentLevel) local r = options.newline..string.rep(options.indentation, indentLevel)
if options.mapLines then if options.mapLines then
local sub = code:sub(lastInputPos) 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 if source and line then
lastSource = source lastSource = source
@ -42,15 +42,31 @@ return function(code, ast, options)
return newline() return newline()
end end
--- Module management --- State stacks
local required = {} -- { ["module"] = true, ... } -- Used for context-sensitive syntax.
local requireStr = "" local states = {
-- 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". push = {}, -- push stack variable names
local function addRequire(mod, name, field) destructuring = {}, -- list of variable that need to be assigned from a destructure {id = "parent variable", "field1", "field2"...}
if not required[mod] then scope = {} -- list of variables defined in the current scope
requireStr ..= "local "..options.variablePrefix..name..(" = require(%q)"):format(mod)..(field and "."..field or "")..options.newline }
required[mod] = true -- 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 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 end
--- Variable management --- Variable management
@ -59,6 +75,26 @@ return function(code, ast, options)
return options.variablePrefix..name return options.variablePrefix..name
end 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 --- AST traversal helpers
local loop = { "While", "Repeat", "Fornum", "Forin", "WhileExpr", "RepeatExpr", "FornumExpr", "ForinExpr" } -- loops tags (can contain continue) 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) 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 return true
end 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 --- Lua compiler
local tags 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. -- 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 local CONTINUE_STOP = () -- at the start of loops using continue
return unindent().."end"..newline().."::"..var"continue".."::" return unindent().."end"..newline().."::"..var"continue".."::"
end 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 --- Tag constructors
tags = setmetatable({ tags = setmetatable({
@ -185,7 +238,7 @@ return function(code, ast, options)
hasPush.tag = "Return" hasPush.tag = "Return"
hasPush = false hasPush = false
end end
local r = "" local r = push("scope", {})
if hasPush then if hasPush then
r ..= push("push", var"push").."local "..var"push".." = {}"..newline() r ..= push("push", var"push").."local "..var"push".." = {}"..newline()
end 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 if hasPush and (t[#t] and t[#t].tag ~= "Return") then -- add return only if needed
r ..= newline().."return "..UNPACK(var"push")..pop("push") r ..= newline().."return "..UNPACK(var"push")..pop("push")
end end
return r return r..pop("scope")
end, end,
-- stat -- -- stat --
@ -209,28 +262,73 @@ return function(code, ast, options)
end, end,
-- Set{ {lhs+} (opid? = opid?)? {expr+} } -- Set{ {lhs+} (opid? = opid?)? {expr+} }
Set = (t) Set = (t)
if #t == 2 then -- extract vars and values
return lua(t[1], "_lhs").." = "..lua(t[2], "_lhs") local expr = t[#t]
elseif #t == 3 then local vars, values = {}, {}
return lua(t[1], "_lhs").." = "..lua(t[3], "_lhs") 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 elseif #t == 4 then
if t[3] == "=" then if t[3] == "=" then
local r = lua(t[1], "_lhs").." = "..lua({ t[2], t[1][1], { tag = "Paren", t[4][1] } }, "Op") local r = ""
for i=2, math.min(#t[4], #t[1]), 1 do if #vars > 0 then
r ..= ", "..lua({ t[2], t[1][i], { tag = "Paren", t[4][i] } }, "Op") 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 end
return r return r
else 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 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 end
return r return r
end end
else -- You are mad. 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 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 end
return r return r
end end
@ -344,12 +442,13 @@ return function(code, ast, options)
end, end,
-- Forin{ {ident+} {expr+} block } -- Forin{ {ident+} {expr+} block }
Forin = (t) Forin = (t)
local destructured = {}
local hasContinue = any(t[3], { "Continue" }, loop) 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 if hasContinue then
r ..= CONTINUE_START() r ..= CONTINUE_START()
end end
r ..= lua(t[3]) r ..= DESTRUCTURING_ASSIGN(destructured, true)..lua(t[3])
if hasContinue then if hasContinue then
r ..= CONTINUE_STOP() r ..= CONTINUE_STOP()
end end
@ -357,15 +456,17 @@ return function(code, ast, options)
end, end,
-- Local{ {ident+} {expr+}? } -- Local{ {ident+} {expr+}? }
Local = (t) 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 if t[2][1] then
r ..= " = "..lua(t[2], "_lhs") r ..= " = "..lua(t[2], "_lhs")
end end
return r return r..DESTRUCTURING_ASSIGN(destructured)
end, end,
-- Let{ {ident+} {expr+}? } -- Let{ {ident+} {expr+}? }
Let = (t) 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 local r = "local "..nameList
if t[2][1] then if t[2][1] then
if all(t[2], { "Nil", "Dots", "Boolean", "Number", "String" }) then -- predeclaration doesn't matter here 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") r ..= newline()..nameList.." = "..lua(t[2], "_lhs")
end end
end end
return r return r..DESTRUCTURING_ASSIGN(destructured)
end, end,
-- Localrec{ {ident} {expr} } -- Localrec{ {ident} {expr} }
Localrec = (t) Localrec = (t)
@ -658,6 +759,21 @@ return function(code, ast, options)
Id = (t) Id = (t)
return t[1] return t[1]
end, 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{ expr expr }
Index = (t) Index = (t)
if t[1].tag == "String" or t[1].tag == "Table" then if t[1].tag == "String" or t[1].tag == "Table" then

View file

@ -1,4 +1,4 @@
targetName = "luajit" targetName = "LuaJIT"
UNPACK = (list, i, j) UNPACK = (list, i, j)
return "unpack(" .. list .. (i and (", " .. i .. (j and (", " .. j) or "")) or "") .. ")" return "unpack(" .. list .. (i and (", " .. i .. (j and (", " .. j) or "")) or "") .. ")"

View file

@ -6,7 +6,7 @@ To be implemented, theese need to:
* are invalid vanilla Lua syntax. * are invalid vanilla Lua syntax.
* are not ambigous with any vanilla Lua syntax. * are not ambigous with any vanilla Lua syntax.
* be significantly useful compared to existing Candran/Lua code. * 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: Example rejected ideas:
* Python-style function decorators (implemented in Candran 0.1.0): * Python-style function decorators (implemented in Candran 0.1.0):
@ -37,6 +37,7 @@ end
local a = new Thing() local a = new Thing()
-> ->
(TODO: define how classes work. May even use ClassCommons) (TODO: define how classes work. May even use ClassCommons)
Not very Lua-ey to impose how to make your classes? Not very Lua-ey to impose how to make your classes?
* try / except|catch / finally / else / other keywords * try / except|catch / finally / else / other keywords
@ -48,6 +49,8 @@ finally
clean() clean()
end end
may be doable using if with assignement + pcall
* static type checking * static type checking
local a = externalFunc() -- unknown local a = externalFunc() -- unknown
if a == "hey" then 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...) 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 * Destructuring assignment
local pos = { x = 5, y = 12 }
local {x, y} = pos -- x, y = pos.x, pos.y Allow recursive destructing assignements
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.
* String interpolation * String interpolation
Delimited by ``: Delimited by ``:
@ -93,4 +102,7 @@ Also allows multi-line with this maybe?
meh meh
* Other potential inspiration * 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

View file

@ -59,7 +59,7 @@ apply:
`Call{ expr expr* } `Call{ expr expr* }
| `SafeCall{ 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 opid: -- includes additional operators from Lua 5.3 and all relational operators
'add' | 'sub' | 'mul' | 'div' 'add' | 'sub' | 'mul' | 'div'
@ -164,6 +164,10 @@ local labels = {
{ "ErrExprFKey", "expected an expression after '[' for the table key" }, { "ErrExprFKey", "expected an expression after '[' for the table key" },
{ "ErrCBracketFKey", "expected ']' to close 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" }, { "ErrCBracketTableCompr", "expected ']' to close the table comprehension" },
{ "ErrDigitHex", "expected one or more hexadecimal digits after '0x'" }, { "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); 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" Stat = V"IfStat" + V"DoStat" + V"WhileStat" + V"RepeatStat" + V"ForStat"
+ V"LocalStat" + V"FuncStat" + V"BreakStat" + V"LabelStat" + V"GoToStat" + V"LocalStat" + V"FuncStat" + V"BreakStat" + V"LabelStat" + V"GoToStat"
+ V"LetStat"
+ V"FuncCall" + V"Assignment" + V"FuncCall" + V"Assignment"
+ V"LetStat" + V"ContinueStat" + V"PushStat" + V"ContinueStat" + V"PushStat"
+ sym(";"); + sym(";");
BlockEnd = P"return" + "end" + "elseif" + "else" + "until" + "]" + -1 + V"ImplicitPushStat" + V"Assignment"; 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"); ForNum = tagC("Fornum", V"Id" * sym("=") * V"NumRange" * V"ForBody");
NumRange = expect(V"Expr", "ExprFor1") * expect(sym(","), "CommaFor") *expect(V"Expr", "ExprFor2") NumRange = expect(V"Expr", "ExprFor1") * expect(sym(","), "CommaFor") *expect(V"Expr", "ExprFor2")
* (sym(",") * expect(V"Expr", "ExprFor3"))^-1; * (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"); ForBody = expectBlockOrSingleStatWithStartEnd(kw("do"), "DoFor", "EndFor");
LocalStat = kw("local") * expect(V"LocalFunc" + V"LocalAssign", "DefLocal"); LocalStat = kw("local") * expect(V"LocalFunc" + V"LocalAssign", "DefLocal");
LocalFunc = tagC("Localrec", kw("function") * expect(V"Id", "NameLFunc") * V"FuncBody") / fixFuncStat; 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"); 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; FuncStat = tagC("Set", kw("function") * expect(V"FuncName", "FuncName") * V"FuncBody") / fixFuncStat;
FuncName = Cf(V"Id" * (sym(".") * expect(V"StrId", "NameFunc1"))^0, insertIndex) 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")); ImplicitPushStat = tagC("Push", commaSep(V"Expr", "RetList"));
NameList = tagC("NameList", commaSep(V"Id")); NameList = tagC("NameList", commaSep(V"Id"));
DestructuringNameList = tagC("NameList", commaSep(V"DestructuringId")),
VarList = tagC("VarList", commaSep(V"VarExpr")); VarList = tagC("VarList", commaSep(V"VarExpr"));
ExprList = tagC("ExpList", commaSep(V"Expr", "ExprList")); 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"; Expr = V"OrExpr";
OrExpr = chainOp(V"AndExpr", V"OrOp", "OrExpr"); OrExpr = chainOp(V"AndExpr", V"OrOp", "OrExpr");
AndExpr = chainOp(V"RelExpr", V"AndOp", "AndExpr"); AndExpr = chainOp(V"RelExpr", V"AndOp", "AndExpr");
@ -564,7 +577,7 @@ local G = { V"Lua",
+ tagC("Boolean", kw("true") * Cc(true)) + tagC("Boolean", kw("true") * Cc(true))
+ tagC("Dots", sym("...")) + tagC("Dots", sym("..."))
+ V"FuncDef" + 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"ShortFuncDef"
+ V"SuffixedExpr" + V"SuffixedExpr"
+ V"StatExpr"; + V"StatExpr";

View file

@ -304,6 +304,8 @@ function traverse_var (env, var)
status, msg = traverse_exp(env, var[2]) status, msg = traverse_exp(env, var[2])
if not status then return status, msg end if not status then return status, msg end
return true return true
elseif tag == "DestructuringId" then
return traverse_table(env, var)
else else
error("expecting a variable, but got a " .. tag) error("expecting a variable, but got a " .. tag)
end end

View file

@ -19,12 +19,13 @@ description = {
source = { source = {
url = "git://github.com/Reuh/candran", url = "git://github.com/Reuh/candran",
tag = "v0.10.0" tag = "v0.11.0"
} }
dependencies = { dependencies = {
"lua >= 5.1", "lua >= 5.1",
"lpeglabel >= 1.5.0" "lpeglabel >= 1.5.0",
"linenoise >= 0.9"
} }
build = { build = {

View file

@ -10,6 +10,10 @@ local function test(name, candranCode, expectedResult, options)
results[name] = { result = "not finished", message = "no info" } results[name] = { result = "not finished", message = "no info" }
local self = results[name] local self = results[name]
-- options
options = options or {}
options.chunkname = name
-- make code -- make code
local success, code = pcall(candran.make, candranCode, options) local success, code = pcall(candran.make, candranCode, options)
if not success then if not success then
@ -125,203 +129,203 @@ return a
-- Assignement operators -- Assignement operators
test("+=", [[ test("+=", [[
local a = 5 local a = 5
a += 2 a += 2
return a return a
]], 7) ]], 7)
test("-=", [[ test("-=", [[
local a = 5 local a = 5
a -= 2 a -= 2
return a return a
]], 3) ]], 3)
test("*=", [[ test("*=", [[
local a = 5 local a = 5
a *= 2 a *= 2
return a return a
]], 10) ]], 10)
test("/=", [[ test("/=", [[
local a = 5 local a = 5
a /= 2 a /= 2
return a return a
]], 5/2) ]], 5/2)
test("//=", [[ test("//=", [[
local a = 5 local a = 5
a //= 2 a //= 2
return a return a
]], 2) ]], 2)
test("^=", [[ test("^=", [[
local a = 5 local a = 5
a ^= 2 a ^= 2
return a return a
]], 25) ]], 25)
test("%=", [[ test("%=", [[
local a = 5 local a = 5
a %= 2 a %= 2
return a return a
]], 5%2) ]], 5%2)
test("..=", [[ test("..=", [[
local a = "hello" local a = "hello"
a ..= " world" a ..= " world"
return a return a
]], "hello world") ]], "hello world")
test("and=", [[ test("and=", [[
local a = true local a = true
a and= "world" a and= "world"
return a return a
]], "world") ]], "world")
test("or=", [[ test("or=", [[
local a = false local a = false
a or= "world" a or= "world"
return a return a
]], "world") ]], "world")
test("&=", [[ test("&=", [[
local a = 5 local a = 5
a &= 3 a &= 3
return a return a
]], 1) ]], 1)
test("|=", [[ test("|=", [[
local a = 5 local a = 5
a |= 3 a |= 3
return a return a
]], 7) ]], 7)
test("<<=", [[ test("<<=", [[
local a = 23 local a = 23
a <<= 2 a <<= 2
return a return a
]], 92) ]], 92)
test(">>=", [[ test(">>=", [[
local a = 23 local a = 23
a >>= 2 a >>= 2
return a return a
]], 5) ]], 5)
test("right assigments operators", [[ test("right assigments operators", [[
local a = 5 local a = 5
a =+ 2 assert(a == 7, "=+") a =+ 2 assert(a == 7, "=+")
a =- 2 assert(a == -5, "=-") a =- 2 assert(a == -5, "=-")
a =* -2 assert(a == 10, "=*") a =* -2 assert(a == 10, "=*")
a =/ 2 assert(a == 0.2, "=/") a =/ 2 assert(a == 0.2, "=/")
a =// 2 assert(a == 10, "=//") a =// 2 assert(a == 10, "=//")
a =^ 2 assert(a == 1024, "=^") a =^ 2 assert(a == 1024, "=^")
a =% 2000 assert(a == 976, "=%") a =% 2000 assert(a == 976, "=%")
a = "world" a = "world"
a =.. "hello " assert(a == "hello world", "=..") a =.. "hello " assert(a == "hello world", "=..")
a =and true assert(a == "hello world", "=and") a =and true assert(a == "hello world", "=and")
a = false a = false
a =or nil assert(a == false, "=or") a =or nil assert(a == false, "=or")
a = 3 a = 3
a =& 5 assert(a == 1, '=&') a =& 5 assert(a == 1, '=&')
a =| 20 assert(a == 21, "=|") a =| 20 assert(a == 21, "=|")
a =<< 1 assert(a == 2097152, "=<<") a =<< 1 assert(a == 2097152, "=<<")
a = 2 a = 2
a =>> 23 assert(a == 5, "=>>") a =>> 23 assert(a == 5, "=>>")
]], nil) ]], nil)
test("some left+right assigments operators", [[ test("some left+right assigments operators", [[
local a = 5 local a = 5
a -=+ 2 assert(a == 8, "-=+") a -=+ 2 assert(a == 8, "-=+")
a = "hello" a = "hello"
a ..=.. " world " assert(a == "hello world hello", "..=..") a ..=.. " world " assert(a == "hello world hello", "..=..")
]], nil) ]], nil)
test("left assigments operators priority", [[ test("left assigments operators priority", [[
local a = 5 local a = 5
a *= 2 + 3 a *= 2 + 3
return a return a
]], 25) ]], 25)
test("right assigments operators priority", [[ test("right assigments operators priority", [[
local a = 5 local a = 5
a =/ 2 + 3 a =/ 2 + 3
return a return a
]], 1) ]], 1)
test("left+right assigments operators priority", [[ test("left+right assigments operators priority", [[
local a = 5 local a = 5
a *=/ 2 + 3 a *=/ 2 + 3
return a return a
]], 5) ]], 5)
-- Default function parameters -- Default function parameters
test("default parameters", [[ test("default parameters", [[
local function test(hey, def="re", no, foo=("bar"):gsub("bar", "batru")) local function test(hey, def="re", no, foo=("bar"):gsub("bar", "batru"))
return def..foo return def..foo
end end
return test(78, "SANDWICH", true) return test(78, "SANDWICH", true)
]], "SANDWICHbatru") ]], "SANDWICHbatru")
-- @ self alias -- @ self alias
test("@ as self alias", [[ test("@ as self alias", [[
local a = {} local a = {}
function a:hey() function a:hey()
return @ == self return @ == self
end end
return a:hey() return a:hey()
]], true) ]], true)
test("@ as self alias and indexation", [[ test("@ as self alias and indexation", [[
local a = { local a = {
foo = "Hoi" foo = "Hoi"
} }
function a:hey() function a:hey()
return @.foo return @.foo
end end
return a:hey() return a:hey()
]], "Hoi") ]], "Hoi")
test("@name indexation", [[ test("@name indexation", [[
local a = { local a = {
foo = "Hoi" foo = "Hoi"
} }
function a:hey() function a:hey()
return @foo return @foo
end end
return a:hey() return a:hey()
]], "Hoi") ]], "Hoi")
test("@name method call", [[ test("@name method call", [[
local a = { local a = {
foo = "Hoi", foo = "Hoi",
bar = function(self) bar = function(self)
return self.foo return self.foo
end end
} }
function a:hey() function a:hey()
return @bar() return @bar()
end end
return a:hey() return a:hey()
]], "Hoi") ]], "Hoi")
test("@[expt] indexation", [[ test("@[expt] indexation", [[
local a = { local a = {
foo = "Hoi" foo = "Hoi"
} }
function a:hey() function a:hey()
return @["foo"] return @["foo"]
end end
return a:hey() return a:hey()
]], "Hoi") ]], "Hoi")
-- Short anonymous functions declaration -- Short anonymous functions declaration
test("short anonymous function declaration", [[ test("short anonymous function declaration", [[
local a = (arg1) local a = (arg1)
return arg1 return arg1
end end
return a(5) return a(5)
]], 5) ]], 5)
test("short anonymous method declaration", [[ test("short anonymous method declaration", [[
local a = :(arg1) local a = :(arg1)
return self + arg1 return self + arg1
end end
return a(2, 3) return a(2, 3)
]], 5) ]], 5)
test("short anonymous method parsing edge cases", [[ test("short anonymous method parsing edge cases", [[
-- Taken from the file I used when solving this horror, too tired to make separate tests. -- Taken from the file I used when solving this horror, too tired to make separate tests.
x = "" x = ""
function a(s) function a(s)
x = x .. tostring(s or "+") x = x .. tostring(s or "+")
end end
k=true k=true
while k do while k do
k=false k=false
cap = {[0] = op, a} cap = {[0] = op, a}
a(tostring(h)) a(tostring(h))
@ -334,30 +338,30 @@ while k do
a() a()
end end
a() a()
end end
a() a()
a("l") a("l")
let h = (h) let h = (h)
a("h") a("h")
end end
h() h()
a("lol") a("lol")
if false then exit() end if false then exit() end
a("pmo") a("pmo")
if true then if true then
if false if false
a = (h) a = (h)
a() a()
a("pom") a("pom")
end end
a("lo") a("lo")
a("kol") a("kol")
if false then if false then
j() j()
p() p()
end end
do do
b = [ b = [
k = () end k = () end
if false if false
@ -367,45 +371,45 @@ do
k() k()
a()] a()]
end end
if a() then h() end if a() then h() end
local function f (...) local function f (...)
if select('#', ...) == 1 then if select('#', ...) == 1 then
return (...) return (...)
else else
return "***" return "***"
end end
end end
return f(x) return f(x)
]], "nil++++lhlolpmo+pomlokol++") ]], "nil++++lhlolpmo+pomlokol++")
-- let variable declaration -- let variable declaration
test("let variable declaration", [[ test("let variable declaration", [[
let a = { let a = {
foo = function() foo = function()
return type(a) return type(a)
end end
} }
return a.foo() return a.foo()
]], "table") ]], "table")
-- continue keyword -- continue keyword
test("continue keyword in while", [[ test("continue keyword in while", [[
local a = "" local a = ""
local i = 0 local i = 0
while i < 10 do while i < 10 do
i = i + 1 i = i + 1
if i % 2 == 0 then if i % 2 == 0 then
continue continue
end end
a = a .. i a = a .. i
end end
return a return a
]], "13579") ]], "13579")
test("continue keyword in while, used with break", [[ test("continue keyword in while, used with break", [[
local a = "" local a = ""
local i = 0 local i = 0
while i < 10 do while i < 10 do
i = i + 1 i = i + 1
if i % 2 == 0 then if i % 2 == 0 then
continue continue
@ -414,25 +418,25 @@ while i < 10 do
if i == 5 then if i == 5 then
break break
end end
end end
return a return a
]], "135") ]], "135")
test("continue keyword in repeat", [[ test("continue keyword in repeat", [[
local a = "" local a = ""
local i = 0 local i = 0
repeat repeat
i = i + 1 i = i + 1
if i % 2 == 0 then if i % 2 == 0 then
continue continue
end end
a = a .. i a = a .. i
until i == 10 until i == 10
return a return a
]], "13579") ]], "13579")
test("continue keyword in repeat, used with break", [[ test("continue keyword in repeat, used with break", [[
local a = "" local a = ""
local i = 0 local i = 0
repeat repeat
i = i + 1 i = i + 1
if i % 2 == 0 then if i % 2 == 0 then
continue continue
@ -441,22 +445,22 @@ repeat
if i == 5 then if i == 5 then
break break
end end
until i == 10 until i == 10
return a return a
]], "135") ]], "135")
test("continue keyword in fornum", [[ test("continue keyword in fornum", [[
local a = "" local a = ""
for i=1, 10 do for i=1, 10 do
if i % 2 == 0 then if i % 2 == 0 then
continue continue
end end
a = a .. i a = a .. i
end end
return a return a
]], "13579") ]], "13579")
test("continue keyword in fornum, used with break", [[ test("continue keyword in fornum, used with break", [[
local a = "" local a = ""
for i=1, 10 do for i=1, 10 do
if i % 2 == 0 then if i % 2 == 0 then
continue continue
end end
@ -464,24 +468,24 @@ for i=1, 10 do
if i == 5 then if i == 5 then
break break
end end
end end
return a return a
]], "135") ]], "135")
test("continue keyword in for", [[ test("continue keyword in for", [[
local t = {1,2,3,4,5,6,7,8,9,10} local t = {1,2,3,4,5,6,7,8,9,10}
local a = "" local a = ""
for _, i in ipairs(t) do for _, i in ipairs(t) do
if i % 2 == 0 then if i % 2 == 0 then
continue continue
end end
a = a .. i a = a .. i
end end
return a return a
]], "13579") ]], "13579")
test("continue keyword in for, used with break", [[ test("continue keyword in for, used with break", [[
local t = {1,2,3,4,5,6,7,8,9,10} local t = {1,2,3,4,5,6,7,8,9,10}
local a = "" local a = ""
for _, i in ipairs(t) do for _, i in ipairs(t) do
if i % 2 == 0 then if i % 2 == 0 then
continue continue
end end
@ -489,190 +493,190 @@ for _, i in ipairs(t) do
if i == 5 then if i == 5 then
break break
end end
end end
return a return a
]], "135") ]], "135")
-- push keyword -- push keyword
test("push keyword", [[ test("push keyword", [[
function a() function a()
for i=1, 5 do for i=1, 5 do
push i, "next" push i, "next"
end end
return "done" return "done"
end end
return table.concat({a()}) return table.concat({a()})
]], "1next2next3next4next5nextdone") ]], "1next2next3next4next5nextdone")
test("push keyword variable length", [[ test("push keyword variable length", [[
function v() function v()
return "hey", "hop" return "hey", "hop"
end end
function w() function w()
return "foo", "bar" return "foo", "bar"
end end
function a() function a()
push 5, v(), w() push 5, v(), w()
return return
end end
return table.concat({a()}) return table.concat({a()})
]], "5heyfoobar") ]], "5heyfoobar")
-- implicit push -- implicit push
test("implicit push", [[ test("implicit push", [[
function a() function a()
for i=1, 5 do for i=1, 5 do
i, "next" i, "next"
end end
return "done" return "done"
end end
return table.concat({a()}) return table.concat({a()})
]], "1next2next3next4next5nextdone") ]], "1next2next3next4next5nextdone")
test("implicit push variable length", [[ test("implicit push variable length", [[
function v() function v()
return "hey", "hop" return "hey", "hop"
end end
function w() function w()
return "foo", "bar" return "foo", "bar"
end end
function a() function a()
if true then if true then
5, v(), w() 5, v(), w()
end end
end end
return table.concat({a()}) return table.concat({a()})
]], "5heyfoobar") ]], "5heyfoobar")
-- statement expressions -- statement expressions
test("if statement expressions", [[ test("if statement expressions", [[
a = if false then a = if false then
"foo" -- i.e. push "foo", i.e. return "foo" "foo" -- i.e. push "foo", i.e. return "foo"
else else
"bar" "bar"
end end
return a return a
]], "bar") ]], "bar")
test("do statement expressions", [[ test("do statement expressions", [[
a = do a = do
"bar" "bar"
end end
return a return a
]], "bar") ]], "bar")
test("while statement expressions", [[ test("while statement expressions", [[
i=0 i=0
a, b, c = while i<2 do i=i+1; i end a, b, c = while i<2 do i=i+1; i end
return table.concat({a, b, tostring(c)}) return table.concat({a, b, tostring(c)})
]], "12nil") ]], "12nil")
test("repeat statement expressions", [[ test("repeat statement expressions", [[
local i = 0 local i = 0
a, b, c = repeat i=i+1; i until i==2 a, b, c = repeat i=i+1; i until i==2
return table.concat({a, b, tostring(c)}) return table.concat({a, b, tostring(c)})
]], "12nil") ]], "12nil")
test("for statement expressions", [[ test("for statement expressions", [[
a, b, c = for i=1,2 do i end a, b, c = for i=1,2 do i end
return table.concat({a, b, tostring(c)}) return table.concat({a, b, tostring(c)})
]], "12nil") ]], "12nil")
-- table comprehension -- table comprehension
test("table comprehension sequence", [[ test("table comprehension sequence", [[
return table.concat([for i=1,10 do i end]) return table.concat([for i=1,10 do i end])
]], "12345678910") ]], "12345678910")
test("table comprehension associative/self", [[ test("table comprehension associative/self", [[
a = [for i=1, 10 do @[i] = true end] a = [for i=1, 10 do @[i] = true end]
return a[1] and a[10] return a[1] and a[10]
]], true) ]], true)
test("table comprehension variable length", [[ test("table comprehension variable length", [[
t1 = {"hey", "hop"} t1 = {"hey", "hop"}
t2 = {"foo", "bar"} t2 = {"foo", "bar"}
return table.concat([push unpack(t1); push unpack(t2)]) return table.concat([push unpack(t1); push unpack(t2)])
]], "heyhopfoobar") ]], "heyhopfoobar")
-- one line statements -- one line statements
test("one line if", [[ test("one line if", [[
a = 5 a = 5
if false if false
a = 0 a = 0
return a return a
]], 5) ]], 5)
test("one line if-elseif", [[ test("one line if-elseif", [[
a = 5 a = 5
if false if false
a = 0 a = 0
elseif true elseif true
a = 3 a = 3
elseif false elseif false
a = -1 a = -1
return a return a
]], 3) ]], 3)
test("one line for", [[ test("one line for", [[
a = 0 a = 0
for i=1,5 for i=1,5
a = a + 1 a = a + 1
return a return a
]], 5) ]], 5)
test("one line while", [[ test("one line while", [[
a = 0 a = 0
while a < 5 while a < 5
a = a + 1 a = a + 1
return a return a
]], 5) ]], 5)
-- suffixable string litals, table, table comprehension -- suffixable string litals, table, table comprehension
test("suffixable string litteral method", [[ test("suffixable string litteral method", [[
return "foo":len() return "foo":len()
]], 3) ]], 3)
test("suffixable string litteral method lua conflict", [[ test("suffixable string litteral method lua conflict", [[
local s = function() return "four" end local s = function() return "four" end
return s"foo":len() return s"foo":len()
]], 4) ]], 4)
test("suffixable string litteral dot index", [[ test("suffixable string litteral dot index", [[
local a = "foo".len local a = "foo".len
return a("foo") return a("foo")
]], 3) ]], 3)
test("suffixable string litteral dot index lua conflict", [[ test("suffixable string litteral dot index lua conflict", [[
local s = function() return {len=4} end local s = function() return {len=4} end
local a = s"foo".len local a = s"foo".len
return a return a
]], 4) ]], 4)
test("suffixable string litteral array index", [[ test("suffixable string litteral array index", [[
local a = "foo"["len"] local a = "foo"["len"]
return a("foo") return a("foo")
]], 3) ]], 3)
test("suffixable string litteral dot index lua conflict", [[ test("suffixable string litteral dot index lua conflict", [[
local s = function() return {len=4} end local s = function() return {len=4} end
local a = s"foo"["len"] local a = s"foo"["len"]
return a return a
]], 4) ]], 4)
test("suffixable table litteral method", [[ test("suffixable table litteral method", [[
return {a=3,len=function(t) return t.a end}:len() return {a=3,len=function(t) return t.a end}:len()
]], 3) ]], 3)
test("suffixable table litteral method lua conflict", [[ test("suffixable table litteral method lua conflict", [[
local s = function() return "four" end local s = function() return "four" end
return s{a=3,len=function(t) return t.a end}:len() return s{a=3,len=function(t) return t.a end}:len()
]], 4) ]], 4)
test("suffixable table litteral dot index", [[ test("suffixable table litteral dot index", [[
return {len=3}.len return {len=3}.len
]], 3) ]], 3)
test("suffixable table litteral dot index lua conflict", [[ test("suffixable table litteral dot index lua conflict", [[
local s = function() return {len=4} end local s = function() return {len=4} end
return s{len=3}.len return s{len=3}.len
]], 4) ]], 4)
test("suffixable table litteral array index", [[ test("suffixable table litteral array index", [[
return {len=3}["len"] return {len=3}["len"]
]], 3) ]], 3)
test("suffixable table litteral dot index lua conflict", [[ test("suffixable table litteral dot index lua conflict", [[
local s = function() return {len=4} end local s = function() return {len=4} end
return s{len=3}["len"] return s{len=3}["len"]
]], 4) ]], 4)
test("suffixable table comprehension method", [[ test("suffixable table comprehension method", [[
return [@len = function() return 3 end]:len() return [@len = function() return 3 end]:len()
]], 3) ]], 3)
test("suffixable table comprehension dot index", [[ test("suffixable table comprehension dot index", [[
return [@len = 3].len return [@len = 3].len
]], 3) ]], 3)
test("suffixable table comprehension array index", [[ test("suffixable table comprehension array index", [[
return [@len=3]["len"] return [@len=3]["len"]
]], 3) ]], 3)
-- let in condition expression -- let in condition expression
@ -823,6 +827,187 @@ test("safe prefixes, random chaining", [[
assert(f.l?:o?() == nil) 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 -- results
local resultCounter = {} local resultCounter = {}
local testCounter = 0 local testCounter = 0