元 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 }
另请参阅 表达式中的语句。
语法
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 }
另请参阅 表达式作为语句。
语法
`${� 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)
另请参阅 字符串插值。
语法
`$� (`\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.]]
打印 "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.lua.ac.cn/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}
此示例可以扩展。另请参阅 自定义运算符。
(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 中添加一个运算符->构建器条目
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 中包含其他示例