Totally working everything (probably) with if and for macros.
This commit is contained in:
parent
ecc4e82919
commit
4713d7db0d
340
game2.moon
340
game2.moon
@ -3,70 +3,201 @@ utils = require 'utils'
|
|||||||
Game = require 'nomic_whitespace'
|
Game = require 'nomic_whitespace'
|
||||||
g = Game()
|
g = Game()
|
||||||
|
|
||||||
|
print("===========================================================================================")
|
||||||
|
|
||||||
g\def "rule %spec %body", (vars)=>
|
g\def "rule %spec %body", (vars)=>
|
||||||
self\def vars.spec, vars.body
|
self\def vars.spec, vars.body
|
||||||
print "Defined rule: #{vars.spec}"
|
print "Defined rule: #{vars.spec}"
|
||||||
|
|
||||||
g\defmacro("lua %code", ((args)->
|
|
||||||
print("entering macro...: #{utils.repr(args)}")
|
|
||||||
return args[1].value
|
|
||||||
), true)
|
|
||||||
|
|
||||||
g\defmacro("macro %spec %body", ((spec,body)->
|
|
||||||
print("entering macro...: #{utils.repr(spec,true)} / #{utils.repr(body,true)}")
|
|
||||||
-- TODO: parse better
|
|
||||||
lua_thunk, err = loadstring("return "..spec)
|
|
||||||
if not lua_thunk
|
|
||||||
error("Failed to compile")
|
|
||||||
spec = lua_thunk!
|
|
||||||
print"SPEC IS NOW #{utils.repr(spec,true)}"
|
|
||||||
g\defmacro spec, (args,blargs)->
|
|
||||||
print("entering macro...: #{utils.repr(spec,true)} / #{utils.repr(body,true)}")
|
|
||||||
return body
|
|
||||||
return "nil"
|
|
||||||
), false)
|
|
||||||
|
|
||||||
g\def "say %x", (vars)=>
|
g\def "say %x", (vars)=>
|
||||||
print(utils.repr(vars.x))
|
print(utils.repr(vars.x))
|
||||||
|
|
||||||
g\def "return %x", (vars)=>
|
g\defmacro "return %retval", (vars,helpers,ftype)=>
|
||||||
return vars.x
|
with helpers
|
||||||
|
switch ftype
|
||||||
|
when "Expression"
|
||||||
|
error("Cannot use a return statement as an expression")
|
||||||
|
when "Statement"
|
||||||
|
.lua "do return "..(.ded(.transform(vars.retval))).." end"
|
||||||
|
else
|
||||||
|
error"Unknown: #{ftype}"
|
||||||
|
|
||||||
g\run_debug[[
|
return nil
|
||||||
say "hello world!"
|
|
||||||
|
|
||||||
|
g\defmacro "true", (vars,helpers,ftype)=> helpers.lua("true")
|
||||||
|
g\defmacro "false", (vars,helpers,ftype)=> helpers.lua("false")
|
||||||
|
g\defmacro "nil", (vars,helpers,ftype)=> helpers.lua("nil")
|
||||||
|
infix = (ops)->
|
||||||
|
for op in *ops
|
||||||
|
g\defmacro "%x #{op} %y", (vars,helpers,ftype)=>
|
||||||
|
if ftype == "Statement"
|
||||||
|
helpers.lua("ret = (#{helpers.var('x')} #{op} #{helpers.var('y')})")
|
||||||
|
elseif ftype == "Expression"
|
||||||
|
helpers.lua("(#{helpers.var('x')} #{op} #{helpers.var('y')})")
|
||||||
|
else error("Unknown: #{ftype}")
|
||||||
|
unary = (ops)->
|
||||||
|
for op in *ops
|
||||||
|
g\defmacro "#{op} %x", (vars,helpers,ftype)=>
|
||||||
|
if ftype == "Statement"
|
||||||
|
helpers.lua("ret = #{op}(#{helpers.var('x')})")
|
||||||
|
elseif ftype == "Expression"
|
||||||
|
helpers.lua("#{op}(#{helpers.var('x')})")
|
||||||
|
else error("Unknown: #{ftype}")
|
||||||
|
infix{"+","-","*","/","==","!=","<","<=",">",">=","^"}
|
||||||
|
unary{"-","#","not"}
|
||||||
|
|
||||||
|
g\test[[
|
||||||
|
say "foo"
|
||||||
|
===
|
||||||
|
Call [say %]:
|
||||||
|
"foo"
|
||||||
|
]]
|
||||||
|
|
||||||
|
g\test[[
|
||||||
|
say (4)
|
||||||
|
===
|
||||||
|
Call [say %]:
|
||||||
|
4
|
||||||
|
]]
|
||||||
|
|
||||||
|
g\test[[
|
||||||
rule "fart": say "poot"
|
rule "fart": say "poot"
|
||||||
|
===
|
||||||
|
Call [rule % %]:
|
||||||
|
"fart"
|
||||||
|
Thunk:
|
||||||
|
Call [say %]:
|
||||||
|
"poot"
|
||||||
|
]]
|
||||||
|
|
||||||
|
g\test[[
|
||||||
rule "doublefart":
|
rule "doublefart":
|
||||||
say "poot"
|
say "poot"
|
||||||
say "poot"
|
say "poot"
|
||||||
|
===
|
||||||
|
Call [rule % %]:
|
||||||
|
"doublefart"
|
||||||
|
Thunk:
|
||||||
|
Call [say %]:
|
||||||
|
"poot"
|
||||||
|
Call [say %]:
|
||||||
|
"poot"
|
||||||
|
]]
|
||||||
|
|
||||||
fart
|
g\test[[
|
||||||
doublefart
|
say (subexpressions work)
|
||||||
|
===
|
||||||
|
Call [say %]:
|
||||||
|
Call [subexpressions work]!
|
||||||
|
]]
|
||||||
|
|
||||||
rule "say both %x and %y":
|
g\test[[
|
||||||
say %x
|
say ["lists", "work"]
|
||||||
say %y
|
===
|
||||||
|
Call [say %]:
|
||||||
|
List:
|
||||||
|
"lists"
|
||||||
|
"work"
|
||||||
|
]]
|
||||||
|
|
||||||
say both "vars" and "work!"
|
g\test[[
|
||||||
|
say []
|
||||||
say ( return "subexpressions work" )
|
===
|
||||||
|
Call [say %]:
|
||||||
say "goodbye"
|
<Empty List>
|
||||||
|
]]
|
||||||
say [1,2,3]
|
|
||||||
|
|
||||||
|
g\test[[
|
||||||
say [..]
|
say [..]
|
||||||
1, 2, 3
|
1, 2
|
||||||
4, 5
|
3
|
||||||
|
===
|
||||||
|
Call [say %]:
|
||||||
|
List:
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
]]
|
||||||
|
|
||||||
|
g\test[[
|
||||||
say both [..]
|
say both [..]
|
||||||
1,2,3
|
1,2
|
||||||
..and [..]
|
..and [..]
|
||||||
4,5,6
|
3,4
|
||||||
|
===
|
||||||
|
Call [say both % and %]:
|
||||||
|
List:
|
||||||
|
1
|
||||||
|
2
|
||||||
|
List:
|
||||||
|
3
|
||||||
|
4
|
||||||
|
]]
|
||||||
|
|
||||||
|
g\test[[
|
||||||
say both..
|
say both..
|
||||||
"hello"
|
"hello"
|
||||||
and "world"
|
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 [..]
|
say [..]
|
||||||
"this is a stupidly long list", "the items go way past the 80 character", "limit that older consoles"
|
"this is a stupidly long list", "the items go way past the 80 character", "limit that older consoles"
|
||||||
@ -76,14 +207,141 @@ rule "dumbfunc %a %b %c %d %e":
|
|||||||
say "doop"
|
say "doop"
|
||||||
|
|
||||||
dumbfunc..
|
dumbfunc..
|
||||||
"this is a stupidly long list" "the items go way past the 80 character" "limit that older consoles"
|
"this is a stupidly long set of arguments" "the items go way past the 80 character" "limit that older consoles"
|
||||||
"had." "It just keeps going and going"
|
"had." "It just keeps going and going"
|
||||||
|
|
||||||
|
]]
|
||||||
|
g\run[[
|
||||||
rule "four": return 4
|
rule "four": return 4
|
||||||
say both..
|
rule "say both %one and %two":
|
||||||
|
say %one
|
||||||
|
say %two
|
||||||
|
|
||||||
|
say both ..
|
||||||
"a list:"
|
"a list:"
|
||||||
and [..]
|
and [..]
|
||||||
1,2,3,(four),(5)
|
1,2,3,(four),(5)
|
||||||
|
|
||||||
say "done"
|
say "done"
|
||||||
]]
|
]]
|
||||||
|
|
||||||
|
g\defmacro "if %condition %if_body else %else_body", (vars,helpers,ftype)=>
|
||||||
|
with helpers
|
||||||
|
switch ftype
|
||||||
|
when "Expression"
|
||||||
|
.lua "((#{.ded(.transform(vars.condition))}) and"
|
||||||
|
.indented ->
|
||||||
|
.lua "("..(.ded(.transform(vars.if_body)))..")"
|
||||||
|
.lua "or ("..(.ded(.transform(vars.if_body))).."))(game, vars)"
|
||||||
|
when "Statement"
|
||||||
|
.lua("if (#{.ded(.transform(vars.condition))}) then")
|
||||||
|
.indented ->
|
||||||
|
if_body = vars.if_body
|
||||||
|
while if_body.type != "Block"
|
||||||
|
if_body = if_body.value
|
||||||
|
if if_body == nil then error("Failed to find body.")
|
||||||
|
for statement in *if_body.value
|
||||||
|
.lua(.ded(.transform(statement)))
|
||||||
|
.lua("else")
|
||||||
|
.indented ->
|
||||||
|
else_body = vars.else_body
|
||||||
|
while else_body.type != "Block"
|
||||||
|
else_body = else_body.value
|
||||||
|
if else_body == nil then error("Failed to find body.")
|
||||||
|
for statement in *else_body.value
|
||||||
|
.lua(.ded(.transform(statement)))
|
||||||
|
.lua("end")
|
||||||
|
return nil
|
||||||
|
|
||||||
|
g\defmacro "for %varname in %iterable %body", (vars,helpers,ftype)=>
|
||||||
|
with helpers
|
||||||
|
switch ftype
|
||||||
|
when "Expression"
|
||||||
|
.lua "(function(game, vars)"
|
||||||
|
.indented ->
|
||||||
|
.lua "local comprehension, vars = {}, setmetatable({}, {__index=vars})"
|
||||||
|
.lua "for i, value in ipairs(#{.ded(.transform(vars.iterable))}) 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 i, value in ipairs(#{.ded(.transform(vars.iterable))}) 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\defmacro "if %condition %if_body", "if %condition %if_body else: return nil"
|
||||||
|
|
||||||
|
g\def [[do %action]], (vars)=> return vars.action(self,vars)
|
||||||
|
g\run[[
|
||||||
|
rule "do %thing also %also-thing":
|
||||||
|
do %thing
|
||||||
|
do %also-thing
|
||||||
|
return 99
|
||||||
|
|
||||||
|
do: say "one liner"
|
||||||
|
..also: say "another one liner"
|
||||||
|
|
||||||
|
say (..)
|
||||||
|
do:
|
||||||
|
say "hi"
|
||||||
|
return 5
|
||||||
|
say "bye"
|
||||||
|
|
||||||
|
say (do: return "wow")
|
||||||
|
if 1: say "hi1" ..else: say "bye1"
|
||||||
|
|
||||||
|
if 1: say "hi2"
|
||||||
|
..else: say "bye2"
|
||||||
|
|
||||||
|
]]
|
||||||
|
g\run[[
|
||||||
|
rule "foo %x":
|
||||||
|
if %x:
|
||||||
|
say "YES"
|
||||||
|
55
|
||||||
|
..else:
|
||||||
|
say "NO"
|
||||||
|
-99
|
||||||
|
|
||||||
|
say (foo 1)
|
||||||
|
say (foo (false))
|
||||||
|
|
||||||
|
]]
|
||||||
|
|
||||||
|
g\run[[
|
||||||
|
say (1 + (-(2 * 3)))
|
||||||
|
]]
|
||||||
|
|
||||||
|
g\run_debug[[
|
||||||
|
for "x" in ["A","B","C"]:
|
||||||
|
say %x
|
||||||
|
]]
|
||||||
|
g\run_debug[[
|
||||||
|
say (for "x" in [1,2,3]:%x + 100)
|
||||||
|
say (..)
|
||||||
|
for "x" in [1,2,3]:
|
||||||
|
%x + 200
|
||||||
|
]]
|
||||||
|
@ -39,14 +39,14 @@ add_indent_tokens = (str)->
|
|||||||
return
|
return
|
||||||
indent = get_line_indentation(line)
|
indent = get_line_indentation(line)
|
||||||
if indent > indent_stack[#indent_stack]
|
if indent > indent_stack[#indent_stack]
|
||||||
table.insert result, "{\n " --(" ")\rep(indent_stack[#indent_stack]+1).."{\n "
|
table.insert result, "{\n "
|
||||||
table.insert indent_stack, indent
|
table.insert indent_stack, indent
|
||||||
elseif indent < indent_stack[#indent_stack]
|
elseif indent < indent_stack[#indent_stack]
|
||||||
dedents = 0
|
dedents = 0
|
||||||
tokens = {}
|
tokens = {}
|
||||||
while indent < indent_stack[#indent_stack]
|
while indent < indent_stack[#indent_stack]
|
||||||
table.remove indent_stack
|
table.remove indent_stack
|
||||||
table.insert tokens, "}" --(" ")\rep(indent_stack[#indent_stack]+1).."}"
|
table.insert tokens, "}"
|
||||||
table.insert tokens, " "
|
table.insert tokens, " "
|
||||||
table.insert result, table.concat(tokens, "\n")
|
table.insert result, table.concat(tokens, "\n")
|
||||||
else
|
else
|
||||||
@ -67,39 +67,48 @@ add_indent_tokens = (str)->
|
|||||||
while #indent_stack > 1
|
while #indent_stack > 1
|
||||||
table.remove indent_stack
|
table.remove indent_stack
|
||||||
table.insert result, "}\n"
|
table.insert result, "}\n"
|
||||||
return table.concat result
|
return (table.concat result)\sub(1,-2)
|
||||||
|
|
||||||
lingo = [=[
|
lingo = [=[
|
||||||
file <- ({} {| {:body: (" " block) :} ({:errors: errors :})? |} {}) -> File
|
file <- ({} {| {:body: (" " block) :} ({:errors: errors :})? |} {}) -> File
|
||||||
errors <- ({} {.+} {}) -> Errors
|
errors <- ({} {.+} {}) -> Errors
|
||||||
block <- ({} {| statement (%nodent statement)* |} {}) -> Block
|
block <- ({} {| statement (%nodent statement)* |} {}) -> Block
|
||||||
one_liner <- ({} {| statement |} {}) -> Block
|
statement <- ({} (functioncall / expression) {}) -> Statement
|
||||||
statement <- ({} functioncall {}) -> Statement
|
one_liner <- ({} {|
|
||||||
|
(({}
|
||||||
|
(({} {|
|
||||||
|
(expression (%word_boundary fn_bit)+) / (word (%word_boundary fn_bit)*)
|
||||||
|
|} {}) -> FunctionCall)
|
||||||
|
{}) -> Statement)
|
||||||
|
|} {}) -> Block
|
||||||
|
|
||||||
functioncall <- ({} {| fn_bits |} {}) -> FunctionCall
|
functioncall <- ({} {| (expression %word_boundary fn_bits) / (word (%word_boundary fn_bits)?) |} {}) -> FunctionCall
|
||||||
fn_bit <- (expression / word)
|
fn_bit <- (expression / word)
|
||||||
fn_bits <-
|
fn_bits <-
|
||||||
((".." (%indent %nodent indented_fn_bits %dedent))
|
((".." %ws? (%indent %nodent indented_fn_bits %dedent) (%nodent ".." %ws? fn_bits)?)
|
||||||
/ fn_bit) (fn_sep fn_bits)?
|
/ (%nodent ".." fn_bit fn_bits)
|
||||||
|
/ (fn_bit (%word_boundary fn_bits)?))
|
||||||
indented_fn_bits <-
|
indented_fn_bits <-
|
||||||
fn_bit ((%ws / %nodent) indented_fn_bits)?
|
fn_bit ((%nodent / %word_boundary) indented_fn_bits)?
|
||||||
|
|
||||||
fn_sep <- (%nodent ".." %ws?) / %ws / (&":") / (&"..") / (&'"') / (&"[")
|
|
||||||
|
|
||||||
thunk <-
|
thunk <-
|
||||||
({} ":" %ws? ((one_liner (%ws? ";")?) / (%indent %nodent block %dedent)) {}) -> Thunk
|
({} ":" %ws?
|
||||||
|
((%indent %nodent block %dedent (%nodent "..")?)
|
||||||
|
/ (one_liner (%ws? ((%nodent? "..")))?)) {}) -> Thunk
|
||||||
|
|
||||||
word <- ({} {%wordchar+} {}) -> Word
|
word <- ({} !number {%wordchar+} {}) -> Word
|
||||||
expression <- string / number / variable / list / thunk / subexpression
|
expression <- ({} (string / number / variable / list / thunk / subexpression) {}) -> Expression
|
||||||
|
|
||||||
string <- ({} '"' {(("\\" .) / [^"])*} '"' {}) -> String
|
string <- ({} '"' {(("\\" .) / [^"])*} '"' {}) -> String
|
||||||
number <- ({} {'-'? [0-9]+ ("." [0-9]+)?} {}) -> Number
|
number <- ({} {'-'? [0-9]+ ("." [0-9]+)?} {}) -> Number
|
||||||
variable <- ({} ("%" {%wordchar+}) {}) -> Var
|
variable <- ({} ("%" {%wordchar+}) {}) -> Var
|
||||||
|
|
||||||
subexpression <- "(" %ws? (expression / functioncall) %ws? ")"
|
subexpression <-
|
||||||
|
("(" %ws? (functioncall / expression) %ws? ")")
|
||||||
|
/ ("(..)" %ws? %indent %nodent (expression / (({} {| indented_fn_bits |} {}) -> FunctionCall)) %dedent (%nodent "..")?)
|
||||||
|
|
||||||
list <- ({} {|
|
list <- ({} {|
|
||||||
("[..]" %indent %nodent indented_list ","? %dedent)
|
("[..]" %ws? %indent %nodent indented_list ","? %dedent (%nodent "..")?)
|
||||||
/ ("[" %ws? (list_items ","?)? %ws?"]")
|
/ ("[" %ws? (list_items ","?)? %ws?"]")
|
||||||
|} {}) -> List
|
|} {}) -> List
|
||||||
list_items <- (expression (list_sep list_items)?)
|
list_items <- (expression (list_sep list_items)?)
|
||||||
@ -115,6 +124,7 @@ defs =
|
|||||||
indent: linebreak * lpeg.P("{") * lpeg.S(" \t")^0
|
indent: linebreak * lpeg.P("{") * lpeg.S(" \t")^0
|
||||||
nodent: linebreak * lpeg.P(" ") * lpeg.S(" \t")^0
|
nodent: linebreak * lpeg.P(" ") * lpeg.S(" \t")^0
|
||||||
dedent: 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, {
|
setmetatable(defs, {
|
||||||
__index: (t,key)->
|
__index: (t,key)->
|
||||||
@ -166,31 +176,27 @@ class Game
|
|||||||
invocation = table.concat name_bits, " "
|
invocation = table.concat name_bits, " "
|
||||||
return invocation, arg_names
|
return invocation, arg_names
|
||||||
|
|
||||||
defmacro: (spec, fn, advanced_mode=false)=>
|
defmacro: (spec, fn)=>
|
||||||
invocation,arg_names = self\get_invocation spec
|
invocation,arg_names = self\get_invocation spec
|
||||||
if advanced_mode
|
|
||||||
@macros[invocation] = {fn, arg_names}
|
|
||||||
return
|
|
||||||
|
|
||||||
text_manipulator = fn
|
if type(fn) == 'string'
|
||||||
fn = (args, transform,src,indent_level,macros)->
|
error("not implemented")
|
||||||
text_args = [transform(src,a,indent_level,macros) for a in *args]
|
str = fn
|
||||||
return text_manipulator(unpack(text_args))
|
fn = (vars,helper,ftype)=> nil
|
||||||
|
|
||||||
@macros[invocation] = {fn, arg_names}
|
@macros[invocation] = {fn, arg_names}
|
||||||
|
|
||||||
run: (text)=>
|
run: (text)=>
|
||||||
if @debug
|
if @debug
|
||||||
print("Running text:\n")
|
print("RUNNING TEXT:\n")
|
||||||
print(text)
|
print(text)
|
||||||
indentified = add_indent_tokens(text)
|
|
||||||
print("Indentified:\n[[#{indentified}]]")
|
|
||||||
print("\nCompiling...")
|
|
||||||
code = self\compile(text)
|
code = self\compile(text)
|
||||||
if @debug
|
if @debug
|
||||||
|
print("\nGENERATED LUA CODE:")
|
||||||
print(code)
|
print(code)
|
||||||
lua_thunk, err = loadstring(code)
|
lua_thunk, err = loadstring(code)
|
||||||
if not lua_thunk
|
if not lua_thunk
|
||||||
error("Failed to compile")
|
error("Failed to compile generated code:\n#{code}")
|
||||||
action = lua_thunk!
|
action = lua_thunk!
|
||||||
if @debug
|
if @debug
|
||||||
print("Running...")
|
print("Running...")
|
||||||
@ -211,25 +217,29 @@ class Game
|
|||||||
print("\nINDENTIFIED:\n#{indentified}")
|
print("\nINDENTIFIED:\n#{indentified}")
|
||||||
tree = lingo\match indentified
|
tree = lingo\match indentified
|
||||||
if @debug
|
if @debug
|
||||||
print("\nRESULT:\n#{utils.repr(tree)}")
|
print("\nPARSE TREE:")
|
||||||
|
self\print_tree(tree)
|
||||||
assert tree, "Failed to parse: #{str}"
|
assert tree, "Failed to parse: #{str}"
|
||||||
return tree
|
return tree
|
||||||
|
|
||||||
transform: (tree, indent_level=0)=>
|
transform: (tree, indent_level=0, parent=nil)=>
|
||||||
indented = (fn)->
|
indented = (fn)->
|
||||||
export indent_level
|
export indent_level
|
||||||
indent_level += 1
|
indent_level += 1
|
||||||
fn!
|
fn!
|
||||||
indent_level -= 1
|
indent_level -= 1
|
||||||
transform = (t)-> self\transform(t, indent_level)
|
transform = (t,parent)-> self\transform(t, indent_level, parent or tree)
|
||||||
ind = (line) -> (" ")\rep(indent_level)..line
|
ind = (line) -> (" ")\rep(indent_level)..line
|
||||||
ded = (lines)-> lines\match"^%s*(.*)"
|
ded = (lines)->
|
||||||
|
if not lines.match then error("WTF: #{utils.repr(lines)}")
|
||||||
|
lines\match"^%s*(.*)"
|
||||||
|
|
||||||
ret_lines = {}
|
ret_lines = {}
|
||||||
lua = (line, skip_indent=false)->
|
lua = (line, skip_indent=false)->
|
||||||
unless skip_indent
|
unless skip_indent
|
||||||
line = ind(ded(line))
|
line = ind(ded(line))
|
||||||
table.insert ret_lines, line
|
table.insert ret_lines, line
|
||||||
|
return line
|
||||||
|
|
||||||
comma_separated_items = (open, items, close)->
|
comma_separated_items = (open, items, close)->
|
||||||
buffer = open
|
buffer = open
|
||||||
@ -250,12 +260,15 @@ class Game
|
|||||||
|
|
||||||
switch tree.type
|
switch tree.type
|
||||||
when "File"
|
when "File"
|
||||||
if tree.value.errors and #tree.value.errors.value > 1
|
if tree.value.errors and #tree.value.errors.value > 0
|
||||||
return transform(tree.value.errors)
|
ret = transform(tree.value.errors)
|
||||||
|
return ret
|
||||||
|
|
||||||
lua "return (function(game, vars)"
|
lua "return (function(game, vars)"
|
||||||
indented ->
|
indented ->
|
||||||
|
lua "local ret"
|
||||||
lua transform(tree.value.body)
|
lua transform(tree.value.body)
|
||||||
|
lua "return ret"
|
||||||
lua "end)"
|
lua "end)"
|
||||||
|
|
||||||
when "Errors"
|
when "Errors"
|
||||||
@ -272,12 +285,20 @@ class Game
|
|||||||
lua "(function(game,vars)"
|
lua "(function(game,vars)"
|
||||||
indented ->
|
indented ->
|
||||||
lua "local ret"
|
lua "local ret"
|
||||||
|
assert tree.value.type == "Block", "Non-block value in Thunk"
|
||||||
lua transform(tree.value)
|
lua transform(tree.value)
|
||||||
lua "return ret"
|
lua "return ret"
|
||||||
lua "end)"
|
lua "end)"
|
||||||
|
|
||||||
when "Statement"
|
when "Statement"
|
||||||
lua "ret = #{ded(transform(tree.value))}"
|
ret = transform(tree.value)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
when "Expression"
|
||||||
|
ret = transform(tree.value)
|
||||||
|
if parent.type == "Statement"
|
||||||
|
ret = "ret = "..ded(ret)
|
||||||
|
return ret
|
||||||
|
|
||||||
when "FunctionCall"
|
when "FunctionCall"
|
||||||
name_bits = {}
|
name_bits = {}
|
||||||
@ -285,13 +306,20 @@ class Game
|
|||||||
table.insert name_bits, if token.type == "Word" then token.value else "%"
|
table.insert name_bits, if token.type == "Word" then token.value else "%"
|
||||||
name = table.concat(name_bits, " ")
|
name = table.concat(name_bits, " ")
|
||||||
if @macros[name]
|
if @macros[name]
|
||||||
-- TODO: figure out args
|
{fn, arg_names} = @macros[name]
|
||||||
|
helpers = {:indented, :transform, :ind, :ded, :lua, :comma_separated_items}
|
||||||
args = [a for a in *tree.value when a.type != "Word"]
|
args = [a for a in *tree.value when a.type != "Word"]
|
||||||
return @macros[name][1](self, args, transform)
|
args = {name,args[i] for i,name in ipairs(arg_names)}
|
||||||
|
helpers.var = (varname)->
|
||||||
args = [ded(transform(a)) for a in *tree.value when a.type != "Word"]
|
ded(transform(args[varname]))
|
||||||
table.insert args, 1, utils.repr(name, true)
|
m = fn(self, args, helpers, parent.type)
|
||||||
comma_separated_items("game:call(", args, ")")
|
if m != nil then return m
|
||||||
|
else
|
||||||
|
if parent.type == "Statement"
|
||||||
|
lua "ret ="
|
||||||
|
args = [ded(transform(a)) for a in *tree.value when a.type != "Word"]
|
||||||
|
table.insert args, 1, utils.repr(name, true)
|
||||||
|
comma_separated_items("game:call(", args, ")")
|
||||||
|
|
||||||
when "String"
|
when "String"
|
||||||
lua utils.repr(tree.value, true)
|
lua utils.repr(tree.value, true)
|
||||||
@ -311,15 +339,99 @@ class Game
|
|||||||
lua "vars[#{utils.repr(tree.value,true)}]"
|
lua "vars[#{utils.repr(tree.value,true)}]"
|
||||||
|
|
||||||
else
|
else
|
||||||
error("Unknown/unimplemented thingy: #{utils.repr(tree)}")
|
error("Unknown/unimplemented thingy: #{tree.type}")
|
||||||
|
|
||||||
return table.concat ret_lines, "\n"
|
ret = table.concat ret_lines, "\n"
|
||||||
|
return ret
|
||||||
|
|
||||||
|
_yield_tree: (tree, indent_level=0)=>
|
||||||
|
ind = (s) -> (" ")\rep(indent_level)..s
|
||||||
|
switch tree.type
|
||||||
|
when "File"
|
||||||
|
coroutine.yield(ind"File:")
|
||||||
|
self\_yield_tree(tree.value.body, indent_level+1)
|
||||||
|
|
||||||
|
when "Errors"
|
||||||
|
coroutine.yield(ind"Error:\n#{tree.value}")
|
||||||
|
|
||||||
|
when "Block"
|
||||||
|
for chunk in *tree.value
|
||||||
|
self\_yield_tree(chunk, indent_level)
|
||||||
|
|
||||||
|
when "Thunk"
|
||||||
|
coroutine.yield(ind"Thunk:")
|
||||||
|
self\_yield_tree(tree.value, indent_level+1)
|
||||||
|
|
||||||
|
when "Statement"
|
||||||
|
self\_yield_tree(tree.value, indent_level)
|
||||||
|
|
||||||
|
when "Expression"
|
||||||
|
self\_yield_tree(tree.value, indent_level)
|
||||||
|
|
||||||
|
when "FunctionCall"
|
||||||
|
name_bits = {}
|
||||||
|
for token in *tree.value
|
||||||
|
table.insert name_bits, if token.type == "Word" then token.value else "%"
|
||||||
|
name = table.concat(name_bits, " ")
|
||||||
|
if #[a for a in *tree.value when a.type != "Word"] == 0
|
||||||
|
coroutine.yield(ind"Call [#{name}]!")
|
||||||
|
else
|
||||||
|
coroutine.yield(ind"Call [#{name}]:")
|
||||||
|
for a in *tree.value
|
||||||
|
if a.type != "Word"
|
||||||
|
self\_yield_tree(a, indent_level+1)
|
||||||
|
|
||||||
|
when "String"
|
||||||
|
coroutine.yield(ind(utils.repr(tree.value, true)))
|
||||||
|
|
||||||
|
when "Number"
|
||||||
|
coroutine.yield(ind(tree.value))
|
||||||
|
|
||||||
|
when "List"
|
||||||
|
if #tree.value == 0
|
||||||
|
coroutine.yield(ind("<Empty List>"))
|
||||||
|
else
|
||||||
|
coroutine.yield(ind"List:")
|
||||||
|
for item in *tree.value
|
||||||
|
self\_yield_tree(item, indent_level+1)
|
||||||
|
|
||||||
|
when "Var"
|
||||||
|
coroutine.yield ind"Var[#{utils.repr(tree.value)}]"
|
||||||
|
|
||||||
|
else
|
||||||
|
error("Unknown/unimplemented thingy: #{tree.type}")
|
||||||
|
return nil -- to prevent tail calls
|
||||||
|
|
||||||
|
print_tree:(tree)=>
|
||||||
|
for line in coroutine.wrap(-> self\_yield_tree(tree))
|
||||||
|
print(line)
|
||||||
|
|
||||||
|
stringify_tree:(tree)=>
|
||||||
|
result = {}
|
||||||
|
for line in coroutine.wrap(-> self\_yield_tree(tree))
|
||||||
|
table.insert(result, line)
|
||||||
|
return table.concat result, "\n"
|
||||||
|
|
||||||
compile: (src)=>
|
compile: (src)=>
|
||||||
tree = self\parse(src)
|
tree = self\parse(src)
|
||||||
code = self\transform(tree,0)
|
code = self\transform(tree,0)
|
||||||
return code
|
return code
|
||||||
|
|
||||||
|
test: (src, expected)=>
|
||||||
|
if expected == nil
|
||||||
|
start,stop = src\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}"
|
||||||
|
|
||||||
|
|
||||||
return Game
|
return Game
|
||||||
|
@ -30,7 +30,7 @@ utils = {
|
|||||||
tostring(x)
|
tostring(x)
|
||||||
|
|
||||||
split: (str, sep="%s")->
|
split: (str, sep="%s")->
|
||||||
[chunk for chunk in str\gmatch("[^#{sep}]")]
|
[chunk for chunk in str\gmatch("[^#{sep}]+")]
|
||||||
|
|
||||||
keys: (t)-> [k for k in pairs(t)]
|
keys: (t)-> [k for k in pairs(t)]
|
||||||
values: (t)-> [v for _,v in pairs(t)]
|
values: (t)-> [v for _,v in pairs(t)]
|
||||||
|
Loading…
Reference in New Issue
Block a user