diff options
| author | Bruce Hill <bitbucket@bruce-hill.com> | 2017-08-16 04:35:35 -0700 |
|---|---|---|
| committer | Bruce Hill <bitbucket@bruce-hill.com> | 2017-08-16 04:35:35 -0700 |
| commit | 4c584bd2edda45f374a16e6bf909bd9d256701da (patch) | |
| tree | 557cb8909d66f85732eae69bf5aea4c462570cc2 /nomic.moon | |
Initial commit, mostly working. Slightly broken.
Diffstat (limited to 'nomic.moon')
| -rw-r--r-- | nomic.moon | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/nomic.moon b/nomic.moon new file mode 100644 index 0000000..92fdb8b --- /dev/null +++ b/nomic.moon @@ -0,0 +1,169 @@ +re = require 're' +lpeg = require 'lpeg' +moon = require 'moon' +type = moon.type + +export __DEBUG__ + +invocation_def = [[ + name <- {| {chunk} (" " {chunk})* |} -> semicolonify + chunk <- ({"$"} %S+) / ({%S+}) +]] +invocation_def = re.compile(invocation_def, {semicolonify: (bits) -> table.concat(bits, ";")}) + + +as_value = (x, globals, locals)-> + assert (globals and locals), "Shit's fucked" + if type(x) == 'number' or type(x) == 'string' or type(x) == 'table' + return x + ret = x\as_value globals, locals + return ret + +class Var + new:(@name)=> + as_value:(globals, locals)=> + if __DEBUG__ + print("Looking up variable #{@name} = #{locals[@name]}") + if locals[@name] == nil + print("LOCALS:") + for k,v in pairs locals + print(" #{k} = #{v}") + print("GLOBALS:") + for k,v in pairs globals + print(" #{k} = #{v}") + error("Could not find #{@name}") + locals[@name] + __tostring:=> "Var(#{@text})" + +class Word + new:(@text)=> + __tostring:=> "Word(#{@text})" + +class Action + new:(tokens)=> + words = [(if type(t) == Word then t.text else "$") for t in *tokens] + @name = table.concat(words, ";") + if __DEBUG__ + print("ACTION: #{@name}") + for t in *tokens + print(" TOKEN: #{t}") + @args = [t for t in *tokens when type(t) != Word] + + __tostring:=> "Action(#{@name})" + + as_value:(globals, locals)=> + assert((globals and locals), "f'd up") + ret = @\run globals, locals + return ret + + run:(globals, locals)=> + assert((globals and locals), "f'd up") + rule = globals.rules[@name] + unless rule + error("Tried to run rule, but couldn't find: #{@name}") + arg_names = rule.arg_names + new_locals = {} + for i, arg in ipairs(@args) + new_locals[arg_names[i]] = as_value(arg, globals, locals) + + ret = rule.fn(globals, new_locals) + return ret + +class Thunk + new:(@actions)=> + if __DEBUG__ + print("CONSTRUCTING THUNK WITH ACTIONS:") + for k,v in pairs @actions + print(" #{k} = #{v}") + as_value:=>@ + run:(globals, locals)=> + assert((globals and locals), "f'd up") + ret = nil + for a in *@actions + ret = a\run globals,locals + return ret + __tostring:=> + "Thunk(#{table.concat([tostring(a) for a in *@actions], ", ") .. tostring(@actions.returnValue or "") })" + +lingo = [[ + actions <- {| (%nl " "*)* ((" "*) action ((%nl " "*)+ action)*)? (%nl " "*)* |} -> Thunk + action <- {| token (" "+ token)* " "* |} -> Action + token <- expression / ({(!"[" [^ {}()$])+} -> Word) + expression <- number / string / list / variable / thunk / subexpression + number <- ('-'? [0-9]+ ("." [0-9]+)?) -> tonumber + string <- ('"' {(("\\" .) / [^"])*} '"') -> tostring + list <- {| '[' (expression (',' (' '*) expression)*)? ']' |} + variable <- ("$" {%S+}) -> Var + subexpression <- "(" action ")" + thunk <- ("{" {| actions |} "}") -> Thunk +]] +defs = { + Var: (...)->Var(...) + Word: (...)->Word(...) + Action: (...)->Action(...) + Thunk: (...)->Thunk(...) + tostring: tostring + tonumber: tonumber +} +lingo = re.compile lingo, defs + +class Rule + new:(invocations, action)=> + if type(action) == 'string' + @body_str = action + thunk = lingo\match action + unless thunk + error("failed to parse!") + @fn = (globals,locals)-> thunk\run(globals, locals) + else + @body_str = "<lua function>" + @fn = action + + eq = (x,y)-> + if #x != #y then return false + for i=1,#x + if x[i] != y[i] then return false + return true + + @raw_invocations = invocations + @invocations = {} + for raw_invocation in *invocations + name_bits = {} + arg_names = {} + for chunk in raw_invocation\gmatch("%S+") + if chunk\sub(1,1) == "$" + table.insert name_bits, "$" + table.insert arg_names, chunk\sub(2,-1) + else + table.insert name_bits, chunk + if @arg_names + assert(eq(arg_names, @arg_names), "Attempt to use an alias with different variables") + else + @arg_names = arg_names + invocation = table.concat name_bits, ";" + table.insert @invocations, invocation + + __tostring:=> + "Rule: #{table.concat(@raw_invocations, " | ")} :=\n #{@body_str\gsub("\n","\n ")}" + + +def = (game, invocation, action)-> + invocations = if type(invocation) == 'table' then invocation else {invocation} + rule = Rule(invocations, action) + for invocation in *rule.invocations + game.rules[invocation] = rule + print rule + +run = (game, str)-> + print(">> #{str\gsub("\n", "\n.. ")}") + thunk = lingo\match str + unless thunk + error("failed to parse nomic:\n#{str}") + game.you = "@spill" + ret = thunk\run game, {} + if ret != nil + print("= #{ret}") + return ret + +return ()-> + {rules:{}, relations:{}, :run, :def} |
