aboutsummaryrefslogtreecommitdiff
path: root/nomic.moon
diff options
context:
space:
mode:
authorBruce Hill <bitbucket@bruce-hill.com>2017-08-16 04:35:35 -0700
committerBruce Hill <bitbucket@bruce-hill.com>2017-08-16 04:35:35 -0700
commit4c584bd2edda45f374a16e6bf909bd9d256701da (patch)
tree557cb8909d66f85732eae69bf5aea4c462570cc2 /nomic.moon
Initial commit, mostly working. Slightly broken.
Diffstat (limited to 'nomic.moon')
-rw-r--r--nomic.moon169
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}