summary refs log tree commit diff stats
path: root/parser.lua
diff options
context:
space:
mode:
authorSoniEx2 <endermoneymod@gmail.com>2019-03-31 11:02:57 -0300
committerSoniEx2 <endermoneymod@gmail.com>2019-03-31 11:02:57 -0300
commitd03d77d28b812244be66763356f24659da769f05 (patch)
treecef5cff3fc7dcfc1f0794633d7c835c8938c3a6b /parser.lua
Parser seems to work?
Diffstat (limited to 'parser.lua')
-rw-r--r--parser.lua119
1 files changed, 119 insertions, 0 deletions
diff --git a/parser.lua b/parser.lua
new file mode 100644
index 0000000..479d80a
--- /dev/null
+++ b/parser.lua
@@ -0,0 +1,119 @@
+--[[
+    parser.lua - table based parsing
+    Copyright (C) 2019  Soni L.
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+--]]
+
+-- key for STATE
+local STATE = {}
+-- key for DATA
+local DATA = {}
+-- key for GENERATOR
+local GEN = {}
+-- key for DATA OFFSET
+local OFFDATA = {}
+
+local type, tostring
+    = type, tostring
+
+local function get_next_common(state, in_pos, token)
+    -- note: must preserve "token" - do not call recursively with a different token
+    local transition
+    if state[STATE] ~= nil then
+        transition = state[STATE][token]
+        if not transition then
+            transition = state[STATE][""]
+        end
+        local recheck = true
+        while recheck do
+            recheck = false
+            local tytrans = type(transition)
+            if tytrans == "string" then
+                transition = state[STATE][transition]
+                recheck = true
+            elseif tytrans == "function" then
+                transition = transition(state, token)
+                recheck = true
+            end
+        end
+        state[STATE] = transition -- may be nil
+    end
+    -- must NOT use elseif here - the above may set state to nil!
+    if state[STATE] == nil then
+        -- unexpected token. stream consumer may attempt to recover,
+        -- but we do this mostly to differentiate it from "end of stream" condition.
+        return in_pos - 1, nil, "unexpected token", token, state
+    end
+    return in_pos, state, transition -- TODO is this what we should be returning?
+end
+
+local function get_next_table(state, in_pos)
+    if state[DATA] == nil or #state[DATA] == 0 then return in_pos, state end
+    in_pos = in_pos + 1
+    local token = state[DATA][in_pos - state[OFFDATA]]
+    if token == nil then
+        state[OFFDATA] = in_pos - 1
+        state[DATA] = state[GEN]()
+        return get_next_table(state, state[OFFDATA])
+    end
+    return get_next_common(state, in_pos, token)
+end
+
+local function get_next_string(state, in_pos)
+    if state[DATA] == nil or #state[DATA] == 0 then return in_pos, state end
+    in_pos = in_pos + 1
+    local token = state[DATA]:sub(in_pos - state[OFFDATA], in_pos - state[OFFDATA])
+    if token == "" then
+        state[OFFDATA] = in_pos - 1
+        state[DATA] = state[GEN]()
+        return get_next_string(state, state[OFFDATA])
+    end
+    return get_next_common(state, in_pos, token)
+end
+
+local function stream(defs, data)
+    local state = {}
+    local fn
+    state[STATE] = defs
+    if type(data) == "string" then
+        state[DATA] = data
+        state[GEN] = function() end
+        fn = get_next_string
+    else
+        state[DATA] = data()
+        state[GEN] = data
+        fn = type(state[DATA]) == "string" and get_next_string or get_next_table
+    end
+    state[OFFDATA] = 0
+    return fn, state, state[OFFDATA]
+end
+
+local function parse(defs, data)
+    for pos, state, transemsg, etoken, estate in stream(defs, data) do
+        if not state then
+            -- parse error
+            return nil, transemsg, etoken, estate
+        elseif not transemsg then
+            -- parse success (maybe) - caller needs to check state[STATE] against what it considers a successful state
+            return state
+        end
+    end
+end
+
+return {
+    STATE = STATE,
+    stream = stream,
+    parse = parse,
+}