Meta Lua 範例 |
|
語法
( `return� explist? | `break� ) ( ( `if� | `unless� ) expr )?
範例
-{ 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
實作:[*1]
-- metalua/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 local return_expr_list_parser = gg.list { mlp.expr, separators = ",", terminators = mlp.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 }
語法
exp ::= var `=� exp
範例
-{ extension 'assignmentexpressions' } local x = t[k] or (t[k] = {}) -- equivalent to local x = t[k] if not x then x = {}; t[k] = x end
實作:[*1]
-- metalua/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 }
另請參閱 StatementsInExpressions。
語法
stat ::= exp
範例
-{ extension 'expressionstatements' } f() or error 'failed!'
實作:[*1]
-- metalua/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 }
另請參閱 ExpressionsAsStatements。
語法
`${� expr `}� (embedded in string literal)
請注意,這個版本的字串插補比其他解決方案好一些:插補是在編譯時完成,而非執行時,因此插補的內容僅編譯一次。-- FabienFleutot。
範例
-{ extension 'stringinterpolation' } local x = 5 print("test ${x+2} asdf") --> 7
實作:[*1]
-- metalua/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)
另請參閱 StringInterpolation。
語法
`$� (`\r� | `\n�) ... `$� (embedded in string literal)
範例
-{ extension 'stringbreaks' } print [[This is a very long sentence $ $that spans multiple lines and $ $is very long and spans multiple $ $lines.]]
印出「這是一個很長的句子,跨越多行,而且很長,跨越多行。」(單一行)。
實作:[*1]
-- metalua/extension/stringbreaks.mlua -- https://lua-users.dev.org.tw/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)
基於 LuaList:2008-01/msg00790.html 的建議。
語法
stat ::= `infixoperator� Name
範例
-{ extension 'infixoperator' } local function plus(x,y) return x+y end infixoperator plus print(2 plus 3)
實作:[*1]
-- metalua/extension/infixoperator.mlua local function builder (id, prec) mlp.lexer:add(id[1][1]) -- turn infix opname into a keyword mlp.expr.infix:add {id[1][1], prec=50, assoc='left', builder = |op1, _, op2| `Call{id[1], op1, op2} } return +{block: } end mlp.lexer:add 'infixoperator' mlp.stat:add {'infixoperator', mlp.id, builder = builder}
這個範例可以擴充。另請參閱 CustomOperators。
(Fabien :) Metalua 有自己原生的方法可以使用函數在中綴位置,這從 Haskell 借用來的:用反引號框起來的函數名稱是一個中綴、左結合運算子。例如
function plus(a,b) return a+b end c = 2 `plus` 2 assert(c==4)
Metalua 已經從 C 中定義了一些有用的運算子:+=、-=、/=、*=。新的運算子可以輕易地加入
「!=」作為「~=」更熟悉的別名
mlp.lexer:add "!=" mlp.expr.infix:add { '!=', prec = 30, builder = |a, _, b| +{-{a} ~= -{b}} }
mlp.lexer:add "!" mlp.expr.prefix:add { '!', prec = 80, builder = |_, a| +{not -{a}} }
mlp.lexer:add "&&" mlp.expr.infix:add { '&&', prec = 20, builder = |a, _, b| +{-{a} and -{b}} } mlp.lexer:add "||" mlp.expr.infix:add { '||', prec = 10, builder = |a, _, b| +{-{a} or -{b}} }
還有一個定義新賦值運算子的標準方法:在表單 mlp.stat.assignments 中加入 operator->builder 條目
mlp.keywords.add "|=" mlp.stat.assignments["|="] = function (left_expr_list, right_expr_list) assert (#left_expr_list==1 and #right_expr_list==1) local left, right = left_expr_list[1], right_expr_list[1] return -{stat: (-{left}) = -{left} or -{right} } end
mlp.lexer:add "label" mlp.stat:add { "label", mlp.string, builder = |a| `Label{a[1]} } mlp.lexer:add "goto" mlp.stat:add { "goto", mlp.string, builder = |a| `Goto{a[1]} }
範例
goto "foo" print "you won't see this" label "foo"
語法:僅包含大寫字母和底線的識別碼會被解釋為常數,不應該可寫入。
範例
-{ 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)
實作:[*1]
-- metalua/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)
這個範例很基礎,可以擴充。
Metalua 中包含其他範例