Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit d17fcf2
Showing
2 changed files
with
222 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
local stackish = require("stackish") | ||
|
||
local print_table = stackish.print_table | ||
local parse = stackish.parse | ||
|
||
print_table(parse([[ [ "abcd" [ "1234 " 5678 454.234 root things ]])) | ||
print_table(parse([[ [ [ "hello" 1 child root ]])) |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
local quotes = { ["\""] = true, | ||
["\'"] = true, | ||
} | ||
|
||
local numerals = { ["0"] = true | ||
, ["1"] = true | ||
, ["2"] = true | ||
, ["3"] = true | ||
, ["4"] = true | ||
, ["5"] = true | ||
, ["6"] = true | ||
, ["7"] = true | ||
, ["8"] = true | ||
, ["9"] = true | ||
} | ||
|
||
local whitespace = { [""] = true | ||
, [" "] = true | ||
, ["\t"] = true | ||
, ["\n"] = true | ||
, ["\r"] = true | ||
} | ||
|
||
local types = { mark = { name = "mark" } | ||
, string = { name = "string" } | ||
, number = { name = "number" } | ||
, word = { name = "word" } | ||
} | ||
|
||
do | ||
local function mktype(t, value) | ||
return { value = value | ||
, __type = types[t] | ||
} | ||
end | ||
|
||
for i, t in pairs({ "mark", "string", "number", "word" }) do | ||
types["mk"..t] = function(value) return mktype(t, value) end | ||
end | ||
|
||
end | ||
|
||
local function print_element(v) | ||
print("el", v.__type.name, v.value) | ||
end | ||
|
||
local function print_stack(t) | ||
for i,v in pairs(t) do | ||
print_element(v) | ||
end | ||
end | ||
|
||
local function print_table(t, depth) | ||
if not depth then | ||
depth = 0 | ||
end | ||
|
||
for i,v in pairs(t) do | ||
if type(v) == "table" then | ||
print(string.rep(" ", depth)..tostring(i)..":") | ||
print_table(v, depth+1) | ||
elseif type(v) == "string" then | ||
print(string.rep(" ", depth).."\""..v.."\"") | ||
else | ||
print(string.rep(" ", depth)..tostring(v)) | ||
end | ||
end | ||
end | ||
|
||
local function peak(s) | ||
if #s == 0 then | ||
return nil | ||
else | ||
return s:sub(1,1) | ||
end | ||
end | ||
|
||
local function tail(s) | ||
return s:sub(2, #s) | ||
end | ||
|
||
local function chomp(s) | ||
local p = peak(s) | ||
|
||
if whitespace[p] then | ||
return chomp(tail(s)) | ||
else | ||
return s | ||
end | ||
end | ||
|
||
local function take_until(input, u, output) | ||
c = peak(input) | ||
assert(c, "not found: "..u) | ||
|
||
if not output then | ||
output = "" | ||
end | ||
|
||
if c == u then | ||
return output, tail(input) | ||
else | ||
return take_until(tail(input), u, output..c) | ||
end | ||
end | ||
|
||
local function tokenize_string(input) | ||
local c = peak(input) | ||
assert(quotes[c], "not a string") | ||
local str, rest = take_until(tail(input), c) | ||
return types.mkstring(str), rest | ||
end | ||
|
||
local function tokenize_number(input, output) | ||
if not output then | ||
output = "" | ||
end | ||
|
||
p = peak(input) | ||
|
||
if (p == nil) or whitespace[p] then | ||
return types.mknumber(tonumber(output)), tail(input) | ||
else | ||
assert(numerals[p] or (p == "."), "Invalid number") | ||
return tokenize_number(tail(input), output..p) | ||
end | ||
end | ||
|
||
local function tokenize_word(input, output) | ||
if not output then | ||
output = "" | ||
end | ||
|
||
p = peak(input) | ||
|
||
if (p == nil) or whitespace[p] then | ||
return types.mkword(output), tail(input) | ||
else | ||
return tokenize_word(tail(input), output..p) | ||
end | ||
end | ||
|
||
local function tokenize(input, stack) | ||
if not stack then | ||
stack = {} | ||
end | ||
|
||
local c = peak(input) | ||
|
||
if not c then | ||
return stack | ||
end | ||
|
||
if c == "[" then | ||
table.insert(stack, types.mkmark(c)) | ||
return tokenize(tail(input), stack) | ||
elseif whitespace[c] then | ||
return tokenize(chomp(input), stack) | ||
elseif quotes[c] then | ||
local str, rest = tokenize_string(input) | ||
table.insert(stack, str) | ||
return tokenize(rest, stack) | ||
elseif numerals[c] then | ||
local number, rest = tokenize_number(input) | ||
table.insert(stack, number) | ||
return tokenize(rest, stack) | ||
else | ||
word, rest = tokenize_word(input) | ||
table.insert(stack, word) | ||
return tokenize(rest, stack) | ||
end | ||
end | ||
|
||
local function map(itterable, f) | ||
res = {} | ||
for i,v in ipairs(itterable) do | ||
table.insert(res, f(v)) | ||
end | ||
return res | ||
end | ||
|
||
local function parse_ast(ast, _table, depth) | ||
if not depth then | ||
depth = 0 | ||
end | ||
|
||
if not _table then | ||
_table = {} | ||
end | ||
|
||
if #ast <= 0 then | ||
return _table | ||
end | ||
|
||
local current = table.remove(ast) | ||
|
||
if current.__type == types.mark then | ||
return _table | ||
elseif current.__type == types.word then | ||
_table[current.value] = parse_ast(ast, {}, depth+1) | ||
return parse_ast(ast, _table, depth) | ||
else | ||
table.insert(_table, current.value) | ||
return parse_ast(ast, _table, depth) | ||
end | ||
end | ||
|
||
local function parse(input) | ||
return parse_ast(tokenize(input)) | ||
end | ||
|
||
return { print_table = print_table | ||
, parse = parse | ||
, tokenize = tokenize | ||
} |