aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bitbucket@bruce-hill.com>2017-09-05 21:20:11 -0700
committerBruce Hill <bitbucket@bruce-hill.com>2017-09-05 21:20:11 -0700
commit2e9a3cdf009e80caced01f81dc34d2968346d429 (patch)
tree7f109784b50fc094609c0916abd91f519ad6eb51
parent86c0fd47eb4b227446d6ca2286cb4b078176144e (diff)
Added longstrings and removed indentification step.
-rwxr-xr-xcore.moon131
-rwxr-xr-xgame2.moon168
-rw-r--r--nomic.moon340
-rw-r--r--utils.moon4
4 files changed, 321 insertions, 322 deletions
diff --git a/core.moon b/core.moon
index f1847c3..4f832f8 100755
--- a/core.moon
+++ b/core.moon
@@ -1,7 +1,86 @@
#!/usr/bin/env moon
-nomic = require 'nomic'
+Nomic = require 'nomic'
utils = require 'utils'
-g = nomic()
+
+
+class PermissionNomic extends Nomic
+ new: (...)=>
+ super(...)
+ @callstack = {}
+
+ call: (fn_name,...)=>
+ fn_info = @defs[fn_name]
+ if fn_info == nil
+ error "Attempt to call undefined function: #{fn_name}"
+ unless self\check_permission(fn_name)
+ error "You do not have the authority to call: #{fn_name}"
+ table.insert @callstack, fn_name
+ {:fn, :arg_names} = fn_info
+ args = {name, select(i,...) for i,name in ipairs(arg_names)}
+ if @debug
+ print "Calling #{fn_name} with args: #{utils.repr(args)}"
+ ret = fn(self, args)
+ table.remove @callstack
+ return ret
+
+ check_permission: (fn_name)=>
+ fn_info = @defs[fn_name]
+ if fn_info == nil
+ error "Undefined function: #{fn_name}"
+ if fn_info.whitelist == nil then return true
+ for caller in *@callstack
+ if fn_info.whitelist[caller]
+ return true
+ return false
+
+
+g = PermissionNomic()
+
+g\def {"restrict %fn to %whitelist"}, (vars)=>
+ fns = if type(vars.fn) == 'string' then {vars.fn} else vars.fn
+ whitelist = if type(vars.whitelist) == 'string' then {vars.whitelist} else vars.whitelist
+ whitelist = {w,true for w in *whitelist}
+ for fn in *fns
+ fn_info = @defs[fn]
+ if fn_info == nil
+ print "Undefined function: #{fn}"
+ continue
+ unless self\check_permission(fn)
+ print "You do not have permission to restrict function: #{fn}"
+ continue
+ @defs[fn] = whitelist
+
+g\def {"allow %whitelist to %fn"}, (vars)=>
+ fns = if type(vars.fn) == 'string' then {vars.fn} else vars.fn
+ whitelist = if type(vars.whitelist) == 'string' then {vars.whitelist} else vars.whitelist
+ for fn in *fns
+ fn_info = @defs[fn]
+ if fn_info == nil
+ print "Undefined function: #{fn}"
+ continue
+ if fn_info.whitelist == nil
+ print "Function is already allowed by everyone: #{fn}"
+ continue
+ unless self\check_permission(fn)
+ print "You do not have permission to grant permissions for function: #{fn}"
+ continue
+ for w in *whitelist do fn_info.whitelist[w] = true
+
+g\def {"forbid %blacklist to %fn"}, (vars)=>
+ fns = if type(vars.fn) == 'string' then {vars.fn} else vars.fn
+ blacklist = if type(vars.blacklist) == 'string' then {vars.blacklist} else vars.blacklist
+ for fn in *fns
+ fn_info = @defs[fn]
+ if fn_info == nil
+ print "Undefined function: #{fn}"
+ continue
+ if fn_info.whitelist == nil
+ print "Cannot remove items from a whitelist when there is no whitelist on function: #{fn}"
+ continue
+ unless self\check_permission(fn)
+ print "You do not have permission to restrict function: #{fn}"
+ continue
+ for b in *blacklist do fn_info.whitelist[b] = nil
g\def {"say %x", "print %x"}, (vars)=>
@@ -41,8 +120,10 @@ infix = (ops)->
alias = op
if type(op) == 'table'
{alias,op} = op
- g\defmacro "%x #{op} %y", (vars,helpers,ftype)=>
- helpers.lua("(#{helpers.var('x')} #{op} #{helpers.var('y')})")
+ g\defmacro "%x #{alias} %y", (vars,helpers,ftype)=>
+ value = "(#{helpers.var('x')} #{op} #{helpers.var('y')})"
+ if ftype == "Expression" then helpers.lua(value)
+ else helpers.lua("ret = #{value}")
unary = (ops)->
for op in *ops
g\defmacro "#{op} %x", (vars,helpers,ftype)=>
@@ -51,7 +132,7 @@ unary = (ops)->
singleton {"true","yes"}, "true"
singleton {"false","no"}, "false"
singleton {"nil","null","nop","pass"}, "nil"
-infix{"+","-","*","/","==",{"!=","~="},"<","<=",">",">=","^","and","or"}
+infix{"+","-","*","/","==",{"!=","~="},"<","<=",">",">=","^","and","or",{"mod","%"}}
unary{"-","#","not"}
g\def [[%x == %y]], (args)=> utils.equivalent(args.x, args.y)
@@ -176,6 +257,45 @@ g\defmacro "for %varname in %iterable %body", (vars,helpers,ftype)=>
.lua "end"
return nil
+g\defmacro "for %varname = %start to %stop %body", (vars,helpers,ftype)=>
+ with helpers
+ switch ftype
+ when "Expression"
+ .lua "(function(game, vars)"
+ .indented ->
+ .lua "local comprehension, vars = {}, setmetatable({}, {__index=vars})"
+ .lua "for value=(#{.ded(.transform(vars.start))}),(#{.ded(.transform(vars.stop))}) do"
+ .indented ->
+ .lua "local comp_value"
+ .lua "vars[#{.ded(.transform(vars.varname))}] = value"
+ body = vars.body
+ while body.type != "Block"
+ body = body.value
+ if body == nil then error("Failed to find body.")
+ for statement in *body.value
+ -- TODO: Clean up this ugly bit
+ .lua("comp_value = "..(.ded(.transform(statement.value, {type:"Expression"}))))
+ .lua "table.insert(comprehension, comp_value)"
+ .lua "end"
+ .lua "return comprehension"
+ .lua "end)(game,vars)"
+ when "Statement"
+ .lua "do"
+ .indented ->
+ .lua "local vars = setmetatable({}, {__index=vars})"
+ .lua "for value=(#{.ded(.transform(vars.start))}),(#{.ded(.transform(vars.stop))}) do"
+ .indented ->
+ .lua "vars[#{.ded(.transform(vars.varname))}] = value"
+ body = vars.body
+ while body.type != "Block"
+ body = body.value
+ if body == nil then error("Failed to find body.")
+ for statement in *body.value
+ .lua(.ded(.transform(statement)))
+ .lua "end"
+ .lua "end"
+ return nil
+
g\simplemacro "if %condition %body", [[
if %condition %body
..else: pass
@@ -205,4 +325,5 @@ g\defmacro [[lua %lua_code]], (vars,helpers,ftype)=>
g\defmacro [[macro %spec %body]], (vars,helpers,ftype)=>
self\simplemacro vars.spec.value.value, vars.body.value.value.src
+
return g
diff --git a/game2.moon b/game2.moon
index d26d368..3d5f1ae 100755
--- a/game2.moon
+++ b/game2.moon
@@ -6,157 +6,6 @@ g = Game(require'core')
print("===========================================================================================")
-g\test[[
-say "foo"
-===
-Call [say %]:
- "foo"
-]]
-
-g\test[[
-say (4)
-===
-Call [say %]:
- 4
-]]
-
-g\test[[
-rule "fart": say "poot"
-===
-Call [rule % %]:
- "fart"
- Thunk:
- Call [say %]:
- "poot"
-]]
-
-g\test[[
-rule "doublefart":
- say "poot"
- say "poot"
-===
-Call [rule % %]:
- "doublefart"
- Thunk:
- Call [say %]:
- "poot"
- Call [say %]:
- "poot"
-]]
-
-g\test[[
-say (subexpressions work)
-===
-Call [say %]:
- Call [subexpressions work]!
-]]
-
-g\test[[
-say ["lists", "work"]
-===
-Call [say %]:
- List:
- "lists"
- "work"
-]]
-
-g\test[[
-say []
-===
-Call [say %]:
- <Empty List>
-]]
-
-g\test[[
-say [..]
- 1, 2
- 3
-===
-Call [say %]:
- List:
- 1
- 2
- 3
-]]
-
-g\test[[
-say both [..]
- 1,2
-..and [..]
- 3,4
-===
-Call [say both % and %]:
- List:
- 1
- 2
- List:
- 3
- 4
-]]
-
-g\test[[
-say both..
- "hello"
- and "world"
-===
-Call [say both % and %]:
- "hello"
- "world"
-]]
-
-g\test[[
-say both ..
- "a list:"
- and [..]
- 1,2,(three),(4)
-===
-Call [say both % and %]:
- "a list:"
- List:
- 1
- 2
- Call [three]!
- 4
-]]
-
-g\test[[
-if 1: yes
-..else: no
-===
-Call [if % % else %]:
- 1
- Thunk:
- Call [yes]!
- Thunk:
- Call [no]!
-]]
-g\test[[
-if 1: yes ..else: no
-===
-Call [if % % else %]:
- 1
- Thunk:
- Call [yes]!
- Thunk:
- Call [no]!
-]]
-g\test[[
-say (do: return 5)
-===
-Call [say %]:
- Call [do %]:
- Thunk:
- Call [return %]:
- 5
-]]
-g\test[[
-say (..)
- fn call
-===
-Call [say %]:
- Call [fn call]!
-]]
-
g\run[[
say [..]
@@ -257,3 +106,20 @@ g\run[[
smet "fnord" = 23
say %fnord
]]
+
+g\run[[
+
+rule "fizz buzz":
+ for "i" = 1 to 100:
+ if ((%i mod 15) == 0):
+ say "fizzbuzz"
+ ..else: if ((%i mod 3) == 0):
+ say "fizz"
+ ..else: if ((%i mod 5) == 0):
+ say "buzz"
+ ..else:
+ say %i
+
+fizz buzz
+
+]]
diff --git a/nomic.moon b/nomic.moon
index 4a7ecf3..3c14f7d 100644
--- a/nomic.moon
+++ b/nomic.moon
@@ -4,8 +4,10 @@ utils = require 'utils'
moon = require 'moon'
lpeg.setmaxstack 10000 -- whoa
+{:P,:V,:S,:Cg,:C,:Cp,:B,:Cmt} = lpeg
-linebreak = lpeg.P("\r")^-1 * lpeg.P("\n")
+wordchar = P(1)-S(' \t\n\r%:;,.{}[]()"')
+spaces = S(" \t")^1
get_line_indentation = (line)->
indent_amounts = {[" "]:1, ["\t"]:4}
@@ -14,158 +16,27 @@ get_line_indentation = (line)->
for c in leading_space\gmatch "[\t ]"
sum += indent_amounts[c]
-pos_to_line = (str,pos)->
- line_no = 1
- for line in str\gmatch("[^%n]+")
- if #line >= pos then return line, line_no
- pos -= (#line + 1)
- line_no += 1
- error "Failed to find position #{pos} in str"
-
-pos_to_line_no = (str,pos)->
- with line = 1
- for _ in str\sub(1, pos)\gmatch("\n")
- line += 1
-
-add_indent_tokens = (str)->
- indent_stack = {0}
- result = {}
- -- TODO: Store mapping from new line numbers to old ones
- defs =
- linebreak: linebreak
- process_line: (line)->
- -- Remove blank lines
- unless line\match"[^ \t\n]"
- return
- indent = get_line_indentation(line)
- if indent > indent_stack[#indent_stack]
- table.insert result, "{\n "
- table.insert indent_stack, indent
- elseif indent < indent_stack[#indent_stack]
- dedents = 0
- tokens = {}
- while indent < indent_stack[#indent_stack]
- table.remove indent_stack
- table.insert tokens, "}"
- table.insert tokens, " "
- table.insert result, table.concat(tokens, "\n")
- else
- table.insert result, " "
- -- Delete leading whitespace
- --line = line\gsub("[ \t]*", "", 1)
- -- Delete trailing whitespace and carriage returns
- line = line\gsub("[ \t\r]*\n", "\n", 1)
- table.insert result, line
-
- indentflagger = [=[
- file <- line*
- line <- ((string / [^%linebreak])* %linebreak) -> process_line
- string <- '"' (("\" .) / [^"])* '"'
- ]=]
- indentflagger = re.compile indentflagger, defs
- indentflagger\match(str.."\n")
- while #indent_stack > 1
- table.remove indent_stack
- table.insert result, "}\n"
- return (table.concat result)\sub(1,-2)
-
-lingo = [=[
- file <- ({ {| {:body: (" " block) :} ({:errors: errors :})? |} }) -> File
- errors <- ({ {.+} }) -> Errors
- block <- ({ {| statement (%nodent 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 %nodent indented_fn_bits %dedent) (%nodent ".." %ws? fn_bits)?)
- / (%nodent ".." fn_bit fn_bits)
- / (fn_bit (%word_boundary fn_bits)?))
- indented_fn_bits <-
- fn_bit ((%nodent / %word_boundary) indented_fn_bits)?
-
- thunk <-
- ({ ":" %ws?
- ((%indent %nodent block %dedent (%nodent "..")?)
- / (one_liner (%ws? ((%nodent? "..")))?)) }) -> Thunk
-
- word <- ({ !number {%wordchar+} }) -> Word
- expression <- ({ (string / number / variable / list / thunk / subexpression) }) -> Expression
-
- string <- ({ '"' {(("\" .) / [^"])*} '"' }) -> String
- number <- ({ {'-'? [0-9]+ ("." [0-9]+)?} }) -> Number
- variable <- ({ ("%" {%wordchar+}) }) -> Var
-
- subexpression <-
- ("(" %ws? (functioncall / expression) %ws? ")")
- / ("(..)" %ws? %indent %nodent (expression / (({ {| indented_fn_bits |} }) -> FunctionCall)) %dedent (%nodent "..")?)
-
- list <- ({ {|
- ("[..]" %ws? %indent %nodent indented_list ","? %dedent (%nodent "..")?)
- / ("[" %ws? (list_items ","?)? %ws?"]")
- |} }) -> List
- list_items <- (expression (list_sep list_items)?)
- list_sep <- %ws? "," %ws?
- indented_list <-
- expression (((list_sep %nodent?) / %nodent) indented_list)?
-]=]
-
-wordchar = lpeg.P(1)-lpeg.S(' \t\n\r%:;,.{}[]()"')
-defs =
- eol: #(linebreak) + (lpeg.P("")-lpeg.P(1))
- ws: lpeg.S(" \t")^1
- wordchar: wordchar
- indent: linebreak * lpeg.P("{") * lpeg.S(" \t")^0
- nodent: linebreak * lpeg.P(" ") * lpeg.S(" \t")^0
- dedent: linebreak * lpeg.P("}") * lpeg.S(" \t")^0
- word_boundary: lpeg.S(" \t")^1 + lpeg.B(lpeg.P("..")) + lpeg.B(lpeg.S("\";)]")) + #lpeg.S("\":([") + #lpeg.P("..")
-
-setmetatable(defs, {
- __index: (t,key)->
- --print("WORKING for #{key}")
- fn = (src, value, ...)->
- token = {type: key, src:src, value: value}
- return token
- t[key] = fn
- return fn
-})
-lingo = re.compile lingo, defs
-
class Game
new:(parent)=>
@defs = setmetatable({}, {__index:parent and parent.defs})
- @macros = setmetatable({}, {__index: parent and parent.macros})
@debug = false
call: (fn_name,...)=>
- if @defs[fn_name] == nil
+ fn_info = @defs[fn_name]
+ if fn_info == nil
error "Attempt to call undefined function: #{fn_name}"
- {fn, arg_names} = @defs[fn_name]
- if @debug
- print("Calling #{fn_name}...")
- args = {}
- for i,name in ipairs(arg_names)
- args[name] = select(i,...)
- if @debug
- print("arg #{utils.repr(name,true)} = #{utils.repr(select(i,...), true)}")
- ret = fn(self, args)
+ {:fn, :arg_names} = fn_info
+ args = {name, select(i,...) for i,name in ipairs(arg_names)}
if @debug
- print "returned #{utils.repr(ret,true)}"
- return ret
+ print "Calling #{fn_name} with args: #{utils.repr(args)}"
+ return fn(self, args)
def: (spec, fn)=>
invocations,arg_names = self\get_invocations spec
+ fn_info = {:fn, :arg_names, :invocations, is_macro:false}
for invocation in *invocations
- @defs[invocation] = {fn, arg_names}
+ @defs[invocation] = fn_info
get_invocations:(text)=>
if type(text) == 'string' then text = {text}
@@ -190,21 +61,55 @@ class Game
defmacro: (spec, fn)=>
assert fn, "No function supplied"
invocations,arg_names = self\get_invocations spec
+ fn_info = {:fn, :arg_names, :invocations, is_macro:true}
for invocation in *invocations
- @macros[invocation] = {fn, arg_names}
+ @defs[invocation] = fn_info
simplemacro: (spec, replacement)=>
+ spec = spec\gsub("\r", "")
+ replacement = replacement\gsub("\r", "")
+ indent_stack = {0}
+ push = (n)-> table.insert indent_stack, n
+ pop = ()-> table.remove indent_stack
+ check_indent = (subject,end_pos,spaces)->
+ num_spaces = get_line_indentation(spaces)
+ if num_spaces <= indent_stack[#indent_stack] then return nil
+ push num_spaces
+ return end_pos
+ check_dedent = (subject,end_pos,spaces)->
+ num_spaces = get_line_indentation(spaces)
+ if num_spaces >= indent_stack[#indent_stack] then return nil
+ pop!
+ return end_pos
+ check_nodent = (subject,end_pos,spaces)->
+ num_spaces = get_line_indentation(spaces)
+ if num_spaces != indent_stack[#indent_stack] then return nil
+ return end_pos
+
+ nl = P("\n")
+ blank_line = spaces^-1 * nl
+ defs =
+ eol: #(nl) + (P("")-P(1))
+ ws: S(" \t")^1
+ :wordchar
+ :replacer
+ :nl, :spaces
+ word_boundary: S(" \t")^1 + B(P("..")) + B(S("\";)]")) + #S("\":([") + #P("..")
+ indent: #(nl * blank_line^0 * Cmt(spaces^-1, check_indent))
+ dedent: #(nl * blank_line^0 * Cmt(spaces^-1, check_dedent))
+ new_line: nl * blank_line^0 * Cmt(spaces^-1, check_nodent)
+
replace_grammar = [[
- stuff <- {~ (var / string / .)+ ~}
+ stuff <- {~ (var / longstring / string / .)+ ~}
var <- ("%" {%wordchar+}) -> replacer
string <- '"' (("\" .) / [^"])* '"'
+ longstring <- ('"..' %indent %nl {(!%dedent .)*} %new_line '.."')
]]
- replacement = add_indent_tokens replacement
fn = (vars, helpers, ftype)=>
replacer = (varname)->
ret = vars[varname].src
return ret
- replacement_grammar = re.compile(replace_grammar, {:wordchar, :replacer})
+ replacement_grammar = re.compile(replace_grammar, defs)
replaced = replacement_grammar\match(replacement)
tree = lingo\match replaced
if tree.value.errors and #tree.value.errors.value > 0
@@ -242,10 +147,98 @@ class Game
parse: (str)=>
if @debug
print("PARSING:\n#{str}")
- indentified = add_indent_tokens str
- if @debug
- print("\nINDENTIFIED:\n#{indentified}")
- tree = lingo\match indentified
+ lingo = [=[
+ file <- ({ {| %new_line? {:body: block :} %new_line? ({:errors: errors :})? |} }) -> File
+ errors <- ({ {.+} }) -> Errors
+ 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 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 "..")?)
+ / (one_liner (%ws? (%new_line? ".."))?)) }) -> Thunk
+
+ word <- ({ !number {%wordchar+} }) -> Word
+ expression <- ({ (longstring / string / number / variable / list / thunk / subexpression) }) -> Expression
+
+ string <- ({ (!('"..' %ws? %nl)) '"' {(("\" .) / [^"])*} '"' }) -> String
+ longstring <- ({ '"..' %ws? %indent %nl {(!%dedent .)* (%nl %ws? %eol)*} %new_line '.."' }) -> Longstring
+ number <- ({ {'-'? [0-9]+ ("." [0-9]+)?} }) -> Number
+ variable <- ({ ("%" {%wordchar+}) }) -> Var
+
+ subexpression <-
+ ("(" %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 "..")?)
+ / ("[" %ws? (list_items ","?)? %ws?"]")
+ |} }) -> List
+ list_items <- (expression (list_sep list_items)?)
+ list_sep <- %ws? "," %ws?
+ indented_list <-
+ expression (((list_sep %new_line?) / %new_line) indented_list)?
+ ]=]
+
+ str = str\gsub("\r", "")
+ indent_stack = {0}
+ push = (n)-> table.insert indent_stack, n
+ pop = ()-> table.remove indent_stack
+ check_indent = (subject,end_pos,spaces)->
+ num_spaces = get_line_indentation(spaces)
+ if num_spaces <= indent_stack[#indent_stack] then return nil
+ push num_spaces
+ return end_pos
+ check_dedent = (subject,end_pos,spaces)->
+ num_spaces = get_line_indentation(spaces)
+ if num_spaces >= indent_stack[#indent_stack] then return nil
+ pop!
+ return end_pos
+ check_nodent = (subject,end_pos,spaces)->
+ num_spaces = get_line_indentation(spaces)
+ if num_spaces != indent_stack[#indent_stack] then return nil
+ return end_pos
+
+ nl = P("\n")
+ blank_line = spaces^-1 * nl
+ defs =
+ eol: #(nl) + (P("")-P(1))
+ ws: S(" \t")^1
+ :wordchar
+ :nl, :spaces
+ word_boundary: S(" \t")^1 + B(P("..")) + B(S("\";)]")) + #S("\":([") + #P("..")
+ indent: #(nl * blank_line^0 * Cmt(spaces^-1, check_indent))
+ dedent: #(nl * blank_line^0 * Cmt(spaces^-1, check_dedent))
+ new_line: nl * blank_line^0 * Cmt(spaces^-1, check_nodent)
+
+ setmetatable(defs, {
+ __index: (t,key)->
+ --print("WORKING for #{key}")
+ fn = (src, value, ...)->
+ token = {type: key, src:src, value: value}
+ return token
+ t[key] = fn
+ return fn
+ })
+ lingo = re.compile lingo, defs
+ tree = lingo\match(str\gsub("\r","").."\n")
if @debug
print("\nPARSE TREE:")
self\print_tree(tree)
@@ -326,7 +319,8 @@ class Game
for token in *tree.value.value
table.insert name_bits, if token.type == "Word" then token.value else "%"
name = table.concat(name_bits, " ")
- if @macros[name]
+ if @defs[name] and @defs[name].is_macro
+ -- This case here is to prevent "ret =" from getting prepended when the macro might not want it
lua transform(tree.value)
ret = table.concat ret_lines, "\n"
return ret
@@ -340,8 +334,8 @@ class Game
for token in *tree.value
table.insert name_bits, if token.type == "Word" then token.value else "%"
name = table.concat(name_bits, " ")
- if @macros[name]
- {fn, arg_names} = @macros[name]
+ if @defs[name] and @defs[name].is_macro
+ {:fn, :arg_names} = @defs[name]
helpers = {:indented, :transform, :ind, :ded, :lua, :comma_separated_items}
args = [a for a in *tree.value when a.type != "Word"]
args = {name,args[i] for i,name in ipairs(arg_names)}
@@ -359,6 +353,15 @@ class Game
unescaped = tree.value\gsub("\\(.)", ((c)-> escapes[c] or c))
lua utils.repr(unescaped, true)
+ when "Longstring"
+ first_nonblank_line = tree.value\match("[^\n]+")
+ indent = first_nonblank_line\match("[ \t]*")
+ result = {}
+ for line in (tree.value.."\n")\gmatch("(.-)\n")
+ line = line\gsub("^"..indent, "", 1)
+ table.insert result, line
+ lua utils.repr(table.concat(result, "\n"), true)
+
when "Number"
lua tree.value
@@ -417,6 +420,11 @@ class Game
self\_yield_tree(a, indent_level+1)
when "String"
+ -- TODO: Better implement
+ coroutine.yield(ind(utils.repr(tree.value, true)))
+
+ when "Longstring"
+ -- TODO: Better implement
coroutine.yield(ind(utils.repr(tree.value, true)))
when "Number"
@@ -453,20 +461,24 @@ class Game
return code
test: (src, expected)=>
- if expected == nil
- start,stop = src\find"==="
+ i = 1
+ while i != nil
+ start,stop = src\find("\n\n", i)
+
+ test = src\sub(i,start)
+ i = stop
+ start,stop = test\find"==="
if not start or not stop then
- error("WHERE'S THE ===? in:\n#{src}")
- src, expected = src\sub(1,start-1), src\sub(stop+1,-1)
- expected = expected\match'[\n]*(.*[^\n])'
- if not expected then error("WTF???")
- tree = self\parse(src)
- got = if tree.value.errors and #tree.value.errors.value > 0
- self\stringify_tree(tree.value.errors)
- else
- self\stringify_tree(tree.value.body)
- if got != expected
- error"TEST FAILED!\nSource:\n#{src}\nExpected:\n#{expected}\n\nGot:\n#{got}"
+ error("WHERE'S THE ===? in:\n#{test}")
+ test_src, expected = test\sub(1,start-1), test\sub(stop+1,-1)
+ expected = expected\match'[\n]*(.*[^\n])'
+ tree = self\parse(test_src)
+ got = if tree.value.errors and #tree.value.errors.value > 0
+ self\stringify_tree(tree.value.errors)
+ else
+ self\stringify_tree(tree.value.body)
+ if got != expected
+ error"TEST FAILED!\nSource:\n#{test_src}\nExpected:\n#{expected}\n\nGot:\n#{got}"
return Game
diff --git a/utils.moon b/utils.moon
index 3004c58..ee97903 100644
--- a/utils.moon
+++ b/utils.moon
@@ -17,9 +17,9 @@ utils = {
when 'string'
if not add_quotes
x
- elseif not x\find[["]]
+ elseif not x\find[["]] and not x\find"\n"
"\"#{x}\""
- elseif not x\find[[']]
+ elseif not x\find[[']] and not x\find"\n"
"\'#{x}\'"
else
for i=0,math.huge