元 Lua 食谱

lua-users home
wiki

这些是 元 Lua 语法扩展的示例。

后缀 if/unless

语法

( `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}}
}
"!" 作为 "not" 的别名
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

标签和 goto

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 中的其他示例

Metalua 中包含其他示例


[*1] 采用与 Lua 本身相同的条款(MIT 许可)授权。--DavidManura

另请参阅


最近更改 · 偏好设置
编辑 · 历史记录
最后编辑于 2009 年 5 月 6 日上午 6:49 GMT (差异)