indentation parsing and if

This commit is contained in:
leaf corcoran 2011-05-18 00:45:27 -07:00
parent ee8e51c339
commit 2f09192a6a
4 changed files with 143 additions and 16 deletions

View File

@ -5,6 +5,10 @@ require"util"
require"lpeg"
require"moonscript.compile"
require"moonscript.dump"
require"moonscript.data"
local Stack = data.Stack
local function count_indent(str)
local sum = 0
@ -16,10 +20,11 @@ local function count_indent(str)
end
local R, S, V, P = lpeg.R, lpeg.S, lpeg.V, lpeg.P
local C, Ct = lpeg.C, lpeg.Ct
local C, Ct, Cmt = lpeg.C, lpeg.Ct, lpeg.Cmt
local Space = S" \t"^0
local Break = S"\n" + -1
local Break = S"\n"
local Stop = Break + -1
local Indent = C(S"\t "^0) / count_indent
local ArgDelim = "," * Space
@ -53,6 +58,14 @@ function mark(name)
end
end
function got(what)
return function(...)
print("got "..tostring(what))
return true
end
end
function flatten(tbl)
if #tbl == 1 then
return tbl[1]
@ -61,7 +74,7 @@ function flatten(tbl)
end
local build_grammar = wrap(function()
local err_msg = "Failed to compile, line:\n [%d] >> %s"
local err_msg = "Failed to compile, line:\n [%d] >> %s (%d)"
local line = 1
local function line_count(subject, pos, str)
for _ in str:gmatch("\n") do
@ -71,18 +84,48 @@ local build_grammar = wrap(function()
return true
end
local Space = lpeg.Cmt(Space, line_count)
local Break = lpeg.Cmt(Break, line_count)
local Space = Cmt(Space, line_count)
local Break = Cmt(Break, line_count)
local _indent = Stack(0) -- current indent
local function check_indent(str, pos, indent)
return _indent:top() == indent
end
local function advance_indent(str, pos, indent)
if indent > _indent:top() then
_indent:push(indent)
return true
end
end
local function pop_indent(str, pos)
if not _indent:pop() then error("unexpected outdent") end
return true
end
local keywords = {}
local function key(word)
keywords[word] = true
return word * Space
end
local g = lpeg.P{
Block,
Block = Ct(Line^0),
Line = Ct(Funcall) * Break,
Block = Ct((Line)^0),
Line = Break + Cmt(Indent, check_indent) * (Ct(If) + Exp * Stop),
InBlock = #Cmt(Indent, advance_indent) * Block * OutBlock,
OutBlock = Cmt(P(""), pop_indent),
Funcall = Name * ArgList / mark"fncall",
If = key"if" * Exp * Break * InBlock / mark "if",
ArgList = Ct(Exp * (ArgDelim * Exp)^0),
Exp = Ct(Value * (FactorOp * Value)^0) / flatten,
Value = Funcall + Num + Name
}
return {
_g = Space * g * Space * -1,
match = function(self, str, ...)
@ -95,7 +138,7 @@ local build_grammar = wrap(function()
local tree = self._g:match(str, ...)
if not tree then
return nil, err_msg:format(line, get_line(line))
return nil, err_msg:format(line, get_line(line), _indent:top())
end
return tree
end
@ -105,20 +148,27 @@ end)
local grammar = build_grammar()
local program = [[
if gogo bozango
print hello_world
if two_dads
do something
if yum
heckyes 23
print 2
print dadas
this is what a sentence does when you use it
]]
-- print hi + world + 2342
-- print 23424
-- ]]
local tree, err = grammar:match(program)
if not tree then error(err) end
print(util.dump(tree))
-- print(compile.tree(tree))
dump.tree(tree)
print""
print(compile.tree(tree))
local program2 = [[
if something

View File

@ -2,13 +2,21 @@
module("moonscript.compile", package.seeall)
require "util"
-- this doesn't work
-- setmetatable(_M, {
-- __call = setfenv(function(self, ...)
-- compile(...)
-- end, _G)
-- })
local indent_char = " "
local compilers = {
_indent = 0,
ichar = function(self)
return indent_char:rep(self._indent)
end,
fncall = function(self, node)
local _, name, args = unpack(node)
return name .. self:args(args)
@ -20,6 +28,21 @@ local compilers = {
end
return "(" .. table.concat(values, ", ") .. ")"
end,
["if"] = function(self, node)
local _, cond, block = unpack(node)
return ("if %s then\n%s\n%send"):format(
self:value(cond), self:block(block, 1), self:ichar())
end,
block = function(self, node, inc)
if inc then self._indent = self._indent + inc end
local lines = {}
local i = self:ichar()
for _, ln in ipairs(node) do
table.insert(lines, i..self:value(ln))
end
if inc then self._indent = self._indent - inc end
return table.concat(lines, "\n")
end,
value = function(self, node)
if type(node) == "table" then
local op = unpack(node)
@ -35,7 +58,7 @@ _M.tree = function(tree)
for _, line in ipairs(tree) do
local op = line[1]
local fn = compilers[op]
if not fn then error("Unknown op: "..op) end
if not fn then error("Unknown op: "..tostring(op)) end
table.insert(buff, compilers[op](compilers, line))
end

31
moonscript/data.lua Normal file
View File

@ -0,0 +1,31 @@
module("moonscript.data", package.seeall)
local stack_t = {}
local _stack_mt = { __index = stack_t, __tostring = function(self)
return "<Stack {"..table.concat(self, ", ").."}>"
end}
function stack_t:pop()
return table.remove(self)
end
function stack_t:push(value)
table.insert(self, value)
return value
end
function stack_t:top()
return self[#self]
end
function Stack(...)
local self = setmetatable({}, _stack_mt)
for _, v in ipairs{...} do
self:push(v)
end
return self
end

23
moonscript/dump.lua Normal file
View File

@ -0,0 +1,23 @@
module("moonscript.dump", package.seeall)
local function flat_value(op, depth)
depth = depth or 1
if type(op) == "string" then return '"'..op..'"' end
if type(op) ~= "table" then return tostring(op) end
local items = {}
for _, item in ipairs(op) do
table.insert(items, flat_value(item, depth+1))
end
return "{"..table.concat(items, ", ").."}"
end
function tree(block, depth)
depth = depth or 0
for _, op in ipairs(block) do
print(flat_value(op))
end
end