7f09824e89
Not very well tested, needs a better public API, not integrated with Minetest, ... This is WAY bigger than a commit should be. The next stages will be: - unit tests - API (allows it to be more than just a language for this project) - integration with Minetest
184 lines
4.2 KiB
Lua
184 lines
4.2 KiB
Lua
-- parse types
|
|
--[[
|
|
quote
|
|
identifier
|
|
code
|
|
map
|
|
string
|
|
number
|
|
symbol
|
|
--]]
|
|
|
|
|
|
local function sublist(list, istart, iend, inclusive)
|
|
local o = {}
|
|
local oi = 1
|
|
|
|
inclusive = inclusive or false
|
|
|
|
for i, v in ipairs(list) do
|
|
iend = iend or 0 -- idk how but iend can become nil
|
|
|
|
local uninc = i > istart and i < iend
|
|
local incl = i >= istart and i <= iend
|
|
|
|
if (inclusive and incl) or (not inclusive and uninc) then
|
|
o[oi] = v
|
|
oi = oi + 1
|
|
end
|
|
end
|
|
|
|
return o
|
|
end
|
|
|
|
|
|
local parse
|
|
|
|
local function parse_peek(state)
|
|
return state.lexed[state.position]
|
|
end
|
|
|
|
local function parse_next(state)
|
|
local n = parse_peek(state)
|
|
state.position = state.position + 1
|
|
return n
|
|
end
|
|
|
|
local function parse_map(state)
|
|
local map = {}
|
|
local mapi = 1
|
|
|
|
if parse_next(state).type ~= "map_open" then
|
|
return nil -- ERROR
|
|
end
|
|
|
|
while true do
|
|
local n = parse_next(state)
|
|
local skip = false -- lua has no continue, 5.1 has no goto
|
|
|
|
if n == nil then
|
|
return nil -- ERROR
|
|
end
|
|
|
|
if n.type == "map_close" then
|
|
break
|
|
elseif n.type == "literal" and (n.subtype == "identifier" or n.subtype == "string") then
|
|
local key = n.value
|
|
local mr = parse_peek(state)
|
|
|
|
if mr.type == "map_relation" then
|
|
parse_next(state)
|
|
local nval = parse({parse_next(state)})
|
|
|
|
if nval == nil then
|
|
return nil -- ERROR
|
|
end
|
|
|
|
map[key] = nval[1]
|
|
skip = true
|
|
end
|
|
end
|
|
|
|
if not skip then
|
|
local nval = parse({n})
|
|
|
|
if nval == nil then
|
|
return nil -- ERROR
|
|
end
|
|
|
|
map[mapi] = nval[1]
|
|
mapi = mapi + 1
|
|
end
|
|
end
|
|
|
|
return {type = "map", value = map}
|
|
end
|
|
|
|
local function parse_find_matching(state, open, close)
|
|
local level = 1
|
|
|
|
parse_next(state) -- skip beginning
|
|
|
|
while level ~= 0 do
|
|
local n = parse_next(state)
|
|
if n == nil then
|
|
return nil -- ERROR
|
|
elseif n.type == open then
|
|
level = level + 1
|
|
elseif n.type == close then
|
|
level = level - 1
|
|
end
|
|
end
|
|
|
|
return state.position - 1
|
|
end
|
|
|
|
local function parse_code(state, open, close)
|
|
local istart = state.position
|
|
local iend = parse_find_matching(state, open, close)
|
|
|
|
return {
|
|
type = "code",
|
|
value = parse(sublist(state.lexed, istart, iend))
|
|
}
|
|
end
|
|
|
|
local function parse_step(state)
|
|
local n = parse_peek(state)
|
|
|
|
if n == nil then
|
|
return nil
|
|
elseif n.type == "code_open" then
|
|
return parse_code(state, "code_open", "code_close")
|
|
elseif n.type == "code_e_open" then
|
|
return parse_code(state, "code_e_open", "code_e_close")
|
|
-- also return run
|
|
elseif n.type == "map_open" then
|
|
local istart = state.position
|
|
local iend = parse_find_matching(state, "map_open", "map_close")
|
|
return parse_map({lexed = sublist(state.lexed, istart, iend, true), position = 1})
|
|
elseif n.type == "literal" then
|
|
if n.subtype == "number" then
|
|
parse_next(state)
|
|
return {type = "number", value = tonumber(n.value)}
|
|
elseif n.subtype == "string" then
|
|
parse_next(state)
|
|
return {type = "string", value = n.value}
|
|
elseif n.subtype == "identifier" then
|
|
parse_next(state)
|
|
return {type = "identifier", value = n.value}
|
|
elseif n.subtype == "quote" then
|
|
parse_next(state)
|
|
return {type = "quote", value = n.value}
|
|
end
|
|
elseif n.type == "symbol" then
|
|
parse_next(state)
|
|
return {type = "symbol", value = n.value}
|
|
end
|
|
end
|
|
|
|
|
|
-- parse
|
|
parse = function(lexed)
|
|
local state = {lexed = lexed, position = 1}
|
|
local tree = {}
|
|
local treei = 1
|
|
|
|
while true do
|
|
local n = parse_step(state)
|
|
|
|
if n == nil then
|
|
if state.position <= #state.lexed then
|
|
return nil
|
|
else
|
|
return tree
|
|
end
|
|
end
|
|
|
|
tree[treei] = n
|
|
treei = treei + 1
|
|
end
|
|
end
|
|
|
|
return parse
|