Last active
July 4, 2022 14:27
-
-
Save tacigar/93b30931c879cd8a9b12380724b956aa to your computer and use it in GitHub Desktop.
Lua製PEG「LPeg」を触ってみた.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
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