Meta Lua Recipes |
|
Syntax:
( `return´ explist? | `break´ ) ( ( `if´ | `unless´ ) expr )?
Example:
-{ extension 'ifpost' }
for _,v in ipairs(t) do
break if v > 10
break unless v <= 10 -- equivalent
return 1,2 if v == 5
return 1,2 unless v ~= 5 -- equivalent
end
Implementation: [*1]
-- extension/ifpost.mlua -- Supports postfix if/unless syntax -- ( return <exprlist>? | break ) ( ( if | unless ) <expr> )? -- similar to as in Perl. -- Note: this does not conflict with Lua syntax since return/break -- must be at the end of a block. local function returnif_builder (x) local r, cond = unpack(x) if cond then return +{stat: if -{cond[1]} then -{ `Return{unpack(r)} } end } else return `Return{unpack(r)} end end local function breakif_builder(x) local cond = unpack(x) if cond then return +{block: if -{cond[1]} then break end} else return +{block: break } end end local function unless_builder(x) local expr = unpack(x) return +{ not( -{expr} ) } end -- based on 'return' in mlp.stat. local block_terminators = { "if", "unless", -- added "else", "elseif", "end", "until", ")", "}", "]" } local return_expr_list_parser = gg.list { mlp.expr, separators = ",", terminators = block_terminators } mlp.lexer:add 'unless' mlp.stat:del 'return' mlp.stat:add{ "return", return_expr_list_parser, gg.multisequence { {"if", mlp.expr}, {"unless", gg.sequence { mlp.expr, builder=unless_builder } }, }, builder = returnif_builder } mlp.stat:del 'break' mlp.stat:add{ "break", gg.multisequence { {"if", mlp.expr}, {"unless", gg.sequence { mlp.expr, builder=unless_builder } }, }, builder = breakif_builder }
Syntax:
exp ::= var `=´ exp
Example:
-{ extension 'assignmentexpressions' }
local x = t[k] or (t[k] = {})
-- equivalent to
local x = t[k]
if not x then x = {}; t[k] = x end
Implementation: [*1]
-- extension/assignmentexpressions.mlua local function builder (op1, _, op2) local v = mlp.gensym() local s = `Set{ { op1 }, {v} } return `Stat{ +{block: local -{v} = -{op2}; -{s} }, v } end mlp.expr.infix:add{ '=', prec=10, assoc='right', builder = builder }
See also StatementsInExpressions.
Syntax:
stat ::= exp
Example:
-{ extension 'expressionstatements' }
f() or error 'failed!'
Implementation: [*1]
-- extension/expressionstatements.mlua -- We will overwrite mlp.stat.default, which normally handles -- assignments and function call statements (assign_or_call_stat_parser). -- To avoid breaking assignments, we'll make assignments be -- expressions (which are in turn here made statements). -- Function calls, on the other hand, are already expressions. extension 'assignmentexpressions' local function builder (expr) local v = mlp.gensym() return +{block: local -{v} = -{expr[1]} } end mlp.stat.default = gg.sequence{mlp.expr, builder = builder }
See also ExpressionsAsStatements.
Syntax:
`${´ expr `}´ (embedded in string literal)
Notice that this version of string interpolation has an edge over other solutions: interpolation is done at compile-time, not run-time, so interpolated stuff are compiled only once. -- FabienFleutot.
Example:
-{ extension 'stringinterpolation' }
local x = 5
print("test ${x+2} asdf") --> 7
Implementation:[*1]
-- extension/stringinterpolation.mlua local function makeparser(f) return function(...) local res = f(...) if res and res.tag == 'String' then local s = res[1] local expr -- note: left-associative. desirable? local function concat(o) if not expr then expr = o else expr = `Op{'concat', expr, o} end end local i = 1 local _ = s:gsub('(.-)$(%b{})()', function(text, var, pos) var = var:sub(2, var:len()-1) if text ~= '' then concat(`String{text}) end local expr2 = mlp.expr:parse(mlp.lexer:newstream(var)) concat( expr2 ) i = pos end ) local rest = s:sub(i) if rest ~= '' then concat(`String{rest}) end expr = expr or `String '' return expr end return res end end mlp.expr.primary.default = makeparser(mlp.expr.primary.default) mlp.expr.suffix.default.parse = makeparser(mlp.expr.suffix.default.parse)
See also StringInterpolation.
Syntax:
`$´ (`\r´ | `\n´) ... `$´ (embedded in string literal)
Example:
-{ extension 'stringbreaks' }
print [[This is a very long sentence $
$that spans multiple lines and $
$is very long and spans multiple $
$lines.]]
Prints "This is a very long sentence that spans multiple lines and is very long and spans multiple lines." (on one line).
Implementation:[*1]
-- extension/stringbreaks.mlua -- http://lua-users.org/lists/lua-l/2008-01/msg00790.html local function makeparser(f) return function(...) local res = f(...) if res and res.tag == 'String' then local s = res[1] s = s:gsub("%$[\r\n].-%$", "") return `String{s} end return res end end mlp.expr.primary.default = makeparser(mlp.expr.primary.default) mlp.expr.suffix.default.parse = makeparser(mlp.expr.suffix.default.parse)
Based on suggestion in LuaList:2008-01/msg00790.html .
Syntax:
stat ::= `infixoperator´ Name
Example:
-{ extension 'infixoperator' }
local function plus(x,y) return x+y end
infixoperator plus
print(2 plus 3)
Implementation: [*1]
-- extension/infixoperator.mlua local function builder (id, prec) local name = id[1][1] mlp.lexer:add(name) local function b(op1, _, op2) return `Call{id[1], op1, op2} end mlp.expr.infix:add {name, prec=50, assoc='left', builder = b} return +{block: } end mlp.lexer:add 'infixoperator' mlp.stat:add {'infixoperator', mlp.id, builder = builder}
This example could be extended. See also CustomOperators.
Metalua has a native way to use functions at infix positions, borrowed from Haskell: a function name framed between backquotes is an infix, left-associative operator. For instance:
function plus(a,b) return a+b end c = 2 `plus` 2 assert(c==4)
Syntax: identifiers that only contain capital letters and underscores are interpreted as constants that should not be writable to.
Example:
-{ extension 'const' }
local y
local MAX_SIZE = 10
x,y = 1,2 -- ok
print(MAX_SIZE)
MAX_SIZE = 11 -- raises compile time error (writing to constant)
Implementation: [*1]
-- extension/const.mlua local function check(o) if o and o.tag == 'Id' and o[1]:match('^[A-Z_]+$') then error('error: writing to constant ' .. o[1] .. ' at line ' .. o.line, 3) end end local function const_transformer(ast) if not ast then return end if ast.tag == 'Set' then for _,v in ipairs(ast[1]) do check(v) end end end mlp.stat.transformers:add(const_transformer)
This example is rudimentary and could be extended.
Additional examples are included in Metalua: