diff --git a/core.moon b/core.moon index 8d76915..9328676 100755 --- a/core.moon +++ b/core.moon @@ -28,9 +28,13 @@ g\defmacro "let %varname = %value", (vars, helpers, ftype)=> .lua "vars[#{.ded(.transform(vars.varname))} = #{.ded(.transform(vars.value))}" return nil -g\defmacro {"true", "yes"}, (vars,helpers,ftype)=> helpers.lua("true") -g\defmacro {"false", "no"}, (vars,helpers,ftype)=> helpers.lua("false") -g\defmacro "nil", (vars,helpers,ftype)=> helpers.lua("nil") +singleton = (aliases, value)-> + g\defmacro aliases, (vars,helpers,ftype)=> + if ftype == "Expression" + helpers.lua(value) + else + helpers.lua("ret = #{value}") + infix = (ops)-> for op in *ops alias = op @@ -50,6 +54,10 @@ unary = (ops)-> elseif ftype == "Expression" helpers.lua("#{op}(#{helpers.var('x')})") else error("Unknown: #{ftype}") + +singleton {"true","yes"}, "true" +singleton {"false","no"}, "false" +singleton {"nil","null"}, "nil" infix{"+","-","*","/","==",{"!=","~="},"<","<=",">",">=","^","and","or"} unary{"-","#","not"} g\def [[%x == %y]], (args)=> utils.equivalent(args.x, args.y) @@ -108,5 +116,83 @@ g\def {[[# %list]], [[length of %list]], [[size of %list]]}, (args)=> return return #(.list) +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 -return game +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\simplemacro "if %condition %body", [[ +if %condition %body +..else: nil +]] + +g\simplemacro "unless %condition %body", [[ +if (not %condition) %body +..else: nil]] + +g\def [[do %action]], (vars)=> return vars.action(self,vars) + + +return g diff --git a/game2.moon b/game2.moon index e487991..eb1d4c5 100755 --- a/game2.moon +++ b/game2.moon @@ -1,49 +1,10 @@ #!/usr/bin/env moon utils = require 'utils' Game = require 'nomic' -g = Game() +g = Game(require'core') print("===========================================================================================") -g\def "rule %spec %body", (vars)=> - self\def vars.spec, vars.body - print "Defined rule: #{vars.spec}" - -g\def "say %x", (vars)=> - print(utils.repr(vars.x)) - -g\defmacro "return %retval", (vars,helpers,ftype)=> - 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}" - return nil - -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" @@ -224,76 +185,6 @@ say both .. 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 @@ -334,13 +225,20 @@ g\run[[ say (1 + (-(2 * 3))) ]] -g\run_debug[[ +g\run[[ for "x" in ["A","B","C"]: say %x ]] -g\run_debug[[ +g\run[[ say (for "x" in [1,2,3]:%x + 100) say (..) for "x" in [1,2,3]: %x + 200 ]] + +g\run[[ +if (1 == 1): + say "Simple macros work!" +unless (1 > 2): + say "This one too!" +]] diff --git a/nomic.moon b/nomic.moon index a8eb4d5..a2a35a6 100644 --- a/nomic.moon +++ b/nomic.moon @@ -63,26 +63,26 @@ add_indent_tokens = (str)-> string <- '"' (("\\" .) / [^"])* '"' ]=] indentflagger = re.compile indentflagger, defs - indentflagger\match(str) + 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 <- ({} {| - (({} - (({} {| + 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) - {}) -> Statement) - |} {}) -> Block + |} }) -> FunctionCall) + }) -> Statement) + |} }) -> Block - functioncall <- ({} {| (expression %word_boundary fn_bits) / (word (%word_boundary fn_bits)?) |} {}) -> FunctionCall + 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)?) @@ -92,35 +92,36 @@ lingo = [=[ fn_bit ((%nodent / %word_boundary) indented_fn_bits)? thunk <- - ({} ":" %ws? + ({ ":" %ws? ((%indent %nodent block %dedent (%nodent "..")?) - / (one_liner (%ws? ((%nodent? "..")))?)) {}) -> Thunk + / (one_liner (%ws? ((%nodent? "..")))?)) }) -> Thunk - word <- ({} !number {%wordchar+} {}) -> Word - expression <- ({} (string / number / variable / list / thunk / subexpression) {}) -> Expression + word <- ({ !number {%wordchar+} }) -> Word + expression <- ({ (string / number / variable / list / thunk / subexpression) }) -> Expression - string <- ({} '"' {(("\\" .) / [^"])*} '"' {}) -> String - number <- ({} {'-'? [0-9]+ ("." [0-9]+)?} {}) -> Number - variable <- ({} ("%" {%wordchar+}) {}) -> Var + 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 "..")?) + / ("(..)" %ws? %indent %nodent (expression / (({ {| indented_fn_bits |} }) -> FunctionCall)) %dedent (%nodent "..")?) - list <- ({} {| + list <- ({ {| ("[..]" %ws? %indent %nodent indented_list ","? %dedent (%nodent "..")?) / ("[" %ws? (list_items ","?)? %ws?"]") - |} {}) -> List + |} }) -> 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: lpeg.P(1)-lpeg.S(' \t\n\r%:;,.{}[]()"') + 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 @@ -129,8 +130,8 @@ defs = setmetatable(defs, { __index: (t,key)-> --print("WORKING for #{key}") - fn = (start, value, stop, ...)-> - token = {type: key, range:{start,stop}, value: value} + fn = (src, value, ...)-> + token = {type: key, src:src, value: value} return token t[key] = fn return fn @@ -139,9 +140,9 @@ lingo = re.compile lingo, defs class Game - new:=> - @defs = {} - @macros = {} + new:(parent)=> + @defs = setmetatable({}, {__index:parent and parent.defs}) + @macros = setmetatable({}, {__index: parent and parent.macros}) @debug = false call: (fn_name,...)=> @@ -154,7 +155,7 @@ class Game for i,name in ipairs(arg_names) args[name] = select(i,...) if @debug - print("arg #{utils.repr(name,true)} = #{select(i,...)}") + print("arg #{utils.repr(name,true)} = #{utils.repr(select(i,...), true)}") ret = fn(self, args) if @debug print "returned #{utils.repr(ret,true)}" @@ -189,6 +190,26 @@ class Game invocations,arg_names = self\get_invocations spec for invocation in *invocations @macros[invocation] = {fn, arg_names} + + simplemacro: (spec, replacement)=> + replace_grammar = [[ + stuff <- {~ (var / string / .)+ ~} + var <- ("%" {%wordchar+}) -> replacer + string <- '"' (("\\" .) / [^"])* '"' + ]] + 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}) + replaced = replacement_grammar\match(replacement) + tree = lingo\match (replaced) + if not tree + error "Couldn't match:\n#{replaced}" + helpers.lua(helpers.transform(tree.value.body)) + return code + self\defmacro spec, fn run: (text)=> if @debug @@ -276,7 +297,7 @@ class Game lua "end)" when "Errors" - -- TODO: Better error reporting via tree.range[1] + -- TODO: Better error reporting via tree.src error("\nParse error on: #{tree.value}") when "Block"