-- 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