2017-09-12 21:38:54 -07:00
local re = require ( ' re ' )
local lpeg = require ( ' lpeg ' )
local utils = require ( ' utils ' )
local INDENT = " "
lpeg.setmaxstack ( 10000 )
local P , V , S , Cg , C , Cp , B , Cmt
P , V , S , Cg , C , Cp , B , Cmt = lpeg.P , lpeg.V , lpeg.S , lpeg.Cg , lpeg.C , lpeg.Cp , lpeg.B , lpeg.Cmt
local wordchar = P ( 1 ) - S ( ' \t \n \r %:;,.{}[]()" ' )
local comment = re.compile ( [[comment <- "(#" (comment / ((! "#)") .))* "#)"]] )
local whitespace = ( S ( " \t " ) + comment ) ^ 1
local nl = P ( " \n " )
local blank_line = whitespace ^ - 1 * nl
local get_line_indentation
get_line_indentation = function ( line )
local indent_amounts = {
[ " " ] = 1 ,
[ " \t " ] = 4
}
do
local sum = 0
local leading_space = line : gsub ( " ([ \t ]*).* " , " %1 " )
for c in leading_space : gmatch ( " [ \t ] " ) do
sum = sum + indent_amounts [ c ]
end
return sum
end
end
local make_parser
make_parser = function ( lingo , extra_definitions )
local indent_stack = {
0
}
local push
push = function ( n )
return table.insert ( indent_stack , n )
end
local pop
pop = function ( )
return table.remove ( indent_stack )
end
local check_indent
check_indent = function ( subject , end_pos , spaces )
local num_spaces = get_line_indentation ( spaces )
if num_spaces <= indent_stack [ # indent_stack ] then
return nil
end
push ( num_spaces )
return end_pos
end
local check_dedent
check_dedent = function ( subject , end_pos , spaces )
local num_spaces = get_line_indentation ( spaces )
if num_spaces >= indent_stack [ # indent_stack ] then
return nil
end
pop ( )
return end_pos
end
local check_nodent
check_nodent = function ( subject , end_pos , spaces )
local num_spaces = get_line_indentation ( spaces )
if num_spaces ~= indent_stack [ # indent_stack ] then
return nil
end
return end_pos
end
local defs = {
wordchar = wordchar ,
nl = nl ,
ws = whitespace ,
comment = comment ,
eol = # nl + ( P ( " " ) - P ( 1 ) ) ,
word_boundary = whitespace + B ( P ( " .. " ) ) + B ( S ( " \" ;)] " ) ) + # S ( " \" :([ " ) + # ( ( whitespace + nl ) ^ 0 * P ( " .. " ) ) ,
indent = # ( nl * blank_line ^ 0 * Cmt ( whitespace ^ - 1 , check_indent ) ) ,
dedent = # ( nl * blank_line ^ 0 * Cmt ( whitespace ^ - 1 , check_dedent ) ) ,
new_line = nl * blank_line ^ 0 * Cmt ( whitespace ^ - 1 , check_nodent ) ,
error_handler = function ( src , pos , errors )
local line_no = 1
for _ in src : sub ( 1 , -# errors ) : gmatch ( " \n " ) do
line_no = line_no + 1
end
local err_pos = # src - # errors + 1
if errors : sub ( 1 , 1 ) == " \n " then
err_pos = err_pos + # errors : match ( " [ \t ]* " , 2 )
end
local start_of_err_line = err_pos
while src : sub ( start_of_err_line , start_of_err_line ) ~= " \n " do
start_of_err_line = start_of_err_line - 1
end
local start_of_prev_line = start_of_err_line - 1
while src : sub ( start_of_prev_line , start_of_prev_line ) ~= " \n " do
start_of_prev_line = start_of_prev_line - 1
end
local prev_line , err_line , next_line = src : match ( " ([^ \n ]*) \n ([^ \n ]*) \n ([^ \n ]*) " , start_of_prev_line + 1 )
local pointer = ( " - " ) : rep ( err_pos - start_of_err_line + 0 ) .. " ^ "
return error ( " \n Parse error on line " .. tostring ( line_no ) .. " : \n \n " .. tostring ( prev_line ) .. " \n " .. tostring ( err_line ) .. " \n " .. tostring ( pointer ) .. " \n " .. tostring ( next_line ) .. " \n " )
end
}
if extra_definitions then
for k , v in pairs ( extra_definitions ) do
defs [ k ] = v
end
end
setmetatable ( defs , {
__index = function ( t , key )
local fn
fn = function ( src , value , errors )
local token = {
type = key ,
src = src ,
value = value ,
errors = errors
}
return token
end
t [ key ] = fn
return fn
end
} )
return re.compile ( lingo , defs )
end
local Compiler
do
local _class_0
local _base_0 = {
call = function ( self , fn_name , ... )
local fn_info = self.defs [ fn_name ]
if fn_info == nil then
self : error ( " Attempt to call undefined function: " .. tostring ( fn_name ) )
end
if fn_info.is_macro then
self : error ( " Attempt to call macro at runtime: " .. tostring ( fn_name ) )
end
if not ( self : check_permission ( fn_name ) ) then
self : error ( " You do not have the authority to call: " .. tostring ( fn_name ) )
end
table.insert ( self.callstack , fn_name )
local fn , arg_names
fn , arg_names = fn_info.fn , fn_info.arg_names
local args
do
local _tbl_0 = { }
for i , name in ipairs ( arg_names ) do
_tbl_0 [ name ] = select ( i , ... )
end
args = _tbl_0
end
if self.debug then
print ( " Calling " .. tostring ( fn_name ) .. " with args: " .. tostring ( utils.repr ( args ) ) )
end
local ret = fn ( self , args )
table.remove ( self.callstack )
return ret
end ,
check_permission = function ( self , fn_name )
local fn_info = self.defs [ fn_name ]
if fn_info == nil then
self : error ( " Undefined function: " .. tostring ( fn_name ) )
end
if fn_info.whiteset == nil then
return true
end
local _list_0 = self.callstack
for _index_0 = 1 , # _list_0 do
local caller = _list_0 [ _index_0 ]
if fn_info.whiteset [ caller ] then
return true
end
end
return false
end ,
def = function ( self , spec , fn )
if self.debug then
print ( " Defining rule: " .. tostring ( spec ) )
end
local invocations , arg_names = self : get_invocations ( spec )
local fn_info = {
fn = fn ,
arg_names = arg_names ,
invocations = invocations ,
is_macro = false
}
for _index_0 = 1 , # invocations do
local invocation = invocations [ _index_0 ]
self.defs [ invocation ] = fn_info
end
end ,
get_invocations = function ( self , text )
if type ( text ) == ' string ' then
text = {
text
}
end
local invocations = { }
local arg_names
for _index_0 = 1 , # text do
local _text = text [ _index_0 ]
local invocation = _text : gsub ( " %%%S+ " , " %% " )
local _arg_names
do
local _accum_0 = { }
local _len_0 = 1
for arg in _text : gmatch ( " %%(%S+) " ) do
_accum_0 [ _len_0 ] = arg
_len_0 = _len_0 + 1
end
_arg_names = _accum_0
end
table.insert ( invocations , invocation )
if arg_names then
if not utils.equivalent ( utils.set ( arg_names ) , utils.set ( _arg_names ) ) then
self : error ( " Conflicting argument names " .. tostring ( utils.repr ( arg_names ) ) .. " and " .. tostring ( utils.repr ( _arg_names ) ) .. " for " .. tostring ( utils.repr ( text ) ) )
end
else
arg_names = _arg_names
end
end
return invocations , arg_names
end ,
defmacro = function ( self , spec , lua_gen_fn )
local invocations , arg_names = self : get_invocations ( spec )
local fn_info = {
fn = lua_gen_fn ,
arg_names = arg_names ,
invocations = invocations ,
is_macro = true
}
for _index_0 = 1 , # invocations do
local invocation = invocations [ _index_0 ]
self.defs [ invocation ] = fn_info
end
end ,
run = function ( self , text )
if self.debug then
print ( " RUNNING TEXT: \n " .. tostring ( text ) )
end
local code = self : compile ( text )
if self.debug then
print ( " \n GENERATED LUA CODE: \n " .. tostring ( code ) )
end
return code
end ,
parse = function ( self , str )
if self.debug then
print ( " PARSING: \n " .. tostring ( str ) )
end
local lingo = [ = [ file <- ( { { | % ws ? % new_line ? { : body : block : } % new_line ? % ws ? ( errors ) ? | } } ) -> File
errors <- ( ( { . + } ) => error_handler )
block <- ( { { | statement ( % new_line statement ) * | } } ) -> Block
statement <- ( { ( functioncall / expression ) } ) -> Statement
one_liner <- ( { { |
( ( {
( ( { { |
( expression ( % word_boundary fn_bit ) + ) / ( word ( % word_boundary fn_bit ) * )
| } } ) -> FunctionCall )
/ ( expression )
} ) -> Statement )
| } } ) -> Block
functioncall <- ( { { | ( expression % word_boundary fn_bits ) / ( word ( % word_boundary fn_bits ) ? ) | } } ) -> FunctionCall
fn_bit <- ( expression / word )
fn_bits <-
( ( " .. " % ws ? ( % indent % new_line indented_fn_bits % dedent ) ( % new_line " .. " % ws ? fn_bits ) ? )
/ ( % new_line " .. " fn_bit ( % word_boundary fn_bits ) ? )
/ ( fn_bit ( % word_boundary fn_bits ) ? ) )
indented_fn_bits <-
fn_bit ( ( % new_line / % word_boundary ) indented_fn_bits ) ?
thunk <-
( { " : " % ws ?
( ( % indent % new_line block ( ( % dedent ( % new_line " .. " ) ? ) / errors ) )
/ ( one_liner ( % ws ? ( % new_line ? " .. " ) ) ? ) ) } ) -> Thunk
word <- ( { ! number { % wordchar + } } ) -> Word
expression <- ( { ( longstring / string / number / variable / list / thunk / subexpression ) } ) -> Expression
string <- ( { ( ! longstring ) ' " ' { ( ( " \" .) / [^ " ] ) * } ' " ' } ) -> String
longstring <- ( { ' ".." ' % ws ? % indent { ( % new_line " | " [ ^% nl ] * ) + } ( ( % dedent ( % new_line ' .. ' ) ? ) / errors ) } ) -> Longstring
number <- ( { { ' - ' ? [ 0 - 9 ] + ( " . " [ 0 - 9 ] + ) ? } } ) -> Number
variable <- ( { ( " % " { % wordchar + } ) } ) -> Var
subexpression <-
( ! % comment " ( " % ws ? ( functioncall / expression ) % ws ? " ) " )
/ ( " (..) " % ws ? % indent % new_line ( ( ( { { | indented_fn_bits | } } ) -> FunctionCall ) / expression ) % dedent ( % new_line " .. " ) ? )
list <- ( { { |
( " [..] " % ws ? % indent % new_line indented_list " , " ? ( ( % dedent ( % new_line " .. " ) ? ) / errors ) )
/ ( " [ " % ws ? ( list_items " , " ? ) ? % ws ? " ] " )
| } } ) -> List
list_items <- ( ( functioncall / expression ) ( list_sep list_items ) ? )
list_sep <- % ws ? " , " % ws ?
indented_list <-
( functioncall / expression ) ( ( ( list_sep % new_line ? ) / % new_line ) indented_list ) ?
] = ]
lingo = make_parser ( lingo )
local tree = lingo : match ( str : gsub ( " \r " , " " ) .. " \n " )
if self.debug then
print ( " \n PARSE TREE: " )
self : print_tree ( tree )
end
assert ( tree , " Failed to parse: " .. tostring ( str ) )
return tree
end ,
tree_to_value = function ( self , tree )
local code = " return (function(compiler, vars) \n return " .. tostring ( self : tree_to_lua ( tree ) ) .. " \n end) "
local lua_thunk , err = load ( code )
if not lua_thunk then
error ( " Failed to compile generated code: \n " .. tostring ( code ) .. " \n \n " .. tostring ( err ) )
end
return ( lua_thunk ( ) ) ( self , { } )
end ,
tree_to_lua = function ( self , tree , kind )
if kind == nil then
kind = " Expression "
end
assert ( tree , " No tree provided. " )
local indent = " "
local buffer = { }
local to_lua
to_lua = function ( t , kind )
local ret = self : tree_to_lua ( t , kind )
return ret
end
local add
add = function ( code )
return table.insert ( buffer , code )
end
local _exp_0 = tree.type
if " File " == _exp_0 then
add ( [ [ return ( function ( compiler , vars )
local ret ] ] )
local vars = { }
local _list_0 = tree.value . body.value
for _index_0 = 1 , # _list_0 do
local statement = _list_0 [ _index_0 ]
local code = to_lua ( statement )
local lua_thunk , err = load ( " return (function(compiler, vars) \n " .. tostring ( code ) .. " \n end) " )
if not lua_thunk then
error ( " Failed to compile generated code: \n " .. tostring ( code ) .. " \n \n " .. tostring ( err ) )
end
local ok
ok , err = pcall ( lua_thunk )
if not ok then
error ( err )
end
ok , err = pcall ( err , self , vars )
if not ok then
self : error ( err )
end
add ( code )
end
add ( [ [ return ret
end )
] ] )
elseif " Block " == _exp_0 then
local _list_0 = tree.value
for _index_0 = 1 , # _list_0 do
local statement = _list_0 [ _index_0 ]
add ( to_lua ( statement ) )
end
elseif " Thunk " == _exp_0 then
assert ( tree.value . type == " Block " , " Non-block value in Thunk " )
add ( [ [ ( function ( compiler , vars )
local ret ] ] )
add ( to_lua ( tree.value ) )
add ( [ [ return ret
end )
] ] )
elseif " Statement " == _exp_0 then
if tree.value . type == " FunctionCall " then
local name = self : fn_name_from_tree ( tree.value )
if self.defs [ name ] and self.defs [ name ] . is_macro then
add ( self : run_macro ( tree.value , " Statement " ) )
else
add ( " ret = " .. ( to_lua ( tree.value ) : match ( " %s*(.*) " ) ) )
end
else
add ( " ret = " .. ( to_lua ( tree.value ) : match ( " %s*(.*) " ) ) )
end
elseif " Expression " == _exp_0 then
add ( to_lua ( tree.value ) )
elseif " FunctionCall " == _exp_0 then
local name = self : fn_name_from_tree ( tree )
if self.defs [ name ] and self.defs [ name ] . is_macro then
add ( self : run_macro ( tree , " Expression " ) )
else
local args
do
local _accum_0 = { }
local _len_0 = 1
local _list_0 = tree.value
for _index_0 = 1 , # _list_0 do
local a = _list_0 [ _index_0 ]
if a.type ~= " Word " then
_accum_0 [ _len_0 ] = to_lua ( a )
_len_0 = _len_0 + 1
end
end
args = _accum_0
end
table.insert ( args , 1 , utils.repr ( name , true ) )
add ( self.__class : comma_separated_items ( " compiler:call( " , args , " ) " ) )
end
elseif " String " == _exp_0 then
local escapes = {
n = " \n " ,
t = " \t " ,
b = " \b " ,
a = " \a " ,
v = " \v " ,
f = " \f " ,
r = " \r "
}
local unescaped = tree.value : gsub ( " \\ (.) " , ( function ( c )
return escapes [ c ] or c
end ) )
add ( utils.repr ( unescaped , true ) )
elseif " Longstring " == _exp_0 then
local result
do
local _accum_0 = { }
local _len_0 = 1
for line in tree.value : gmatch ( " [ \t ]*|([^ \n ]*) " ) do
_accum_0 [ _len_0 ] = line
_len_0 = _len_0 + 1
end
result = _accum_0
end
add ( utils.repr ( table.concat ( result , " \n " ) , true ) )
elseif " Number " == _exp_0 then
add ( tree.value )
elseif " List " == _exp_0 then
if # tree.value == 0 then
add ( " {} " )
elseif # tree.value == 1 then
add ( " { " .. tostring ( to_lua ( tree.value [ 1 ] ) ) .. " } " )
else
add ( self.__class : comma_separated_items ( " { " , ( function ( )
local _accum_0 = { }
local _len_0 = 1
local _list_0 = tree.value
for _index_0 = 1 , # _list_0 do
local item = _list_0 [ _index_0 ]
_accum_0 [ _len_0 ] = to_lua ( item )
_len_0 = _len_0 + 1
end
return _accum_0
end ) ( ) , " } " ) )
end
elseif " Var " == _exp_0 then
add ( " vars[ " .. tostring ( utils.repr ( tree.value , true ) ) .. " ] " )
else
error ( " Unknown/unimplemented thingy: " .. tostring ( tree.type ) )
end
buffer = table.concat ( buffer , " \n " )
return buffer
end ,
fn_name_from_tree = function ( self , tree )
assert ( tree.type == " FunctionCall " , " Attempt to get fn name from non-functioncall tree: " .. tostring ( tree.type ) )
local name_bits = { }
local _list_0 = tree.value
for _index_0 = 1 , # _list_0 do
local token = _list_0 [ _index_0 ]
table.insert ( name_bits , ( function ( )
if token.type == " Word " then
return token.value
else
return " % "
end
end ) ( ) )
end
return table.concat ( name_bits , " " )
end ,
run_macro = function ( self , tree , kind )
if kind == nil then
kind = " Expression "
end
local name = self : fn_name_from_tree ( tree )
if not ( self.defs [ name ] and self.defs [ name ] . is_macro ) then
self : error ( " Macro not found: " .. tostring ( name ) )
end
if not ( self : check_permission ( name ) ) then
self : error ( " You do not have the authority to call: " .. tostring ( name ) )
end
local fn , arg_names
do
local _obj_0 = self.defs [ name ]
fn , arg_names = _obj_0.fn , _obj_0.arg_names
end
local args
do
local _accum_0 = { }
local _len_0 = 1
local _list_0 = tree.value
for _index_0 = 1 , # _list_0 do
local a = _list_0 [ _index_0 ]
if a.type ~= " Word " then
_accum_0 [ _len_0 ] = a
_len_0 = _len_0 + 1
end
end
args = _accum_0
end
do
local _tbl_0 = { }
for i , name in ipairs ( arg_names ) do
_tbl_0 [ name ] = args [ i ]
end
args = _tbl_0
end
table.insert ( self.callstack , name )
local ret , manual_mode = fn ( self , args , kind )
table.remove ( self.callstack )
if not ret then
self : error ( " No return value for macro: " .. tostring ( name ) )
end
if kind == " Statement " and not manual_mode then
ret = " ret = " .. ret
end
return ret
end ,
_yield_tree = function ( self , tree , indent_level )
if indent_level == nil then
indent_level = 0
end
local ind
ind = function ( s )
return INDENT : rep ( indent_level ) .. s
end
local _exp_0 = tree.type
if " File " == _exp_0 then
coroutine.yield ( ind ( " File: " ) )
self : _yield_tree ( tree.value . body , indent_level + 1 )
elseif " Errors " == _exp_0 then
coroutine.yield ( ind ( " Error: \n " .. tostring ( tree.value ) ) )
elseif " Block " == _exp_0 then
local _list_0 = tree.value
for _index_0 = 1 , # _list_0 do
local chunk = _list_0 [ _index_0 ]
self : _yield_tree ( chunk , indent_level )
end
elseif " Thunk " == _exp_0 then
coroutine.yield ( ind ( " Thunk: " ) )
self : _yield_tree ( tree.value , indent_level + 1 )
elseif " Statement " == _exp_0 then
self : _yield_tree ( tree.value , indent_level )
elseif " Expression " == _exp_0 then
self : _yield_tree ( tree.value , indent_level )
elseif " FunctionCall " == _exp_0 then
local name = self : fn_name_from_tree ( tree )
local args
do
local _accum_0 = { }
local _len_0 = 1
local _list_0 = tree.value
for _index_0 = 1 , # _list_0 do
local a = _list_0 [ _index_0 ]
if a.type ~= " Word " then
_accum_0 [ _len_0 ] = a
_len_0 = _len_0 + 1
end
end
args = _accum_0
end
if # args == 0 then
coroutine.yield ( ind ( " Call [ " .. tostring ( name ) .. " ]! " ) )
else
coroutine.yield ( ind ( " Call [ " .. tostring ( name ) .. " ]: " ) )
for _index_0 = 1 , # args do
local a = args [ _index_0 ]
self : _yield_tree ( a , indent_level + 1 )
end
end
elseif " String " == _exp_0 then
coroutine.yield ( ind ( utils.repr ( tree.value , true ) ) )
elseif " Longstring " == _exp_0 then
coroutine.yield ( ind ( utils.repr ( tree.value , true ) ) )
elseif " Number " == _exp_0 then
coroutine.yield ( ind ( tree.value ) )
elseif " List " == _exp_0 then
if # tree.value == 0 then
coroutine.yield ( ind ( " <Empty List> " ) )
else
coroutine.yield ( ind ( " List: " ) )
local _list_0 = tree.value
for _index_0 = 1 , # _list_0 do
local item = _list_0 [ _index_0 ]
self : _yield_tree ( item , indent_level + 1 )
end
end
elseif " Var " == _exp_0 then
coroutine.yield ( ind ( " Var[ " .. tostring ( utils.repr ( tree.value ) ) .. " ] " ) )
else
error ( " Unknown/unimplemented thingy: " .. tostring ( tree.type ) )
end
return nil
end ,
print_tree = function ( self , tree )
for line in coroutine.wrap ( function ( )
return self : _yield_tree ( tree )
end ) do
print ( line )
end
end ,
stringify_tree = function ( self , tree )
local result = { }
for line in coroutine.wrap ( function ( )
return self : _yield_tree ( tree )
end ) do
table.insert ( result , line )
end
return table.concat ( result , " \n " )
end ,
compile = function ( self , src , output_file )
if output_file == nil then
output_file = nil
end
if self.debug then
print ( " COMPILING: \n " .. tostring ( src ) )
end
local tree = self : parse ( src )
assert ( tree , " Tree failed to compile: " .. tostring ( src ) )
local code = self : tree_to_lua ( tree )
if output_file then
local output = io.open ( output_file , " w " )
output : write ( code )
end
return code
end ,
error = function ( self , ... )
print ( ... )
print ( " Callstack: " )
for i = # self.callstack , 1 , - 1 do
print ( " " .. tostring ( self.callstack [ i ] ) )
end
return error ( )
end ,
test = function ( self , src , expected )
local i = 1
while i ~= nil do
local start , stop = src : find ( " \n \n " , i )
local test = src : sub ( i , start )
i = stop
start , stop = test : find ( " === " )
if not start or not stop then
self : error ( " WHERE'S THE ===? in: \n " .. tostring ( test ) )
end
local test_src
test_src , expected = test : sub ( 1 , start - 1 ) , test : sub ( stop + 1 , - 1 )
expected = expected : match ( ' [ \n ]*(.*[^ \n ]) ' )
local tree = self : parse ( test_src )
local got = self : stringify_tree ( tree.value . body )
if got ~= expected then
self : error ( " TEST FAILED! \n Source: \n " .. tostring ( test_src ) .. " \n Expected: \n " .. tostring ( expected ) .. " \n \n Got: \n " .. tostring ( got ) )
end
end
end ,
initialize_core = function ( self )
local as_lua_code
as_lua_code = function ( self , str )
local _exp_0 = str.type
if " String " == _exp_0 then
local escapes = {
n = " \n " ,
t = " \t " ,
b = " \b " ,
a = " \a " ,
v = " \v " ,
f = " \f " ,
r = " \r "
}
local unescaped = str.value : gsub ( " \\ (.) " , ( function ( c )
return escapes [ c ] or c
end ) )
return unescaped
elseif " Longstring " == _exp_0 then
local result
do
local _accum_0 = { }
local _len_0 = 1
for line in str.value : gmatch ( " [ \t ]*|([^ \n ]*) " ) do
_accum_0 [ _len_0 ] = line
_len_0 = _len_0 + 1
end
result = _accum_0
end
return table.concat ( result , " \n " )
else
return self : tree_to_lua ( str )
end
end
self : defmacro ( [[lua block %lua_code]] , function ( self , vars , kind )
if kind == " Expression " then
error ( " Expected to be in statement. " )
end
local lua_code = vars.lua_code . value
local _exp_0 = lua_code.type
if " List " == _exp_0 then
return table.concat ( ( function ( )
local _accum_0 = { }
local _len_0 = 1
local _list_0 = lua_code.value
for _index_0 = 1 , # _list_0 do
local i = _list_0 [ _index_0 ]
_accum_0 [ _len_0 ] = as_lua_code ( self , i.value )
_len_0 = _len_0 + 1
end
return _accum_0
end ) ( ) ) , true
else
return as_lua_code ( self , lua_code ) , true
end
end )
self : defmacro ( [[lua expr %lua_code]] , function ( self , vars , kind )
local lua_code = vars.lua_code . value
local _exp_0 = lua_code.type
if " List " == _exp_0 then
return table.concat ( ( function ( )
local _accum_0 = { }
local _len_0 = 1
local _list_0 = lua_code.value
for _index_0 = 1 , # _list_0 do
local i = _list_0 [ _index_0 ]
_accum_0 [ _len_0 ] = as_lua_code ( self , i.value )
_len_0 = _len_0 + 1
end
return _accum_0
end ) ( ) )
else
return as_lua_code ( self , lua_code )
end
end )
self : def ( " rule %spec %body " , function ( self , vars )
return self : def ( vars.spec , vars.body )
end )
self : defmacro ( [[macro %spec %body]] , function ( self , vars , kind )
if kind == " Expression " then
error ( " Macro definitions cannot be used as expressions. " )
end
self : defmacro ( self : tree_to_value ( vars.spec ) , self : tree_to_value ( vars.body ) )
return " " , true
end )
self : defmacro ( [[macro block %spec %body]] , function ( self , vars , kind )
if kind == " Expression " then
error ( " Macro definitions cannot be used as expressions. " )
end
local invocation = self : tree_to_value ( vars.spec )
local fn = self : tree_to_value ( vars.body )
self : defmacro ( invocation , ( function ( self , vars , kind )
if kind == " Expression " then
error ( " Macro: " .. tostring ( invocation ) .. " was defined to be a block, not an expression. " )
end
return fn ( self , vars , kind ) , true
end ) )
return " " , true
end )
return self : def ( " run file %filename " , function ( self , vars )
local file = io.open ( vars.filename )
return self : run ( file : read ( ' *a ' ) )
end )
end
}
_base_0.__index = _base_0
_class_0 = setmetatable ( {
__init = function ( self , parent )
self.defs = setmetatable ( { } , {
__index = parent and parent.defs
} )
self.callstack = { }
self.debug = false
return self : initialize_core ( )
end ,
__base = _base_0 ,
__name = " Compiler "
} , {
__index = _base_0 ,
__call = function ( cls , ... )
local _self_0 = setmetatable ( { } , _base_0 )
cls.__init ( _self_0 , ... )
return _self_0
end
} )
_base_0.__class = _class_0
local self = _class_0
self.comma_separated_items = function ( self , open , items , close )
return utils.accumulate ( " \n " , function ( )
local buffer = open
local so_far = 0
for i , item in ipairs ( items ) do
if i < # items then
item = item .. " , "
end
if so_far + # item >= 80 and # buffer > 0 then
coroutine.yield ( buffer )
so_far = so_far - # buffer
buffer = item
else
so_far = so_far + # item
buffer = buffer .. item
end
end
buffer = buffer .. close
return coroutine.yield ( buffer )
end )
end
Compiler = _class_0
end
2017-09-12 23:12:45 -07:00
if arg and arg [ 1 ] then
2017-09-12 21:38:54 -07:00
local c = Compiler ( )
local input = io.open ( arg [ 1 ] ) : read ( " *a " )
local _print = print
local _io_write = io.write
if arg [ 2 ] == " - " then
local nop
nop = function ( ) end
print , io.write = nop , nop
end
local code = c : run ( input )
if arg [ 2 ] then
local output
if arg [ 2 ] == " - " then
print , io.write = _print , _io_write
output = io.output ( )
else
output = io.open ( arg [ 2 ] , ' w ' )
end
output : write ( [ [ local load = function ( )
] ] )
output : write ( code )
output : write ( [ [
end
local utils = require ( ' utils ' )
2017-09-12 23:08:38 -07:00
local Compiler = require ( ' nomsu ' )
2017-09-12 23:12:45 -07:00
local c = Compiler ( )
2017-09-12 21:38:54 -07:00
load ( ) ( c , { } )
] ] )
end
end
return Compiler