Making forward progress.

This commit is contained in:
Bruce Hill 2017-09-26 15:27:01 -07:00
parent 02def0af92
commit 10d61df78b
7 changed files with 315 additions and 338 deletions

View File

@ -3,137 +3,99 @@ require "lib/operators.nom"
require "lib/utils.nom" require "lib/utils.nom"
# Conditionals # Conditionals
macro statement [if %condition %if_body] =: parse (if %condition %if_body) as lua code ".."
".."|if \%condition as lua\ then |if \(%condition) then
| \(lua expr "vars.if_body.value") as lua\ | \(lua expr "vars.if_body.value")
|end |end
macro statement [if %condition %if_body else %else_body] =: parse (if %condition %if_body else %else_body) as lua code ".."
".."|if \%condition as lua\ then |if \(%condition) then
| \(lua expr "vars.if_body.value") as lua\ | \(lua expr "vars.if_body.value")
|else |else
| \(lua expr "vars.else_body.value") as lua\ | \(lua expr "vars.else_body.value")
|end |end
# Return # Return
macro statement [return] =: "do return end" parse (return) as lua code "do return end"
macro block [return %return-value] =: ".." parse (return %return-value) as lua code "do return \(%return-value)"
|return \%return-value as lua\ parse (do %action) as lua expr ".."
|(\(%action))(nomsu, setmetatable({}, {__index=vars}))
macro [do %action] =: ".."
|(\%action as lua\)(nomsu, setmetatable({}, {__index=vars}))
# GOTOs # GOTOs
macro statement [-> %label] =: ".." parse (-> %label) as lua code ".."
|::label_\nomsu "var_to_lua_identifier" [%label]\:: |::label_\(nomsu "var_to_lua_identifier" [%label])::
macro statement [go to %label] =: ".." parse (go to %label) as lua code ".."
|goto label_\nomsu "var_to_lua_identifier" [%label]\ |goto label_\(nomsu "var_to_lua_identifier" [%label])
# Loop control flow # Loop control flow
macro statement [stop, stop loop, break] =: "break" parse (stop; stop loop; break) as lua code "break"
macro statement [stop for, stop for-loop, break for] =: "goto break_for" parse (stop for; stop for-loop; break for) as lua code "goto break_for"
macro statement [stop repeat, stop repeat-loop, break repeat] =: "goto break_repeat" parse (stop repeat; stop repeat-loop; break repeat) as lua code "goto break_repeat"
macro statement [stop %var, break %var] =: ".." parse (stop %var; break %var) as lua code ".."
|goto break_\nomsu "var_to_lua_identifier" [%var]\ |goto break_\(nomsu "var_to_lua_identifier" [%var])
macro statement [continue, continue loop] =: "continue" parse (continue; continue loop) as lua code "continue"
macro statement [continue for, continue for-loop] =: "goto continue_for" parse (continue for; continue for-loop) as lua code "goto continue_for"
macro statement [continue repeat, continue repeat-loop] =: "goto continue_repeat" parse (continue repeat; continue repeat-loop) as lua code "goto continue_repeat"
macro statement [continue %var, go to next %var, on to the next %var] =: ".." parse (continue %var; go to next %var; on to the next %var) as lua code ".."
|goto continue_\nomsu "var_to_lua_identifier" [%var]\ |goto continue_\(nomsu "var_to_lua_identifier" [%var])
# While loops # While loops
macro block [repeat %body] =: parse (repeat %body) as lua block ".."
".."|while true do |while true do
| \(lua expr "vars.body.value") as lua\ | \(lua expr "vars.body.value")
| ::continue_repeat:: | ::continue_repeat::
|end |end
|::break_repeat:: |::break_repeat::
macro block [repeat while %condition %body] =: parse (repeat while %condition %body) as lua block ".."
".."|while \%condition as lua\ do |while \(%condition) do
| \(lua expr "vars.body.value") as lua\ | \(lua expr "vars.body.value")
| ::continue_repeat:: | ::continue_repeat::
|end |end
|::break_repeat:: |::break_repeat::
macro block [repeat until %condition %body] =: parse (repeat until %condition %body) as lua block ".."
".."|while not (\%condition as lua\) do |while not (\(%condition)) do
| \(lua expr "vars.body.value") as lua\ | \(lua expr "vars.body.value")
| ::continue_repeat:: | ::continue_repeat::
|end |end
|::break_repeat:: |::break_repeat::
# Numeric range for loops # Numeric range for loops
macro block [for %var from %start to %stop by %step %body] =: parse:
%var-type =: lua expr "vars.var.type" for %var from %start to %stop by %step %body
assert (%var-type == "Var") ".." for %var from %start to %stop via %step %body
|For loop has the wrong type for the loop variable. Expected Var, but got: \%var-type\ ..as lua block ".."
|for i=\(%start),\(%stop),\(%step) do
# This trashes the loop variables, just like in Python. # This trashes the loop variables, just like in Python.
".." | \(%var) = i
|for i=\%start as lua\,\%stop as lua\,\%step as lua\ do | \(lua expr "vars.body.value")
| \%var as lua\ = i | ::continue_for::
| \(lua expr "vars.body.value") as lua\ | ::continue_\(nomsu "var_to_lua_identifier" [%var])::
| ::continue_for:: |end
| ::continue_\nomsu "var_to_lua_identifier" [%var]\:: |::break_for::
|end |::break_\(nomsu "var_to_lua_identifier" [%var])::
|::break_for:: parse (for %var from %start to %stop %body) as: for %var from %start to %stop via 1 %body
|::break_\nomsu "var_to_lua_identifier" [%var]\:: parse:
macro block [for %var from %start to %stop %body] =: for all %start to %stop by %step %body
%thunk =: :for %var from %start to %stop by 1 %body for all %start to %stop via %step %body
lua block ".." ..as: for % from %start to %stop via %step %body
|for i,x in ipairs(vars.thunk.value) do parse (for all %start to %stop %body) as: for all %start to %stop via 1 %body
| if x.type == 'Var' then vars.thunk.type == vars[x.value] end
|end
|return compiler:run_macro(vars.thunk, 'Statement')
macro block [for all %start to %stop by %step %body] =: parse (for %var in %iterable %body) as lua block ".."
|for i,value in ipairs(\(%iterable)) do
# This trashes the loop variables, just like in Python. # This trashes the loop variables, just like in Python.
".." | \(%var) = value
|for i=\%start as lua\,\%stop as lua\,\%step as lua\ do | \(lua expr "vars.body.value")
| vars[''] = i | ::continue_for::
| \(lua expr "vars.body.value") as lua\ | ::continue_\(nomsu "var_to_lua_identifier" [%var])::
| ::continue_for:: |end
| ::continue_\nomsu "var_to_lua_identifier" [%]\:: |::break_for::
|end |::break_\(nomsu "var_to_lua_identifier" [%var])::
|::break_for:: parse (for all %iterable %body) as: for % in %iterable %body
|::break_\nomsu "var_to_lua_identifier" [%]\::
macro block [for %var from %start to %stop %body] =:
%thunk =: :for %var from %start to %stop by 1 %body
lua block ".."
|for i,x in ipairs(vars.thunk.value) do
| if x.type == 'Var' then vars.thunk.type == vars[x.value] end
|end
|return compiler:run_macro(vars.thunk, 'Statement')
macro block [for %var in %iterable %body] =:
%var-type =: lua expr "vars.var.type"
assert (%var-type == "Var") ".."
|For loop has the wrong type for the loop variable. Expected Var, but got: \%var-type\
# This trashes the loop variables, just like in Python.
".."
|for i,value in ipairs(\%iterable as lua\) do
| \%var as lua\ = value
| \(lua expr "vars.body.value") as lua\
| ::continue_for::
| ::continue_\nomsu "var_to_lua_identifier" [%var]\::
|end
|::break_for::
|::break_\nomsu "var_to_lua_identifier" [%var]\::
macro block [for all %iterable %body] =:
pass # TODO: fix compiler bug
# This trashes the loop variables, just like in Python.
".."|for i,value in ipairs(\%iterable as lua\) do
| vars[''] = value
| \(lua expr "vars.body.value") as lua\
| ::continue_for::
| ::continue_\nomsu "var_to_lua_identifier" [%]\::
|end
|::break_for::
|::break_\nomsu "var_to_lua_identifier" [%]\::
# Switch statement/multi-branch if # Switch statement/multi-branch if
macro block [when %body] =: parse (when %body) as lua block:
%result =: "" %result =: ""
%fallthroughs =: [] %fallthroughs =: []
for %statement in (lua expr "vars.body.value.value"): for %statement in (lua expr "vars.body.value.value"):
@ -156,15 +118,18 @@ macro block [when %body] =:
go to next %statement go to next %statement
if (lua expr "vars.condition.type == 'Word' and vars.condition.value == 'else'"): if (lua expr "vars.condition.type == 'Word' and vars.condition.value == 'else'"):
%result join=: ".."| %result join=: ".."
|
|do |do
..else: ..else:
%condition =: %condition as lua %condition =: %condition as lua
for all %fallthroughs: %condition join=: ".."| or \% as lua\ for all %fallthroughs: %condition join= " or \(%)"
%result join=: ".."| %result join=: ".."
|if \%condition\ then |
%result join=: ".."| |if \(%condition) then
| \(lua expr "vars.thunk.value") as lua\ %result join=: ".."
|
| \(lua expr "vars.thunk.value")
| goto finished_when | goto finished_when
|end |end
@ -174,8 +139,8 @@ macro block [when %body] =:
%result %result
# Switch statement # Switch statement
macro block [when %branch-value == ? %body] =: parse (when %branch-value == ? %body) as lua block:
%result =: ".."|local branch_value = \%branch-value as lua\ %result =: "local branch_value = \(%branch-value)"
%fallthroughs =: [] %fallthroughs =: []
for %statement in (lua expr "vars.body.value.value"): for %statement in (lua expr "vars.body.value.value"):
%func-call =: lua expr "vars.statement.value" %func-call =: lua expr "vars.statement.value"
@ -197,15 +162,18 @@ macro block [when %branch-value == ? %body] =:
go to next %statement go to next %statement
if (lua expr "vars.condition.type == 'Word' and vars.condition.value == 'else'"): if (lua expr "vars.condition.type == 'Word' and vars.condition.value == 'else'"):
%result join=: ".."| %result join=: ".."
|
|do |do
..else: ..else:
%condition =: ".."|branch_value == (\%condition as lua\) %condition =: "branch_value == (\(%condition))"
for all %fallthroughs: %condition join=: ".."| or (branch_value == \% as lua\) for all %fallthroughs: %condition join= " or (branch_value == \(%))"
%result join=: ".."| %result join=: ".."
|
|if \%condition\ then |if \%condition\ then
%result join=: ".."| %result join=: ".."
| \(lua expr "vars.thunk.value") as lua\ |
| \((lua expr "vars.thunk.value"))
| goto finished_when | goto finished_when
|end |end
@ -213,4 +181,3 @@ macro block [when %branch-value == ? %body] =:
%result join=: "\n::finished_when::" %result join=: "\n::finished_when::"
%result %result

