local pprint = require "pprint" local None = {} local whitespace = { [' '] = true, ['\n'] = true, ['\r'] = true, ['\t'] = true, } local yield, wrap = coroutine.yield, coroutine.wrap local function gen(t) return wrap(function() for i, v in ipairs(t) do yield(i, v) end end) end local function iter(f) return function(arg) return wrap(function () f(arg) end) end end local function chars(string) local cs = {} for char in string:gmatch(".") do table.insert(cs, char) end return cs end local function copy(t) local t_copy = {} for k, v in pairs(t) do t_copy[k] = v end return t_copy end local function append(a, b) local appended = {} for _, v in ipairs(a) do table.insert(appended, v) end for _, v in ipairs(b) do table.insert(appended, v) end return appended end local function clear(t) for k, v in pairs(t) do t[k] = nil end end local function insert(t, what) table.insert(t, what) return t end local parser_container = iter(function(string) local left = {} local right = {} local target = left local characters = gen(insert(chars(string), None)) local delimiter = None for _, character in characters do if not whitespace[character] then delimiter = character break end end if delimiter == None then return None end for _, character in characters do if character == delimiter or character == None then if target == left then target = right else target = left yield(copy(left), copy(right)) clear(left) clear(right) end else table.insert(target, character) end end end) local parse_labels = iter(function(rules) local left = {} local right = {} local pair = {{}, {}} local target = left local delimiter = None for condition, result in rules do for _, side in ipairs({condition, result}) do local characters = gen(insert(side, None)) local state = "find delimiter" for _, character in characters do if character == None then local label = table.concat(pair[1]):match "^%s*(.-)%s*$" local pattern = table.concat(pair[2]):match "^%s*(.-)%s*$" if not (#label == 0 and #pattern == 0) then table.insert(target, {label, pattern}) end clear(pair[1]) clear(pair[2]) break elseif state == "find delimiter" then if not whitespace[character] then delimiter = character state = "parse label" end elseif state == "parse label" then if character == delimiter then state = "parse pattern" else table.insert(pair[1], character) end elseif state == "parse pattern" then if character == delimiter or character == None then state = "parse label" local label = table.concat(pair[1]):match "^%s*(.-)%s*$" local pattern = table.concat(pair[2]):match "^%s*(.-)%s*$" if not (#label == 0 and #pattern == 0) then table.insert(target, {label, pattern}) end clear(pair[1]) clear(pair[2]) else table.insert(pair[2], character) end end end if target == left then target = right else target = left end end yield(copy(left), copy(right)) clear(left) clear(right) end end) local parse_patterns = iter(function(rules) for conditions, results in rules do local left = {} local right = {} local preserves = {} for _, condition in ipairs(conditions) do local label, pattern = condition[1], condition[2] local preserve = false if pattern:match("%?$") then pattern = pattern:sub(1, -2) preserve = true end if pattern:match("^[\"'](.*)[\"']$") then for i, character in ipairs(chars(pattern:match("^[\"'](.*)[\"\']$"))) do table.insert(left, {label, {tostring(string.byte(character))}}) if preserves then table.insert(right, {label, {tostring(string.byte(character))}}) end end elseif pattern:match("^%((.*)%)$") then for token in pattern:match("^%((.*)%)$"):gmatch("%S+") do table.insert(left, {label, {token}}) if preserve then table.insert(right, {label, {token}}) end end elseif pattern:find("^%.") then -- TODO else local words = {} for word in pattern:gmatch("%S+") do table.insert(words, word) end table.insert(left, {label, words}) if preserve then table.insert(preserves, {label, words}) end end end for _, result in ipairs(results) do local label, pattern = result[1], result[2] if pattern:match("^[\"'](.*)[\"']$") then for i, character in ipairs(chars(pattern:match("^[\"'](.*)[\"\']$"))) do table.insert(right, {label, {tostring(string.byte(character))}}) end elseif pattern:match("^%((.*)%)$") then for token in pattern:match("^%((.*)%)$"):gmatch("%S+") do table.insert(right, {label, {token}}) end elseif pattern:find("^%.") then -- TODO else local words = {} for word in pattern:gmatch("%S+") do table.insert(words, word) end table.insert(right, {label, words}) end end yield(copy(left), append(right, preserves)) clear(left) clear(right) clear(preserves) end end) local file = io.open("main.nv", "r") for left, right in parse_patterns(parse_labels(parser_container(file:read("*a")))) do io.write("|") local left_pattern = {} for _, pattern in ipairs(left) do table.insert(left_pattern, ":" .. pattern[1] .. ": " .. table.concat(pattern[2], " ")) end io.write(table.concat(left_pattern, " ")) io.write("|\n") for _, pattern in ipairs(right) do print("", ":" .. pattern[1] .. ": " .. table.concat(pattern[2], " ")) end io.write("\n") end