Meta Lua 範例

lua-users home
wiki

這些是 MetaLua 語法擴充功能的範例。

後綴 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 } 

另請參閱 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}}
}
「!」作為「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 中加入 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 中的其他範例

Metalua 中包含其他範例


[*1] 使用與 Lua 相同的條款授權(MIT 授權)。--DavidManura

另請參閱


RecentChanges · 偏好設定
編輯 · 歷史
最後編輯日期為 2009 年 5 月 6 日上午 6:49 GMT (diff)