commit 4c584bd2edda45f374a16e6bf909bd9d256701da
Author: Bruce Hill <bitbucket@bruce-hill.com>
Date:   Wed Aug 16 04:35:35 2017 -0700

    Initial commit, mostly working. Slightly broken.

diff --git a/game1.moon b/game1.moon
new file mode 100755
index 0000000..1d76f57
--- /dev/null
+++ b/game1.moon
@@ -0,0 +1,193 @@
+#!/usr/bin/env moon
+nomic = require 'nomic'
+game = nomic()
+
+
+game\def [[say $str]], (locals)=>
+    with locals
+        print(.str)
+        return nil
+
+game\def [[return $retval]], (locals)=> locals.retval
+
+game\def [[do $thunk]], (locals)=>
+    locals.thunk\run(@, locals)
+
+game\def {[[true]], [[yes]]}, (locals)=> true
+game\def {[[false]], [[no]]}, (locals)=> false
+game\def {[[nil]], [[None]], [[nop]]}, (locals)=> nil
+
+game\run [[say "Hello world!"]]
+game\run [[
+    say "Hello!"
+    say "World!"
+]]
+
+game\def {[[greet]], [[say hello]]}, [[say "Hello!"]]
+game\run[[greet]]
+game\run[[say hello]]
+
+
+game\run [[say (return "returned value")]]
+
+game\run [[do {say "did"}]]
+
+game\run [[say 5]]
+game\run [[say -5]]
+
+
+game\def [[fart]], [[say "poot"]]
+game\run [[fart]]
+game\def [[fart twice]], [[
+    say "poot"
+    say "poot again"
+]]
+game\run [[fart twice]]
+
+game\def [[sum $items]], (locals)=>
+    tot = 0
+    for x in *locals.items do tot += x
+    return tot
+
+game\run "say [1,2,3]"
+game\run "sum [1,2,3]"
+game\run "say (sum [1,2,3])"
+
+game\def [[print $x]], [[say $x]]
+game\run [[print "printing variables works"]]
+
+
+game\def [[you]], (_)=> @you
+game\run [[you]]
+game\run [[say (you)]]
+
+game\def [[five]], [[return 5]]
+game\run [[say (five)]]
+game\def [[$x squared]], (locals)=> locals.x^2
+game\run [[say ((five) squared)]]
+
+
+game\def [[remember that $key $relation $value]], (locals)=>
+    with locals
+        assert .relation, "no relation!!"
+        if not @relations[.relation] then @relations[.relation] = {}
+        @relations[.relation][.key] = .value
+
+game\def [[remember that $key $relation]], [[remember that $key $relation (true)]]
+
+game\def [[forget about $key $relation]], (locals)=>
+    with locals
+        if not @relations[.relation] then @relations[.relation] = {}
+        @relations[.relation][.key] = nil
+
+game\def [[the value of $key $relation]], (locals)=>
+    with locals
+        return (@relations[.relation] or {})[.key]
+
+game\def [[it is true that $key $relation $value]], [[return ((the value of $key $relation) == $value)]]
+
+game\def [[it is true that $key $relation]], [[return ((the value of $key $relation) == (true))]]
+
+
+game\run [[remember that "socrates" "is mortal"]]
+game\run [[say (the value of "socrates" "is mortal")]]
+
+game\def [[$x == $y]], (locals)=>
+    with locals
+        print("testing equality of #{.x} and #{.y}")
+        if type(.x) != type(.y)
+            return false
+        if type(.x) == 'table'
+            for k,v in pairs(.x)
+                if .y[k] != v
+                    return false
+            for k,v in pairs(.y)
+                if .x[k] != v
+                    return false
+            return true
+        else
+            return .x == .y
+
+game\def [[not $x]], (locals)=> not locals.x
+
+game\def [[$x != $y]], [[return (not (x == y))]]
+
+game\def [[$x < $y]], (locals)=> locals.x < locals.y
+
+game\def [[$x <= $y]], (locals)=> locals.x <= locals.y
+
+game\def [[$x > $y]], (locals)=> locals.x > locals.y
+
+game\def [[$x >= $y]], (locals)=> locals.x >= locals.y
+
+-- TODO: the rest of the comparisons
+
+game\def [[if $condition then $body else $else_body]], (locals)=>
+    with locals
+        if .condition
+            return .body\run(@, locals)
+        else return .else_body\run(@, locals)
+
+game\def [[if $condition then $body]], [[if $condition then $body else {}]]
+game\def [[when $condition do $body]], [[if $condition then $body else {}]]
+
+game\def [[all keys where $relation is $value]], (locals)=>
+    with locals
+        result = {}
+        for k,v in pairs(@relations[.relation] or {})
+            if v == .value
+                table.insert(result, k)
+        return result
+
+game\run [[if (1 == 1) then {say "Affirmative"} else {say "Negatory"}]]
+game\run [[if (1 == 2) then {say "Affirmative"} else {say "Negatory"}]]
+game\run [[if (1 == 2) then {say "Affirmative"}]]
+
+game\run [[say (if (1 == 1) then {return "Ternary yes"} else {return "Ternary no"})]]
+
+game\def [[$who is a member]], [[return (it is true that $who "is a member")]]
+game\def [[you are a member]], [[return ((you) is a member)]]
+game\run [[say (you are a member)]]
+error("done")
+
+game\run [[
+    if (you are a member) then {say "youre a member!"} else {say "youre not a member"}
+]]
+
+
+
+
+[[
+def: $who is a member
+    return it is true that $who "is a member"
+
+def: you are a member
+    return (you) is a member
+
+def: members
+alias: all members
+    return all keys where "is a member" is "yes"
+
+def: let $someone join
+    if (you are a member) then {
+        remember that $someone "is a member"
+    }
+
+def: propose $def
+    if (you are a member) then {
+        remember that $def "is pending"
+        vote yes on $def
+    }
+
+def: make $voter vote yes on $def
+alias: make $voter approve $def
+    remember $def "is approved by" $voter
+    if ((all members) == (all $def "is approved by" ?)) then {
+        forget $def "is approved by" ?
+        add def $def
+    }
+
+def: vote yes on $def
+    make (you) vote yes on $def
+
+]]
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}