View File

@ -27,7 +27,6 @@ lua code ".."
|end |end
|nomsu:def("parse nomsu %shorthand as nomsu %longhand", parse_as) |nomsu:def("parse nomsu %shorthand as nomsu %longhand", parse_as)
parse nomsu \(parse %shorthand as %longhand) as nomsu \(parse nomsu \%shorthand as nomsu \%longhand) parse nomsu \(parse %shorthand as %longhand) as nomsu \(parse nomsu \%shorthand as nomsu \%longhand)
parse (foo %x) as (baz %x)
lua code ".." lua code ".."
|nomsu:defmacro("lua expr %code", function(nomsu, vars) |nomsu:defmacro("lua expr %code", function(nomsu, vars)
@ -71,17 +70,12 @@ parse (lua block %block) as:
parse (nomsu) as: lua expr "nomsu" parse (nomsu) as: lua expr "nomsu"
parse (nomsu's %key) as: parse (nomsu's %key) as:
lua expr "nomsu[\(%key as lua)]" lua expr "nomsu['\(%key)']"
parse (nomsu %method %args) as: parse (nomsu %method %args) as:
lua block ".." lua expr "nomsu['\(%method)'](nomsu, unpack(\(%args)))"
|local args = {"nomsu"}
|for _,arg in ipairs(vars.args.value) do
| table.insert(args, nomsu:tree_to_lua(arg))
|end
|local method_name = nomsu:repr(nomsu:tree_to_value(vars.method, vars))
..with value ".."
|("nomsu[%s](%s)"):format(method_name, table.concat(args, ", "))
parse (repr %) as: nomsu "repr" [%] parse (repr %) as: nomsu "repr" [%]
repr 5
# Get the source code for a function # Get the source code for a function
rule (help %rule) =: rule (help %rule) =:
@ -111,3 +105,18 @@ parse (source code %body) as: source code from tree \%body
parse (parse tree %code) as: repr (nomsu "tree_to_str" [\%code]) parse (parse tree %code) as: repr (nomsu "tree_to_str" [\%code])
parse (parse %code as lua code %lua) as:
parse nomsu \%code as nomsu:
nomsu "replaced_vars" [\(lua code %lua), lua expr "vars"]
parse (parse %code as lua expr %lua) as:
parse nomsu \%code as nomsu:
nomsu "replaced_vars" [\(lua expr %lua), lua expr "vars"]
parse (parse %code as lua block %lua) as:
parse nomsu \%code as nomsu:
nomsu "replaced_vars" [\(lua block %lua), lua expr "vars"]
parse (pass) as lua code: ""
pass

View File

@ -1,25 +1,25 @@
require "lib/metaprogramming.nom" require "lib/metaprogramming.nom"
# Literals # Literals
macro [true, yes] =: "true" parse (true; yes) as lua expr "true"
macro [false, no] =: "false" parse (false; no) as lua expr "false"
macro [nil, null] =: "nil" parse (nil; null) as lua expr "nil"
macro [inf, infinity] =: "math.huge" parse (inf; infinity) as lua expr "math.huge"
macro [nan, NaN, not a number] =: "(0/0)" parse (nan; NaN; not a number) as lua expr "(0/0)"
macro statement [nop, pass] =: "" parse (nop; pass) as lua code ""
# Ternary operator # Ternary operator
macro [%if_expr if %condition else %else_expr] =: parse (%if_expr if %condition else %else_expr) as lua expr ".."
pass # TODO: Fix compiler bug that doesn't parse right here |(function(nomsu, vars)
# TODO: fix compiler bug that breaks this code if comments immediately follow ".."
#.. Note: this uses a function instead of (condition and if_expr or else_expr) #.. Note: this uses a function instead of (condition and if_expr or else_expr)
because that breaks if %if_expr is falsey. because that breaks if %if_expr is falsey.
".."|(function(nomsu, vars) | if \(%condition) then
| if \%condition as lua\ then | return \(%if_expr)
| return \%if_expr as lua\ | else
| else | return \(%else_expr)
| return \%else_expr as lua\ | end
| end |end)(nomsu, vars)
|end)(nomsu, vars)
# Variable assignment operator, and += type versions # Variable assignment operator, and += type versions
lua block ".." lua block ".."
@ -64,11 +64,11 @@ lua block ".."
| end | end
| nomsu:defmacro("%a "..nomsu_alias.." %b", (function(nomsu, vars, kind) | nomsu:defmacro("%a "..nomsu_alias.." %b", (function(nomsu, vars, kind)
| return "("..nomsu:tree_to_lua(vars.a).." "..op.." "..nomsu:tree_to_lua(vars.b)..")" | return "("..nomsu:tree_to_lua(vars.a).." "..op.." "..nomsu:tree_to_lua(vars.b)..")"
| end), [[".."|(\\%a as lua\\ ]]..op..[[ \\%b as lua\\)]]) | end), [["(\\(%a) ]]..op..[[ \\(%b))"]])
|end |end
# == and != do equivalence checking, rather than identity checking # == and != do equivalence checking, rather than identity checking
macro [%a == %b] =: ".."|nomsu.utils.equivalent(\%a as lua\, \%b as lua\) parse (%a == %b) as lua expr "nomsu.utils.equivalent(\(%a), \(%b))"
macro [%a != %b] =: ".."|(not nomsu.utils.equivalent(\%a as lua\, \%b as lua\)) parse (%a != %b) as lua expr "(not nomsu.utils.equivalent(\(%a), \(%b)))"
# Commutative Operators defined for up to 8 operands # Commutative Operators defined for up to 8 operands
# TODO: work out solution for commutative operators using more clever macros # TODO: work out solution for commutative operators using more clever macros
@ -125,5 +125,5 @@ lua block ".."
|end |end
# Unary operators # Unary operators
macro [- %a] =: ".."|-(\%a as lua\) parse (- %a) as lua expr "-(\(%a))"
macro [not %a] =: ".."|not (\%a as lua\) parse (not %a) as lua expr "not (\(%a))"

View File

@ -1,73 +1,66 @@
require "lib/metaprogramming.nom" require "lib/metaprogramming.nom"
# Error functions # Error functions
rule [error!, panic!, fail!, abort!] =: rule (error!; panic!; fail!; abort!) =:
nomsu "error"[] nomsu "error" []
rule [error %msg] =: rule (error %msg) =:
nomsu "error"[%msg] nomsu "error"[%msg]
macro statement [assert %condition] =: ".." parse (assert %condition) as: lua code ".."
|if not (\%condition as lua\) then |if not (\(%condition)) then
| nomsu:error() | nomsu:error()
|end |end
macro statement [assert %condition %msg] =: ".." parse (assert %condition %msg) as: lua code ".."
|if not (\%condition as lua\) then |if not (\(%condition)) then
| nomsu:error(\%msg as lua\) | nomsu:error(\(%msg))
|end |end
macro statement [show generated lua %block] =: ".." parse (show generated lua %block) as: lua code ".."
|nomsu:writeln(\lua expr "nomsu:repr(nomsu:tree_to_lua(vars.block.value))"\) |nomsu:writeln(\(repr (nomsu "tree_to_lua" [%block])))
# String functions # String functions
rule [join %strs] =: rule (join %strs) =:
lua block ".." lua block ".."
|local str_bits = {} |local str_bits = {}
|for i,bit in ipairs(vars.strs) do str_bits[i] = nomsu.utils.repr_if_not_string(bit) end |for i,bit in ipairs(vars.strs) do str_bits[i] = nomsu.utils.repr_if_not_string(bit) end
|return table.concat(str_bits) |return table.concat(str_bits)
rule [join %strs with glue %glue] =: rule (join %strs with glue %glue) =:
lua block ".." lua block ".."
|local str_bits = {} |local str_bits = {}
|for i,bit in ipairs(vars.strs) do str_bits[i] = nomsu.utils.repr_if_not_string(bit) end |for i,bit in ipairs(vars.strs) do str_bits[i] = nomsu.utils.repr_if_not_string(bit) end
|return table.concat(str_bits, vars.glue) |return table.concat(str_bits, vars.glue)
macro [capitalize %str, %str capitalized] =: ".." parse (capitalize %str; %str capitalized) as: lua expr "(\(%str)):gsub('%l', string.upper, 1)"
|(\%str as lua\):gsub("%l", string.upper, 1)
macro [repr %obj] =: parse (say %str) as: lua block ".."
".."|nomsu:repr(\%obj as lua\) |nomsu:writeln(nomsu.utils.repr_if_not_string(\(%str)))
macro [%obj as string] =:
".."|nomsu.utils.repr_if_not_string(\%obj as lua\)
macro [say %str] =:
".."|nomsu:writeln(nomsu.utils.repr_if_not_string(\%str as lua\))
# Number ranges # Number ranges
macro [%start to %stop] =: ".." parse (%start to %stop) as: lua expr ".."
|nomsu.utils.range(\%start as lua\, \%stop as lua\) |nomsu.utils.range(\(%start), \(%stop))
macro [%start to %stop by %step, %start to %stop via %step] =: ".." parse (%start to %stop by %step; %start to %stop via %step) as: lua expr ".."
|nomsu.utils.range(\%start as lua\, \%stop as lua\, \%step as lua\) |nomsu.utils.range(\(%start), \(%stop), \(%step))
# Common utility functions # Common utility functions
macro [random number, random, rand] =: "math.random()" parse (random number; random; rand) as: lua expr "math.random()"
macro [random int %n, random integer %n, randint %n] =: ".."|math.random(\%n as lua\) parse (random int %n; random integer %n; randint %n) as: lua expr "math.random(\(%n))"
macro [random from %low to %high, random number from %low to %high, rand %low %high] =: ".." parse (random from %low to %high; random number from %low to %high; rand %low %high) as:
|math.random(\%low as lua\, \%high as lua\) lua expr "math.random(\(%low), \(%high))"
rule [random choice from %elements, random choice %elements, random %elements] =: rule (random choice from %elements; random choice %elements; random %elements) =:
lua expr ".."|vars.elements[math.random(#vars.elements)] lua expr "vars.elements[math.random(#vars.elements)]"
macro [sum of %items, sum %items] =: ".."|nomsu.utils.sum(\%items as lua\) parse (sum of %items; sum %items) as: lua expr "nomsu.utils.sum(\(%items))"
macro [product of %items, product %items] =: ".."|nomsu.utils.product(\%items as lua\) parse (product of %items; product %items) as: lua expr "nomsu.utils.product(\(%items))"
macro [all of %items] =: ".."|nomsu.utils.all(\%items as lua\) parse (all of %items) as: lua expr "nomsu.utils.all(\(%items))"
macro [any of %items] =: ".."|nomsu.utils.any(\%items as lua\) parse (any of %items) as: lua expr "nomsu.utils.any(\(%items))"
# This is a rule, not a macro so we can use vars.items twice without running it twice. # This is a rule, not a macro so we can use vars.items twice without running it twice.
rule [avg of %items, average of %items] =: rule (avg of %items; average of %items) =:
lua expr ".."|(nomsu.utils.sum(vars.items)/#vars.items) lua expr "(nomsu.utils.sum(vars.items)/#vars.items)"
macro [min of %items, smallest of %items, lowest of %items] =: parse (min of %items; smallest of %items; lowest of %items) as:
".."|nomsu.utils.min(\%items as lua\) lua expr "nomsu.utils.min(\(%items))"
macro [max of %items, biggest of %items, largest of %items, highest of %items] =: parse (max of %items; biggest of %items; largest of %items; highest of %items) as:
".."|nomsu.utils.min(\%items as lua\) lua expr "nomsu.utils.max(\(%items))"
macro [min of %items with respect to %keys] =: parse (min of %items with respect to %keys) as:
".."|nomsu.utils.min(\%items as lua\, \%keys as lua\) lua expr "nomsu.utils.min(\(%items), \(%keys))"
macro [max of %items with respect to %keys] =: parse (max of %items with respect to %keys) as:
".."|nomsu.utils.max(\%items as lua\, \%keys as lua\) lua expr "nomsu.utils.max(\(%items), \(%keys))"

263
nomsu.lua
View File

@ -7,10 +7,6 @@ do
local _obj_0 = table local _obj_0 = table
insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat
end end
local pcall
pcall = function(fn, ...)
return true, fn(...)
end
lpeg.setmaxstack(10000) lpeg.setmaxstack(10000)
local P, V, S, Cg, C, Cp, B, Cmt 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 P, V, S, Cg, C, Cp, B, Cmt = lpeg.P, lpeg.V, lpeg.S, lpeg.Cg, lpeg.C, lpeg.Cp, lpeg.B, lpeg.Cmt
@ -65,13 +61,13 @@ local nomsu = [=[ file <- ({ {| shebang?
inline_block <- ({ {| "(" inline_statements ")" |} }) -> Block inline_block <- ({ {| "(" inline_statements ")" |} }) -> Block
eol_block <- ({ {| ":" %ws? noeol_statements eol |} }) -> Block eol_block <- ({ {| ":" %ws? noeol_statements eol |} }) -> Block
indented_block <- ({ {| (":" / "(..)") indent indented_block <- ({ {| (":" / "(..)") indent
statements statements (nodent statements)*
(dedent / (({.+} ("" -> "Error while parsing block")) => error)) (dedent / (({.+} ("" -> "Error while parsing block")) => error))
|} }) -> Block |} }) -> Block
inline_nomsu <- ({ ("\" inline_block ) }) -> Nomsu inline_nomsu <- ({ ("\" inline_expression) }) -> Nomsu
eol_nomsu <- ({ ("\" eol_block ) }) -> Nomsu eol_nomsu <- ({ ("\" noeol_expression) }) -> Nomsu
indented_nomsu <- ({ ("\" {indented_block} ) }) -> Nomsu indented_nomsu <- ({ ("\" expression) }) -> Nomsu
inline_expression <- number / variable / inline_string / inline_list / inline_block / inline_nomsu inline_expression <- number / variable / inline_string / inline_list / inline_block / inline_nomsu
noeol_expression <- indented_string / indented_block / indented_nomsu / indented_list / inline_expression noeol_expression <- indented_string / indented_block / indented_nomsu / indented_list / inline_expression
@ -192,27 +188,15 @@ do
return self:write("\n") return self:write("\n")
end, end,
def = function(self, invocation, thunk, src) def = function(self, invocation, thunk, src)
if type(invocation) ~= 'string' then local stub, arg_names = self:get_stub(invocation)
self:error("Invocation should be string, not: " .. tostring(repr(invocation))) assert(stub, "NO STUB FOUND: " .. tostring(repr(invocation)))
end
if self.debug then if self.debug then
self:writeln("Defining rule: " .. tostring(repr(invocation))) self:writeln("Defining rule: " .. tostring(repr(stub)) .. " with args " .. tostring(repr(arg_names)))
end end
local stub = invocation:gsub("'", " '"):gsub("%%%S+", "%%"):gsub("%s+", " ") for i = 1, #arg_names - 1 do
local args for j = i + 1, #arg_names do
do if arg_names[i] == arg_names[j] then
local _accum_0 = { } self:error("Duplicate argument in function " .. tostring(stub) .. ": '" .. tostring(arg_names[i]) .. "'")
local _len_0 = 1
for arg in invocation:gmatch("%%(%S[^%s']*)") do
_accum_0[_len_0] = arg
_len_0 = _len_0 + 1
end
args = _accum_0
end
for i = 1, #args - 1 do
for j = i + 1, #args do
if args[i] == args[j] then
self:error("Duplicate argument in function def: " .. tostring(args[i]))
end end
end end
end end
@ -220,11 +204,11 @@ do
local _with_0 = { local _with_0 = {
thunk = thunk, thunk = thunk,
invocation = invocation, invocation = invocation,
args = args, arg_names = arg_names,
src = src, src = src,
is_macro = false is_macro = false
} }
self.defs[invocation] = _with_0 self.defs[stub] = _with_0
local _ = nil local _ = nil
return _with_0 return _with_0
end end
@ -236,30 +220,31 @@ do
return _with_0 return _with_0
end end
end, end,
call = function(self, alias, ...) call = function(self, stub, ...)
local def = self.defs[alias] local def = self.defs[stub]
if def == nil then if def == nil then
self:error("Attempt to call undefined function: " .. tostring(alias)) self:error("Attempt to call undefined function: " .. tostring(stub))
end end
if def.is_macro and self.callstack[#self.callstack] ~= "#macro" then if def.is_macro and self.callstack[#self.callstack] ~= "#macro" then
self:error("Attempt to call macro at runtime: " .. tostring(alias) .. "\nThis can be caused by using a macro in a function that is defined before the macro.") self:error("Attempt to call macro at runtime: " .. tostring(stub) .. "\nThis can be caused by using a macro in a function that is defined before the macro.")
end end
if not (self:check_permission(def)) then if not (self:check_permission(def)) then
self:error("You do not have the authority to call: " .. tostring(alias)) self:error("You do not have the authority to call: " .. tostring(stub))
end end
local thunk, args local thunk, arg_names
thunk, args = def.thunk, def.args thunk, arg_names = def.thunk, def.arg_names
local args
do do
local _tbl_0 = { } local _tbl_0 = { }
for i, name in ipairs(args) do for i, name in ipairs(arg_names) do
_tbl_0[name] = select(i, ...) _tbl_0[name] = select(i, ...)
end end
args = _tbl_0 args = _tbl_0
end end
if self.debug then if self.debug then
self:writeln("Calling " .. tostring(repr(alias)) .. " with args: " .. tostring(repr(args))) self:writeln("Calling " .. tostring(repr(stub)) .. " with args: " .. tostring(repr(args)))
end end
insert(self.callstack, alias) insert(self.callstack, stub)
local rets = { local rets = {
thunk(self, args) thunk(self, args)
} }
@ -270,10 +255,9 @@ do
if kind == nil then if kind == nil then
kind = "Expression" kind = "Expression"
end end
local args, alias local stub, args = self:get_stub(tree)
alias, args = self:get_alias(tree)
insert(self.callstack, "#macro") insert(self.callstack, "#macro")
local expr, statement = self:call(alias, unpack(args)) local expr, statement = self:call(stub, unpack(args))
remove(self.callstack) remove(self.callstack)
return expr, statement return expr, statement
end, end,
@ -319,13 +303,17 @@ do
run = function(self, src, filename) run = function(self, src, filename)
local tree = self:parse(src, filename) local tree = self:parse(src, filename)
assert(tree, "Tree failed to compile: " .. tostring(src)) assert(tree, "Tree failed to compile: " .. tostring(src))
assert(tree.type == "File") assert(tree.type == "File", "Attempt to run non-file: " .. tostring(tree.type))
local buffer = { } local buffer = { }
local vars = { } local vars = { }
local return_value = nil local return_value = nil
local _list_0 = tree.value local _list_0 = tree.value
for _index_0 = 1, #_list_0 do for _index_0 = 1, #_list_0 do
local statement = _list_0[_index_0] local statement = _list_0[_index_0]
if self.debug then
self:writeln("RUNNING TREE:")
self:print_tree(statement)
end
local ok, expr, statements = pcall(self.tree_to_lua, self, statement) local ok, expr, statements = pcall(self.tree_to_lua, self, statement)
if not ok then if not ok then
self:writeln("Error occurred in statement:\n" .. tostring(statement.src)) self:writeln("Error occurred in statement:\n" .. tostring(statement.src))
@ -350,7 +338,7 @@ do
end end
if not ok then if not ok then
self:writeln("Error occurred in statement:\n" .. tostring(statement.src)) self:writeln("Error occurred in statement:\n" .. tostring(statement.src))
self:error(return_value) self:error(repr(return_value))
end end
insert(buffer, tostring(statements or '') .. "\n" .. tostring(expr and "ret = " .. tostring(expr) or '')) insert(buffer, tostring(statements or '') .. "\n" .. tostring(expr and "ret = " .. tostring(expr) or ''))
end end
@ -372,6 +360,7 @@ do
tree_to_lua = function(self, tree) tree_to_lua = function(self, tree)
assert(tree, "No tree provided.") assert(tree, "No tree provided.")
if not tree.type then if not tree.type then
self:writeln(debug.traceback())
self:error("Invalid tree: " .. tostring(repr(tree))) self:error("Invalid tree: " .. tostring(repr(tree)))
end end
local _exp_0 = tree.type local _exp_0 = tree.type
@ -379,15 +368,12 @@ do
return error("Should not be converting File to lua through this function.") return error("Should not be converting File to lua through this function.")
elseif "Nomsu" == _exp_0 then elseif "Nomsu" == _exp_0 then
return repr(tree.value), nil return repr(tree.value), nil
elseif "Block" == _exp_0 then elseif "Thunk" == _exp_0 then
local lua_bits = { } local lua_bits = { }
local _list_0 = tree.value local _list_0 = tree.value.value
for _index_0 = 1, #_list_0 do for _index_0 = 1, #_list_0 do
local arg = _list_0[_index_0] local arg = _list_0[_index_0]
local expr, statement = self:tree_to_lua(arg) local expr, statement = self:tree_to_lua(arg)
if expr and not statement and #tree.value == 1 then
return expr, nil
end
if statement then if statement then
insert(lua_bits, statement) insert(lua_bits, statement)
end end
@ -395,18 +381,34 @@ do
insert(lua_bits, "ret = " .. tostring(expr)) insert(lua_bits, "ret = " .. tostring(expr))
end end
end end
return ([[ function(nomsu, vars) return ([[ (function(nomsu, vars)
local ret local ret
%s %s
return ret return ret
end]]):format(concat(lua_bits, "\n")) end)]]):format(concat(lua_bits, "\n"))
elseif "Block" == _exp_0 then
if #tree.value == 0 then
return "nil", nil
end
if #tree.value == 1 then
local expr, statement = self:tree_to_lua(tree.value[1])
if not statement then
return expr, nil
end
end
local thunk_lua = self:tree_to_lua({
type = "Thunk",
value = tree,
src = tree.src
})
return ("%s(nomsu, vars)"):format(thunk_lua), nil
elseif "FunctionCall" == _exp_0 then elseif "FunctionCall" == _exp_0 then
local alias = self:get_alias(tree) local stub = self:get_stub(tree)
if self.defs[alias] and self.defs[alias].is_macro then if self.defs[stub] and self.defs[stub].is_macro then
return self:run_macro(tree, "Expression") return self:run_macro(tree, "Expression")
end end
local args = { local args = {
repr(alias) repr(stub)
} }
local _list_0 = tree.value local _list_0 = tree.value
for _index_0 = 1, #_list_0 do for _index_0 = 1, #_list_0 do
@ -460,6 +462,9 @@ do
if string_buffer ~= "" then if string_buffer ~= "" then
insert(concat_parts, repr(string_buffer)) insert(concat_parts, repr(string_buffer))
end end
if #concat_parts == 0 then
return "''", nil
end
return "(" .. tostring(concat(concat_parts, "..")) .. ")", nil return "(" .. tostring(concat(concat_parts, "..")) .. ")", nil
elseif "List" == _exp_0 then elseif "List" == _exp_0 then
local items = { } local items = { }
@ -481,25 +486,49 @@ do
return self:error("Unknown/unimplemented thingy: " .. tostring(tree.type)) return self:error("Unknown/unimplemented thingy: " .. tostring(tree.type))
end end
end, end,
print_tree = function(self, tree, ind) walk_tree = function(self, tree, depth)
if ind == nil then if depth == nil then
ind = "" depth = 0
end end
coroutine.yield(tree, depth)
if type(tree) ~= 'table' or not tree.type then if type(tree) ~= 'table' or not tree.type then
self:writeln(tostring(ind) .. tostring(repr(tree)))
return return
end end
self:writeln(tostring(ind) .. tostring(tree.type) .. ":")
local _exp_0 = tree.type local _exp_0 = tree.type
if "List" == _exp_0 or "File" == _exp_0 or "Block" == _exp_0 or "FunctionCall" == _exp_0 or "String" == _exp_0 then if "List" == _exp_0 or "File" == _exp_0 or "Nomsu" == _exp_0 or "Block" == _exp_0 or "FunctionCall" == _exp_0 or "String" == _exp_0 then
local _list_0 = tree.value local _list_0 = tree.value
for _index_0 = 1, #_list_0 do for _index_0 = 1, #_list_0 do
local v = _list_0[_index_0] local v = _list_0[_index_0]
self:print_tree(v, ind .. " ") self:walk_tree(v, depth + 1)
end end
else else
return self:print_tree(tree.value, ind .. " ") self:walk_tree(tree.value, depth + 1)
end end
return nil
end,
print_tree = function(self, tree)
for node, depth in coroutine.wrap(function()
return self:walk_tree(tree)
end) do
if type(node) ~= 'table' or not node.type then
self:writeln((" "):rep(depth) .. repr(node))
else
self:writeln(tostring((" "):rep(depth)) .. tostring(node.type) .. ":")
end
end
end,
tree_to_str = function(self, tree)
local bits = { }
for node, depth in coroutine.wrap(function()
return self:walk_tree(tree)
end) do
if type(node) ~= 'table' or not node.type then
insert(bits, ((" "):rep(depth) .. repr(node)))
else
insert(bits, (tostring((" "):rep(depth)) .. tostring(node.type) .. ":"))
end
end
return concat(bits, "\n")
end, end,
replaced_vars = function(self, tree, vars) replaced_vars = function(self, tree, vars)
if type(tree) ~= 'table' then if type(tree) ~= 'table' then
@ -510,8 +539,8 @@ do
if vars[tree.value] then if vars[tree.value] then
tree = vars[tree.value] tree = vars[tree.value]
end end
elseif "File" == _exp_0 or "Thunk" == _exp_0 or "Statement" == _exp_0 or "Block" == _exp_0 or "List" == _exp_0 or "FunctionCall" == _exp_0 or "String" == _exp_0 then elseif "File" == _exp_0 or "Nomsu" == _exp_0 or "Thunk" == _exp_0 or "Block" == _exp_0 or "List" == _exp_0 or "FunctionCall" == _exp_0 or "String" == _exp_0 then
local new_value = self:replaced_vars(tree.value) local new_value = self:replaced_vars(tree.value, vars)
if new_value ~= tree.value then if new_value ~= tree.value then
do do
local _tbl_0 = { } local _tbl_0 = { }
@ -526,7 +555,7 @@ do
local new_values = { } local new_values = { }
local any_different = false local any_different = false
for k, v in pairs(tree) do for k, v in pairs(tree) do
new_values[k] = self:replaced_vars(v) new_values[k] = self:replaced_vars(v, vars)
any_different = any_different or (new_values[k] ~= tree[k]) any_different = any_different or (new_values[k] ~= tree[k])
end end
if any_different then if any_different then
@ -535,84 +564,47 @@ do
end end
return tree return tree
end, end,
get_alias = function(self, x) get_stub = function(self, x)
if not x then if not x then
self:error("Nothing to get alias from") self:error("Nothing to get stub from")
end end
if type(x) == 'string' then if type(x) == 'string' then
local alias = x:gsub("'", " '"):gsub("%%%S+", "%%"):gsub("%s+", " ") local stub = x:gsub("'", " '"):gsub("%%%S+", "%%"):gsub("%s+", " ")
local args local args
do do
local _accum_0 = { } local _accum_0 = { }
local _len_0 = 1 local _len_0 = 1
for arg in x:gmatch("%%(%S[^%s']*)") do for arg in x:gmatch("%%([^%s']*)") do
_accum_0[_len_0] = arg _accum_0[_len_0] = arg
_len_0 = _len_0 + 1 _len_0 = _len_0 + 1
end end
args = _accum_0 args = _accum_0
end end
return alias, args return stub, args
end end
local _exp_0 = x.type local _exp_0 = x.type
if "String" == _exp_0 then if "String" == _exp_0 then
return self:get_alias(x.value) return self:get_stub(x.value)
elseif "Statement" == _exp_0 then
return self:get_alias(x.value)
elseif "FunctionCall" == _exp_0 then elseif "FunctionCall" == _exp_0 then
local alias, args = { }, { }, { } local stub, args = { }, { }, { }
local _list_0 = x.value local _list_0 = x.value
for _index_0 = 1, #_list_0 do for _index_0 = 1, #_list_0 do
local token = _list_0[_index_0] local token = _list_0[_index_0]
local _exp_1 = token.type local _exp_1 = token.type
if "Word" == _exp_1 then if "Word" == _exp_1 then
insert(alias, token.value) insert(stub, token.value)
elseif "Var" == _exp_1 then elseif "Var" == _exp_1 then
insert(alias, "%") insert(stub, "%")
insert(args, token.value) insert(args, token.value)
else else
insert(alias, "%") insert(stub, "%")
insert(args, token) insert(args, token)
end end
end end
return concat(alias, " "), args return concat(stub, " "), args
end
end,
get_aliases = function(self, x)
if not x then
self:error("Nothing to get aliases from")
end
if type(x) == 'string' then
local alias, args = self:get_alias(x)
return {
[alias] = args
}
end
local _exp_0 = x.type
if "String" == _exp_0 then
return self:get_aliases({
x.value
})
elseif "Statement" == _exp_0 then
return self:get_aliases({
x.value
})
elseif "FunctionCall" == _exp_0 then
return self:get_aliases({
x
})
elseif "List" == _exp_0 then
x = x.value
elseif "Block" == _exp_0 then elseif "Block" == _exp_0 then
x = x.value self:writeln(debug.traceback())
end return self:error("Please pass in a single line from a block, not the whole thing:\n" .. tostring(self:tree_to_str(x)))
do
local _with_0 = { }
for _index_0 = 1, #x do
local y = x[_index_0]
local alias, args = self:get_alias(y)
_with_0[alias] = args
end
return _with_0
end end
end, end,
var_to_lua_identifier = function(self, var) var_to_lua_identifier = function(self, var)
@ -629,7 +621,9 @@ do
end, end,
error = function(self, ...) error = function(self, ...)
self:writeln("ERROR!") self:writeln("ERROR!")
self:writeln(...) if select(1, ...) then
self:writeln(...)
end
self:writeln("Callstack:") self:writeln("Callstack:")
for i = #self.callstack, 1, -1 do for i = #self.callstack, 1, -1 do
self:writeln(" " .. tostring(self.callstack[i])) self:writeln(" " .. tostring(self.callstack[i]))
@ -639,17 +633,22 @@ do
return error() return error()
end, end,
initialize_core = function(self) initialize_core = function(self)
self:defmacro("lua code %statements with value %value", function(self, vars) local lua_code
local inner_vars = setmetatable({ }, { lua_code = function(self, vars)
__index = function(_, key) local inner_vars = vars
return "vars[" .. tostring(repr(key)) .. "]" local lua = self:tree_to_value(vars.code, inner_vars)
end return nil, lua
}) end
local statements = self:tree_to_value(vars.statements, inner_vars) self:defmacro("lua code %code", lua_code)
local value = self:tree_to_value(vars.value, inner_vars) local lua_value
return value, statements lua_value = function(self, vars)
end) local inner_vars = vars
self:def("require %filename", function(self, vars) local lua = self:tree_to_value(vars.code, inner_vars)
return lua, nil
end
self:defmacro("lua value %code", lua_value)
local _require
_require = function(self, vars)
if not self.loaded_files[vars.filename] then if not self.loaded_files[vars.filename] then
local file = io.open(vars.filename) local file = io.open(vars.filename)
if not file then if not file then
@ -658,14 +657,17 @@ do
self.loaded_files[vars.filename] = (self:run(file:read('*a'), vars.filename)) or true self.loaded_files[vars.filename] = (self:run(file:read('*a'), vars.filename)) or true
end end
return self.loaded_files[vars.filename] return self.loaded_files[vars.filename]
end) end
return self:def("run file %filename", function(self, vars) self:def("require %filename", _require)
local run_file
run_file = function(self, vars)
local file = io.open(vars.filename) local file = io.open(vars.filename)
if not file then if not file then
self:error("File does not exist: " .. tostring(vars.filename)) self:error("File does not exist: " .. tostring(vars.filename))
end end
return self:run(file:read('*a'), vars.filename) return self:run(file:read('*a'), vars.filename)
end) end
return self:def("run file %filename", run_file)
end end
} }
_base_0.__index = _base_0 _base_0.__index = _base_0
@ -726,7 +728,6 @@ do
end end
if arg and arg[1] then if arg and arg[1] then
local c = NomsuCompiler() local c = NomsuCompiler()
c.debug = true
local input = io.open(arg[1]):read("*a") local input = io.open(arg[1]):read("*a")
local _write = c.write local _write = c.write
if arg[2] == "-" then if arg[2] == "-" then

View File

@ -70,7 +70,7 @@ nomsu = [=[
inline_block <- ({ {| "(" inline_statements ")" |} }) -> Block inline_block <- ({ {| "(" inline_statements ")" |} }) -> Block
eol_block <- ({ {| ":" %ws? noeol_statements eol |} }) -> Block eol_block <- ({ {| ":" %ws? noeol_statements eol |} }) -> Block
indented_block <- ({ {| (":" / "(..)") indent indented_block <- ({ {| (":" / "(..)") indent
statements statements (nodent statements)*
(dedent / (({.+} ("" -> "Error while parsing block")) => error)) (dedent / (({.+} ("" -> "Error while parsing block")) => error))
|} }) -> Block |} }) -> Block
@ -329,6 +329,8 @@ class NomsuCompiler
end)]])\format(concat lua_bits, "\n") end)]])\format(concat lua_bits, "\n")
when "Block" when "Block"
if #tree.value == 0
return "nil",nil
if #tree.value == 1 if #tree.value == 1
expr,statement = @tree_to_lua tree.value[1] expr,statement = @tree_to_lua tree.value[1]
if not statement if not statement
@ -367,6 +369,8 @@ class NomsuCompiler
if string_buffer ~= "" if string_buffer ~= ""
insert concat_parts, repr(string_buffer) insert concat_parts, repr(string_buffer)
if #concat_parts == 0
return "''", nil
return "(#{concat(concat_parts, "..")})", nil return "(#{concat(concat_parts, "..")})", nil
when "List" when "List"
@ -502,13 +506,13 @@ class NomsuCompiler
initialize_core: => initialize_core: =>
-- Sets up some core functionality -- Sets up some core functionality
lua_code = (vars)=> lua_code = (vars)=>
inner_vars = setmetatable({}, {__index:(_,key)-> "vars[#{repr(key)}]"}) inner_vars = vars-- setmetatable({}, {__index:(_,key)-> "vars[#{repr(key)}]"})
lua = @tree_to_value(vars.code, inner_vars) lua = @tree_to_value(vars.code, inner_vars)
return nil, lua return nil, lua
@defmacro "lua code %code", lua_code @defmacro "lua code %code", lua_code
lua_value = (vars)=> lua_value = (vars)=>
inner_vars = setmetatable({}, {__index:(_,key)-> "vars[#{repr(key)}]"}) inner_vars = vars--setmetatable({}, {__index:(_,key)-> "vars[#{repr(key)}]"})
lua = @tree_to_value(vars.code, inner_vars) lua = @tree_to_value(vars.code, inner_vars)
return lua, nil return lua, nil
@defmacro "lua value %code", lua_value @defmacro "lua value %code", lua_value

View File

@ -1,6 +1,9 @@
local utils local utils
utils = { utils = {
is_list = function(t) is_list = function(t)
if type(t) ~= 'table' then
return false
end
local i = 1 local i = 1
for _ in pairs(t) do for _ in pairs(t) do
if t[i] == nil then if t[i] == nil then