技術戦略室間野です。好きなうまるちゃんは家うまるです。どなたか次回までにアフィリエイトどこに貼れば良いか教えてください。
これイマイチSEOスコアが低いので他の話をする予定でしたが、今まさにparser書いたのと、概ね全言語にPEGモジュールがあったので考え直しました。100行のPEGは100行のRegExよりだいぶ読める。
PEGとは
Perl6のsyntax parserです。
正規表現の代用
PEGで出来ることの99%は正規表現でも出来て、正規表現で出来ることの(略)PEGの実装は小さく、大抵Cのラッパなので速度もほぼ変わりませんが、どちらが上とかの話はしません。
ただしLuaおめえはダメだ
Luaの正規表現は特殊で、{}や|が無いせいで、ロジックまで変わってしまい、PCREに移植不能になることがあります。PEGならどこにでも持って行けるので、LuaだけはPEGへの全置換を検討しても良いでしょう。
Perlは全域で正規表現を一切使わなければバイナリが小さくなるので、Luaも速度的恩恵があるかも。ないかも。
ini parser
要lpeg
sudo luarocks install lpeg
実装
function toLines(str)
local lines = {}
for val in str:gmatch("(.-)\r?\n") do
table.insert(lines, val)
end
return lines
end
function decodeIni(str)
local lpeg = require "lpeg"
local sp = lpeg.P(" ") ^ 0 --0文字以上の空白
local bb = lpeg.P("[") -- [
local eb = lpeg.P("]") -- ]
local eq = lpeg.P("=") -- =
local cr = lpeg.P("\r") -- <CR>
local lf = lpeg.P("\n") -- <LF>
local key = (lpeg.P(1) - lpeg.P(" ") - eq) ^1
local value = lpeg.P(1) ^0
local ptSubject = sp * bb * lpeg.C((lpeg.P(1) - eb) ^1) * eb
local ptBody = sp * lpeg.C(key) * sp * eq * sp * lpeg.C(value)
local struct = {}
local parentName = nil
local lines = toLines(str)
local matched
for i,line in ipairs(lines) do
matched = lpeg.match(ptSubject, line)
if matched then
parentName = matched
if not struct[parentName] then struct[parentName] = {} end
else
ckey,val = lpeg.match(ptBody, line)
if ckey then
if parentName then
struct[parentName][ckey] = val
else
struct[ckey] = val
end
end
end
end
return struct
end
decodeIniに下のようなString型を食わせるとテーブルを返します。
PARENTNAME=parent
[api]
hostname = 192.168.1.111
port = 8080
authUri = /auth
[auth]
username = root
passwprd = rpwd
簡単な解説
リンク張って済ませるとこですが、イマイチ実装向きの解説がないので、PEGについて超簡単に解説します。
意味 | 正規表現 | |
---|---|---|
* | 連結 | |
+ | または | [] |
- | 除外 | [^] |
^ | 連続 | {} |
今回使ったのはこのぐらい。
(lpeg.P(1) - eb) ^1
の意味は以下です。
任意の1文字 ただし]以外 の1文字以上の連続
先頭から探索
正規表現で
はマッチしますが、PEGでは以下のように解釈されます。
"abc" =~ /b/
"abc" =~ /^b/
日本語(UTF8)
lpegでは日本語は単なる3文字(一部4文字)と見なされます。即ちlpeg.P(3)または(4)で日本語1文字です。
再利用性
PEGではなるべく再利用可能な記述でパターンを組み立てるのがスタンダードです。PCREで <h1>foo</h1> のfooを取り出す時
<h1>(.+?)</h1>
などとキャプチャしがちですが、PEGでは以下のようにタグを組み立てた方が後々幸せです。
lt = p("<")
gt = p(">")
h1 = p("h1")
openh1 = lt * h1 * gt
...