Skip to content

Instantly share code, notes, and snippets.

@tacigar
Last active July 4, 2022 14:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tacigar/93b30931c879cd8a9b12380724b956aa to your computer and use it in GitHub Desktop.
Save tacigar/93b30931c879cd8a9b12380724b956aa to your computer and use it in GitHub Desktop.
Lua製PEG「LPeg」を触ってみた.
--[[
LPegはPEG(Parsing Expression Grammar)のLua実装.
文脈自由文法と違いPEGでは「これらのうちどれか」ではなく,「最初の解析が
うまくいったらそれを, 失敗なら次を順に試してゆき, 成功したものを採用」する.
install: `luarocks install lpeg`.
]]--
LPeg = require "lpeg"
-- SはSetのSで, 文字列内のどれかの文字にマッチ.
-- patt^0はpatt*と同じ.
space = LPeg.S " \n\t"^0
-- CはCaptureのCで, マッチしたものを取り出し.
-- patt^-1はpatt?と同じで, 省略可能を意味する.
-- RはRangeのRで, その間の文字全てにマッチ.
-- "09"ならば'0'-'9'にマッチ.
-- patt^1はpatt+と同じ.
-- patt*pattでパターンを繋げる.
number = LPeg.C(LPeg.P "-"^-1 * LPeg.R("09")^1) * space
termop = LPeg.C(LPeg.S "+-") * space
factop = LPeg.C(LPeg.S "*/") * space
open = "(" * space
close = ")" * space
-- VはVariableのVで, 非終端記号を表す.
-- 作成された記号は, 文法で引数vによってインデックス付けされたルールを参照.
exp = LPeg.V "exp"
term = LPeg.V "term"
fact = LPeg.V "fact"
-- LPeg.Pは引数のタイプで動作が変わる.
-- テーブルが渡された場合, 文法として解釈される.
G = LPeg.P {
exp, -- tbl[1]は開始記号.
-- Ctはパターンに出てくるキャプチャを全てテーブルに押し込んで返す.
exp = LPeg.Ct(term * (termop * term) ^ 0),
term = LPeg.Ct(fact * (factop * fact) ^ 0),
-- patt1+patt2はpatt1またはpatt2にマッチ.
fact = number + open * exp * close,
}
-- LPeg.P(n)はn文字に一致を意味する.
-- -pattはpattと一致しないパターン.
-- LPeg.P(1)は一文字と一致.
-- -LPeg.P(1)は一文字もない. -> 文字列の終端にのみ一致.
-- -LPeg.P(1) == LPeg.P(-1) == -1
G = space * G * -1
function eval(x)
if type(x) == "string" then
return tonumber(x)
end
local op1 = eval(x[1])
for i = 2, #x, 2 do
local op = x[i]
local op2 = eval(x[i + 1])
if (op == "+") then op1 = op1 + op2
elseif (op == "-") then op1 = op1 - op2
elseif (op == "*") then op1 = op1 * op2
elseif (op == "/") then op1 = op1 / op2
end
end
return op1
end
function evalExp(s)
local t = LPeg.match(G, s)
if not t then
error("syntax error", 2)
end
return eval(t)
end
print(evalExp "3 + 5*9 / (1+1) - 12")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment