Created
June 3, 2017 15:53
-
-
Save jnwhiteh/4b7297414f68b75e7f8d703736a92feb to your computer and use it in GitHub Desktop.
An LPEG grammar for the IRC protocol
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
require("luarocks.require") | |
require("lpeg") | |
local v = lpeg.V | |
local c = lpeg.C | |
local grammar = { | |
"message", -- Initial rule | |
message = (v"COLON" * c(v"prefix"))^-1 * c(v"command") * c(v"params") * v"CRLF", | |
params = v"SPACE" * ((v"COLON" * v"trailing") + (v"middle" * v"params"))^-1, | |
nospcrlfcl = lpeg.R("\001\009", "\011\012", "\014\031", "\033\057", "\059\255"), | |
middle = v"nospcrlfcl" * (v"COLON" + v"nospcrlfcl")^0, | |
trailing = (v"COLON" + v"SPACE" + v"nospcrlfcl")^0, | |
prefix = (c(v"servername") * v"SPACE") + ((c(v"nickname") * (v"BANG" * c(v"user"))^-1 * (v"AT" * c(v"host"))^-1) * v"SPACE"), | |
command = (v"LETTER" ^ 1) + (v"DIGIT" * v"DIGIT" * v"DIGIT"), | |
triple = v"DIGIT" * (v"DIGIT"^-1) * (v"DIGIT"^-1), | |
servername = v"hostname", | |
host = v"hostname" + v"hostaddr", | |
hostname = v"shortname" * (v"DOT" * v"shortname")^0, | |
shortname = (v"LETTER" + v"DIGIT") * (v"LETTER" + v"DIGIT" + v"DASH")^0 * (v"LETTER" + v"DIGIT")^0, | |
hostaddr = v"ip4addr", | |
ip4addr = v"triple" * v"DOT" * v"triple" * v"DOT" * v"triple" * v"DOT" * v"triple", | |
nickname = (v"LETTER" + v"SPECIAL") * (v"LETTER" + v"DIGIT" + v"SPECIAL" + v"DASH")^-8, | |
user = lpeg.R("\001\009", "\011\012", "\014\031", "\033\063", "\065\255")^1, | |
CRLF = lpeg.P("\r\n"), | |
DASH = lpeg.P("-"), | |
USCORE = lpeg.P("_"), | |
DOT = lpeg.P("."), | |
SPACE = lpeg.P(" "), | |
COLON = lpeg.P(":"), | |
BANG = lpeg.P("!"), | |
AT = lpeg.P("@"), | |
LETTER = lpeg.R("az", "AZ"), | |
DIGIT = lpeg.R("09"), | |
SPECIAL = lpeg.S(";[]\\`_^{|}"), | |
} | |
parser = lpeg.P(grammar) | |
local function test(entry, subject, expected) | |
subject = subject | |
local nt = {} | |
for k, v in pairs(grammar) do | |
nt[k] = v | |
end | |
nt[1] = entry | |
local pattern, err = pcall(lpeg.P, nt) | |
if not pattern then | |
error("Failed to compile test for entry: " .. entry .. "(" .. tostring(err) .. ")") | |
end | |
local match = lpeg.match(err, subject) | |
assert(match, "Failed test for " .. entry .. " with '" .. subject .. "'") | |
if match ~= expected then | |
error("Failed a complete match for " .. entry .. " got " .. match .. " for " .. #subject .. " string " .. subject) | |
end | |
io.stdout:write(".") | |
end | |
local function fail(entry, subject) | |
local succ,err = pcall(test, entry, subject) | |
assert(not succ, "Did not fail when expected: " .. entry .. " with " .. subject) | |
end | |
do | |
return | |
end | |
io.stdout:write("testing") | |
test("CRLF", "\r\n", 3) | |
test("COLON", ":", 2) | |
test("BANG", "!", 2) | |
test("AT", "@", 2) | |
test("LETTER", "Monkey", 2) | |
test("DIGIT", 31337, 2) | |
test("prefix", "MoNkEyS ", 9) | |
test("command", "PRIVMSG", 8) | |
test("nickname", "Cladhaire", 10) | |
test("nickname", "{clad|foo}", 10) | |
test("hostname", "wowforge.com", 13) | |
test("host", "wowforge.com", 13) | |
test("hostaddr", "128.256.232.122", 16) | |
test("command", "PING", 5) | |
test("params", " :anthony.freenode.net", 23) | |
test("message", "PING :anthony.freenode.net\r\n", 29) | |
test("nickname", "Aelo", 5) | |
test("user", "~Aeolbin", 9) | |
test("host", "81.170.1.150", 13) | |
test("prefix", "anthony.freenode.net ", 22) | |
test("prefix", "[email protected] ", 28) | |
test("COLON", ":", 2) | |
test("SPACE", " ", 2) | |
test("nickname", "Aelo", 5) | |
test("BANG", "!", 2) | |
test("user", "~Aelobin", 9) | |
test("AT", "@", 2) | |
test("host", "81.170.1.150", 13) | |
test("command", "PRIVMSG", 8) | |
test("middle", "#wowui", 7) | |
test("params", " #wowui :rawr", 14) | |
test("message", ":[email protected] PRIVMSG #wowui :rawr\r\n", 51) | |
-- Sanity | |
fail("LETTER", "1") | |
print(" complete!") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment