diff options
| author | Bruce Hill <bitbucket@bruce-hill.com> | 2017-08-18 17:08:15 -0700 |
|---|---|---|
| committer | Bruce Hill <bitbucket@bruce-hill.com> | 2017-08-18 17:08:15 -0700 |
| commit | b2d49dde55522429f805590c40f96a95bfc67106 (patch) | |
| tree | de07c02be2f4ecb256f84c9bbfe7f4216cb404f7 | |
| parent | 73051b34d9ea46710d1e36be4b867a50ccc01eac (diff) | |
Got rid of old versions.
| -rwxr-xr-x[-rw-r--r--] | core.moon | 208 | ||||
| -rwxr-xr-x | core2.moon | 230 | ||||
| -rwxr-xr-x | game1.moon | 416 | ||||
| -rwxr-xr-x | game2.moon | 317 | ||||
| -rw-r--r-- | nomic.moon | 372 | ||||
| -rw-r--r-- | nomic2.moon | 285 |
6 files changed, 662 insertions, 1166 deletions
diff --git a/core.moon b/core.moon index d62cf9e..7155829 100644..100755 --- a/core.moon +++ b/core.moon @@ -18,23 +18,52 @@ repr = (x)-> else tostring(x) -game\def {[[print $str]], [[say $str]]}, (locals)=> - with locals - print(repr(.str)) - return nil +game\def {[[print $str]], [[say $str]]}, (args)=> print(repr(args.str)) +game\def {[[printf $str]]}, (args)=> + for s in *args.str do io.write(repr(s)) + io.write("\n") + +game\def [[return $value]], (args)=> args.value + +game\macro [[let $var = $value]], (var, value)-> + "locals[#{var}] = #{value}" + +game\def [[$signature := $body]], (args)=> + invocations = if type(args.signature) == 'table' then args.signature else {args.signature} + for i in *invocations + unless @check_authorization(i) + print "You are not permitted to redefine this function" + return + + @\def args.signature, args.body + print "Defined new rule: \"#{game.repr(args.signature)}\"" + --print debug.getinfo(args.body, "S").source + return nil -game\def [[$invocations := $body]], (locals)=> - game\def locals.invocations, locals.body +game\def [[compile $body]], (args)=> + print debug.getinfo(args.body, "S").source return nil -game\def [[help $invocation]], (locals)=> - with locals - if @rules[.invocation\gsub(" ",";")] - print(@rules[.invocation\gsub(" ",";")]) +game\def [[help $invocation]], (args)=> + print_rule = (invocation, rule)-> + print "\"#{invocation}\" :=" + info = debug.getinfo(rule, "S") + i = 0 + for line in info.source\gmatch("[^\n]+") + i+=1 + if i >= info.linedefined+1 + print(line) + if i >= info.lastlinedefined-1 + return + print " #{info.short_src}:#{info.linedefined}-#{info.lastlinedefined}" + + with args + if rule = @rules[.invocation] + print_rule .invocation, rule return nil words = [w for w in .invocation\gmatch("%S+")] match_count = (i)-> - iws = [w for w in i\gmatch("[^;$]+")] + iws = [w for w in i\gmatch("[^ $]+")] count = 0 for w in *words for iw in *iws @@ -42,26 +71,32 @@ game\def [[help $invocation]], (locals)=> count += 1/#iws return count rules = {} + invocations = {} for i,r in pairs(@rules) - rules[r] = math.max((rules[r] or 0), match_count(i)) + c = match_count(i) + if c > (rules[r] or 0) + rules[r] = c + invocations[r] = i best = [r for r in pairs rules] table.sort best, ((a,b)-> rules[a] > rules[b]) if rules[best[1]] > 0 for r in *best if rules[r] < rules[best[1]] break - print("Closest match for \"#{.invocation}\" is:\n#{r}") + print_rule invocations[r], r return nil - -game\def [[return $retval]], ((locals)=> locals.retval), "... returns the specified value ..." - -game\def {[[true]], [[yes]]}, ((locals)=> true), "... returns true ..." -game\def {[[false]], [[no]]}, ((locals)=> false), "... returns false ..." -game\def {[[nil]], [[None]], [[nop]], [[done]]}, ((locals)=> nil), "... does nothing, returns nil ..." - -game\def {[[$x == $y]], [[equal $x $y]]}, (locals)=> - with locals +game\macro "true", -> "true" +game\macro "yes", -> "true" +game\macro "false", -> "false" +game\macro "no", -> "false" +game\macro "nil", -> "nil" +game\macro "None", -> "nil" +game\macro "null", -> "nil" +game\def [[nop]], ((args)=> nil), "... does nothing, returns nil ..." + +game\def [[$x == $y]], (args)=> + with args if .x == .y then return true if type(.x) != type(.y) then return false if type(.x) != 'table' then return false @@ -72,81 +107,124 @@ game\def {[[$x == $y]], [[equal $x $y]]}, (locals)=> if .x[k] != v return false return true - -game\def [[not $x]], (locals)=> not locals.x -game\run [["$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 - -game\def {[[$x + $y]], [[$x plus $y]]}, (locals)=> locals.x + locals.y -game\def {[[$x - $y]], [[$x minus $y]]}, (locals)=> locals.x - locals.y -game\def {[[$x * $y]], [[$x times $y]]}, (locals)=> locals.x * locals.y -game\def {[[$x / $y]], [[$x divided by $y]]}, (locals)=> locals.x / locals.y -game\def {[[$x ^ $y]], [[$x to the power of $y]]}, (locals)=> locals.x ^ locals.y - - -game\def [[if $condition then $body else $else_body]], (locals)=> - with locals +game\run [=[ + ["$x != $y", "$x <> $y", "$x ~= $y"] := {return (not (x == y))} +]=] +game\def [[$x < $y]], (args)=> args.x < args.y +game\def [[$x <= $y]], (args)=> args.x <= args.y +game\def [[$x > $y]], (args)=> args.x > args.y +game\def [[$x >= $y]], (args)=> args.x >= args.y + +game\macro [[$x + $y]], (x,y)-> "(#{x} + #{y})" +game\macro [[$x - $y]], (x,y)-> "(#{x} - #{y})" +game\macro [[$x * $y]], (x,y)-> "(#{x} * #{y})" +game\macro [[$x / $y]], (x,y)-> "(#{x} / #{y})" +game\macro [[$x ^ $y]], (x,y)-> "(#{x} ^ #{y})" + +game\macro [[$x < $y]], (x,y)-> "(#{x} < #{y})" +game\macro [[$x <= $y]], (x,y)-> "(#{x} <= #{y})" +game\macro [[$x > $y]], (x,y)-> "(#{x} > #{y})" +game\macro [[$x >= $y]], (x,y)-> "(#{x} >= #{y})" + +game\macro [[not $x]], (x)-> "(not #{x})" +game\macro [[$x and $y]], (x,y)-> "(#{x} and #{y})" +game\macro [[$x or $y]], (x,y)-> "(#{x} or #{y})" + + +[==[ +game\def [[if $condition $body else $else_body]], (args)=> + with args if .condition - return .body\run(@, locals) - else return .else_body\run(@, locals) + return .body(@, args) + else return .else_body(@, args) -game\run [[ -["if $condition then $body", "when $condition do $body"] := {if $condition then $body else {}} -]] -game\run [[ -["unless $condition do $body"] := {if (not $condition) then $body else {}} -]] -game\run [[ -["unless $condition do $body else $else_body"] := {if (not $condition) then $body else $else_body} -]] +game\run [=[ + ["if $condition $body", "when $condition $body"] := {if $condition $body else {}} + ["unless $condition $body"] := {if (not $condition) $body else {}} + ["unless $condition $body else $else_body"] := {if (not $condition) $body else $else_body} +]=] +]==] +game\def [[random]], -> math.random() -game\def [[sum $items]], (locals)=> +game\def [[sum $items]], (args)=> tot = 0 - for x in *locals.items do tot += x + for x in *args.items do tot += x return tot -game\def {[[average $items]], [[avg $items]]}, (locals)=> +game\def [[all $items]], (args)=> + for x in *args.items + if not x then return false + return true + +game\def [[any $items]], (args)=> + for x in *args.items + if x then return true + return false + +game\def {[[average $items]], [[avg $items]]}, (args)=> tot = 0 - for x in *locals.items do tot += x - return tot / #locals.items + for x in *args.items do tot += x + return tot / #args.items -game\def {[[min $items]], [[smallest $items]], [[lowest $items]], [[fewest $items]]}, (locals)=> - with locals +game\def {[[min $items]], [[smallest $items]], [[lowest $items]], [[fewest $items]]}, (args)=> + with args min = .items[1] for i=2,#.items if .items[i] < min min = .items[i] return min -game\def {[[max $items]], [[largest $items]], [[highest $items]], [[most $items]]}, (locals)=> - with locals +game\def {[[max $items]], [[largest $items]], [[highest $items]], [[most $items]]}, (args)=> + with args max = .items[1] for i=2,#.items if .items[i] > max max = .items[i] return max -game\def {[[argmin $items]]}, (locals)=> - with locals +game\def {[[argmin $items]]}, (args)=> + with args min = .items[1] for i=2,#.items if .items[i][2] < min[2] min = .items[i] return min -game\def {[[argmax $items]]}, (locals)=> - with locals +game\def {[[argmax $items]]}, (args)=> + with args max = .items[1] for i=2,#.items if .items[i][2] > max[2] max = .items[i] return max -game\def {[[$index st in $list]], [[$index nd in $list]], [[$index rd in $list]], [[$index th in $list]]}, (locals)=> - locals.list[locals.index] +game\def {[[$index st in $list]], [[$index nd in $list]], [[$index rd in $list]], [[$index th in $list]]}, (args)=> + with args + if type(.list) != 'table' + print "Not a list: #{.list}" + return + .list[.index] + +game\def {[[index of $item in $list]]}, (args)=> + with args + if type(.list) != 'table' + print "Not a list: #{.list}" + return + for i,x in ipairs .list + if x == .item + return i + return nil +game\run [=[ + ["$item is in $list", "$list contains $item"] := {(index of $item in $list) != (nil)} +]=] + +game\def {[[# $list]], [[length of $list]], [[size of $list]]}, (args)=> + with args + if type(.list) != 'table' + print "Not a list: #{.list}" + return + #.list + return game diff --git a/core2.moon b/core2.moon deleted file mode 100755 index 6298426..0000000 --- a/core2.moon +++ /dev/null @@ -1,230 +0,0 @@ -#!/usr/bin/env moon -nomic = require 'nomic2' -game = nomic() - -is_list = (t)-> - i = 0 - for _ in pairs(t) - i += 1 - if t[i] == nil then return false - return true - -repr = (x)-> - if type(x) == 'table' - if is_list x - "[#{table.concat([repr(i) for i in *x], ", ")}]" - else - "{#{table.concat(["#{k}: #{v}" for k,v in pairs x], ", ")}}" - else - tostring(x) - -game\def {[[print $str]], [[say $str]]}, (args)=> print(repr(args.str)) -game\def {[[printf $str]]}, (args)=> - for s in *args.str do io.write(repr(s)) - io.write("\n") - -game\def [[return $value]], (args)=> args.value - -game\macro [[let $var = $value]], (var, value)-> - "locals[#{var}] = #{value}" - -game\def [[$signature := $body]], (args)=> - invocations = if type(args.signature) == 'table' then args.signature else {args.signature} - for i in *invocations - unless @check_authorization(i) - print "You are not permitted to redefine this function" - return - - @\def args.signature, args.body - print "Defined new rule: \"#{game.repr(args.signature)}\"" - --print debug.getinfo(args.body, "S").source - return nil - -game\def [[compile $body]], (args)=> - print debug.getinfo(args.body, "S").source - return nil - -game\def [[help $invocation]], (args)=> - print_rule = (invocation, rule)-> - print "\"#{invocation}\" :=" - info = debug.getinfo(rule, "S") - i = 0 - for line in info.source\gmatch("[^\n]+") - i+=1 - if i >= info.linedefined+1 - print(line) - if i >= info.lastlinedefined-1 - return - print " #{info.short_src}:#{info.linedefined}-#{info.lastlinedefined}" - - with args - if rule = @rules[.invocation] - print_rule .invocation, rule - return nil - words = [w for w in .invocation\gmatch("%S+")] - match_count = (i)-> - iws = [w for w in i\gmatch("[^ $]+")] - count = 0 - for w in *words - for iw in *iws - if w == iw then count += 1 - count += 1/#iws - return count - rules = {} - invocations = {} - for i,r in pairs(@rules) - c = match_count(i) - if c > (rules[r] or 0) - rules[r] = c - invocations[r] = i - best = [r for r in pairs rules] - table.sort best, ((a,b)-> rules[a] > rules[b]) - if rules[best[1]] > 0 - for r in *best - if rules[r] < rules[best[1]] - break - print_rule invocations[r], r - return nil - -game\macro "true", -> "true" -game\macro "yes", -> "true" -game\macro "false", -> "false" -game\macro "no", -> "false" -game\macro "nil", -> "nil" -game\macro "None", -> "nil" -game\macro "null", -> "nil" -game\def [[nop]], ((args)=> nil), "... does nothing, returns nil ..." - -game\def [[$x == $y]], (args)=> - with args - if .x == .y then return true - if type(.x) != type(.y) then return false - if type(.x) != 'table' then return false - 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 -game\run [=[ - ["$x != $y", "$x <> $y", "$x ~= $y"] := {return (not (x == y))} -]=] -game\def [[$x < $y]], (args)=> args.x < args.y -game\def [[$x <= $y]], (args)=> args.x <= args.y -game\def [[$x > $y]], (args)=> args.x > args.y -game\def [[$x >= $y]], (args)=> args.x >= args.y - -game\macro [[$x + $y]], (x,y)-> "(#{x} + #{y})" -game\macro [[$x - $y]], (x,y)-> "(#{x} - #{y})" -game\macro [[$x * $y]], (x,y)-> "(#{x} * #{y})" -game\macro [[$x / $y]], (x,y)-> "(#{x} / #{y})" -game\macro [[$x ^ $y]], (x,y)-> "(#{x} ^ #{y})" - -game\macro [[$x < $y]], (x,y)-> "(#{x} < #{y})" -game\macro [[$x <= $y]], (x,y)-> "(#{x} <= #{y})" -game\macro [[$x > $y]], (x,y)-> "(#{x} > #{y})" -game\macro [[$x >= $y]], (x,y)-> "(#{x} >= #{y})" - -game\macro [[not $x]], (x)-> "(not #{x})" -game\macro [[$x and $y]], (x,y)-> "(#{x} and #{y})" -game\macro [[$x or $y]], (x,y)-> "(#{x} or #{y})" - - -[==[ -game\def [[if $condition $body else $else_body]], (args)=> - with args - if .condition - return .body(@, args) - else return .else_body(@, args) - -game\run [=[ - ["if $condition $body", "when $condition $body"] := {if $condition $body else {}} - ["unless $condition $body"] := {if (not $condition) $body else {}} - ["unless $condition $body else $else_body"] := {if (not $condition) $body else $else_body} -]=] -]==] - -game\def [[random]], -> math.random() - -game\def [[sum $items]], (args)=> - tot = 0 - for x in *args.items do tot += x - return tot - -game\def [[all $items]], (args)=> - for x in *args.items - if not x then return false - return true - -game\def [[any $items]], (args)=> - for x in *args.items - if x then return true - return false - -game\def {[[average $items]], [[avg $items]]}, (args)=> - tot = 0 - for x in *args.items do tot += x - return tot / #args.items - -game\def {[[min $items]], [[smallest $items]], [[lowest $items]], [[fewest $items]]}, (args)=> - with args - min = .items[1] - for i=2,#.items - if .items[i] < min - min = .items[i] - return min - -game\def {[[max $items]], [[largest $items]], [[highest $items]], [[most $items]]}, (args)=> - with args - max = .items[1] - for i=2,#.items - if .items[i] > max - max = .items[i] - return max - -game\def {[[argmin $items]]}, (args)=> - with args - min = .items[1] - for i=2,#.items - if .items[i][2] < min[2] - min = .items[i] - return min - -game\def {[[argmax $items]]}, (args)=> - with args - max = .items[1] - for i=2,#.items - if .items[i][2] > max[2] - max = .items[i] - return max - -game\def {[[$index st in $list]], [[$index nd in $list]], [[$index rd in $list]], [[$index th in $list]]}, (args)=> - with args - if type(.list) != 'table' - print "Not a list: #{.list}" - return - .list[.index] - -game\def {[[index of $item in $list]]}, (args)=> - with args - if type(.list) != 'table' - print "Not a list: #{.list}" - return - for i,x in ipairs .list - if x == .item - return i - return nil -game\run [=[ - ["$item is in $list", "$list contains $item"] := {(index of $item in $list) != (nil)} -]=] - -game\def {[[# $list]], [[length of $list]], [[size of $list]]}, (args)=> - with args - if type(.list) != 'table' - print "Not a list: #{.list}" - return - #.list - - -return game @@ -1,169 +1,317 @@ #!/usr/bin/env moon Game = require 'nomic' core_game = require 'core' -game = Game(core_game) - ------------------- BASIC TESTS --------------------- -game\run [=[ - -say "=========== INITIALIZING GAME ============" - -"fart" := {say "poot"} -fart - -"fart twice" := { - fart - fart -} - -fart twice - -["greet", "say hello"] := { say "Hello!" } - -greet - -say (return "returned value") -say 6 -say -6 -say [] -say [1,2,3,4] -say [[1,2],[3,[4,5]]] -say (sum [1,2,3,4]) -help "fart" -help "fart twice" +game = Game(core_game) -"fart thrice" := { - fart - fart - fart +game\def "# $key $relation = $value", (args)=> + --print "Setting #{args.key} #{args.relation} = #{args.value}" + @relations[args.relation][args.key] = args.value + return args.value + +game\def "set all * $relation = $value", (args)=> + rels = @relations[args.relation] + for k,_ in pairs rels + rels[k] = args.value + if next(rels) == nil + @relations[args.relation] = nil + return args.value + +game\def "$key $relation ?", (args)=> + return @relations[args.relation][args.key] + +deep_pairs = (t)-> + coroutine.wrap -> + for k,v in pairs(t) + coroutine.yield k, v + mt = getmetatable(t) + return unless mt and mt.__index and type(mt.__index) == 'table' and mt.__index != t + for k,v in deep_pairs mt.__index + if t[k] == nil + coroutine.yield k,v + +game\def "* $relation = $value", (args)=> + --print "Finding all * #{args.relation} = #{args.value}" + [key for key, value in deep_pairs(@relations[args.relation]) when value == args.value] + +game\def {"restrict $actions to $whitelist"}, (args)=> + with args + actions = @canonicalize(if type(.actions) == 'table' then .actions else {.actions}) + whitelist = @all_aliases(if type(.whitelist) == 'table' then .whitelist else {.whitelist}) + @\set_whitelist actions, whitelist + for action in *actions + print("Restricting #{Game.repr(action)} to #{Game.repr(whitelist)}") + +game\def {"permit $whitelist to $actions"}, (args)=> + with args + actions = @canonicalize(if type(.actions) == 'table' then .actions else {.actions}) + whitelist = @all_aliases(if type(.whitelist) == 'table' then .whitelist else {.whitelist}) + for action in *actions + if not @authorized[action] + print "#{action} is already available to everyone." + continue + print("Permitting #{Game.repr(action)} to #{Game.repr(whitelist)}") + for w in *whitelist + @authorized[action][w] = true + +game\def {"revoke $actions rights from $whitelist"}, (args)=> + with args + actions = @canonicalize(if type(.actions) == 'table' then .actions else {.actions}) + whitelist = @all_aliases(if type(.whitelist) == 'table' then .whitelist else {.whitelist}) + for action in *actions + if not @authorized[action] + print "#{action} is available to everyone, it can't be restricted." + continue + print("Revoking the right of #{Game.repr(action)} to use #{Game.repr(whitelist)}") + for w in *whitelist + @authorized[action][w] = nil + +game\def "print callstack", (args)=> + print("Callstack:") + for fn in *@callstack + print fn + +game\def {"do $action"}, (args)=> + (args.action)(@, {}) + +game\def {"make $who $action"}, (args)=> + with args + old_you = @you + print("Setting you=#{.who}") + rawset(@, "you", .who) + (.action)(@, {}) + rawset(@, "you", old_you) + +game\def "you", (args)=> @you + +game\run[=[ +say "====================================================" +say " NEW GAME" +say "====================================================" +"everyone approves $action" := {yes} + +"sudo $action" := { + if (everyone approves $action) { + do $action + } else { + say "You do not have the will of the people! >:(" + } } -help "fart lol" -help "yes" - -"five" := {return 5} -say (6 times 6) -"fitz" := {say (return 99)} -fitz -"bazwiz $x" := {say (sum $x)} -bazwiz [10,20,30] -"foobar $x" := {say (return $x)} -foobar 55 - -"$x squared" := {$x times $x} -"$x plus one" := {$x + 1} -"$x foo" := {($x * $x) + 1} -say (5 foo) - -say (1 st in [1,2,3,4,5]) -say (2 nd in [1,2,3,4,5]) -say (3 rd in [1,2,3,4,5]) -say (4 th in [1,2,3,4,5]) - -]=] - -game\def [[you]], (_)=> @you -game\run [[you]] -game\run [[say (you)]] - -game\run [[ - "five" := {return 5} -]] -game\run [[say (five)]] -game\def [[$x squared]], (locals)=> locals.x^2 -game\run [[say ((five) squared)]] +restrict "$ := $" to "sudo $" +restrict "make $ $" to "sudo $" +restrict "set all * $ = $" to "sudo $" +restrict "# $ $ = $" to "sudo $" +restrict "restrict $ to $" to "sudo $" + +sudo { + "propose $action" := { + if ("pending proposal" "is" ?) { + say "Sorry, an action is already pending." + } else { + say "Proposing..." + # "pending proposal" "is" = $action + } + } + "unpropose" := { + let "pending" = ("pending proposal" "is" ?) + set all * $pending = (nil) + # "pending proposal" "is" = (nil) + } -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 - return nil + "mark $who as approving $action" := { + # $who $action = "approved" + } -game\run [[ - "remember that $key $relation" := {remember that $key $relation (true)} -]] + "mark $who as rejecting $action" := { + # $who $action = "rejected" + } -game\def [[forget about $key $relation]], (locals)=> - with locals - if not @relations[.relation] then @relations[.relation] = {} - @relations[.relation][.key] = nil - return nil + ["approve", "vote yes", "vote yea"] := { + let "pending" = ("pending proposal" "is" ?) + mark (you) as approving $pending + say "Voted yes." + if (everyone approves $pending) { + sudo $pending + unpropose + } + } -game\def [[the value of $key $relation]], (locals)=> - with locals - return (@relations[.relation] or {})[.key] + ["reject", "vote no", "vote nay", "veto", "disapprove"] := { + let "pending" = ("pending proposal" "is" ?) + mark (you) as rejecting $pending + say "Voted no." + unpropose + } -game\run [["it is true that $key $relation $value" := {(the value of $key $relation) == $value}]] -game\run [["it is true that $key $relation" := {(the value of $key $relation) == (true)}]] + ["players", "everyone", "everybody", "all players"] := { + * "is a player" = (yes) + } -game\run[[ - remember that "socrates" "is mortal" - say (it is true that "socrates" "is mortal")) -]] + "join" := { + # (you) "is a player" = (yes) + printf ["Welcome to the game, ",(you),"!"] + } + permit "unpropose" to "set all * $ = $" + permit ["join", "mark $ as approving $", "mark $ as rejecting $", "propose $", "unpropose"] to "# $ $ = $" + restrict "unpropose" to ["approve", "reject"] + restrict "mark $ as approving $" to ["propose $", "approve"] + restrict "mark $ as rejecting $" to ["propose $", "reject"] + + "everyone approves $action" := { + (# (players)) == (# (* $action = "approved")) + } +} +join -game\run [[remember that "socrates" "is mortal"]] -game\run [[say (the value of "socrates" "is mortal")]] +propose { + say "fart" +} +approve -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 +"cheat" := { + say "CHEATER!!!" +} +sudo { + say "CHEATER!!!" +} -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"}]] +propose { + # "democracy" "is possible" = (yes) + if ("democracy" "is possible" ?) { + say "DEMOCRACY WORKS!!!" + } +} +approve -game\run [[say (if (1 == 1) then {return "Ternary yes"} else {return "Ternary no"})]] +propose { + "fart" := { + say "poot" + } +} +approve +fart -game\run [["$who is a member" := {return (it is true that $who "is a member")}]] -game\run [["you are a member" := {return ((you) is a member)}]] -game\run [[say (you are a member)]] +propose { + "open election $candidates" := { + if ("candidates" "are" ?) { + say "An election is already in progress." + } else { + # "candidates" "are" = $candidates + } + } -game\run [[ - if (you are a member) then {say "youre a member!"} else {say "youre not a member"} -]] + "close election" := { + let "pending" = ("pending proposal" "is" ?) + set all * "votes for" = (nil) + # "candidates" "are" = (nil) + } + "vote for $candidate" := { + # (you) "votes for" = $candidate + let "vote-percent" = ((# (* "votes for" = $candidate)) / (# (players))) + printf ["Vote cast. ",$candidate," now has ",(100 * $vote-percent),"% of the votes."] + if ($vote-percent > 0.5) { + printf ["The winner of the election is:", $candidate] + close election + } + } + permit ["open election $", "close election", "vote for $"] to ["# $ $ = $"] + permit ["close election"] to ["set all * $ = $"] +} +approve + +propose { + "as bill: $action" := { + if ((you) == "Anonymous") { + make "bill" $action + } else { + printf ["Who do you think you are?", (you)] + } + } + permit ["as bill: $"] to ["make $ $"] +} +approve +as bill: {join} + +propose { + "as dave: $action" := { + if ((you) == "Anonymous") { + make "dave" $action + } else { + printf ["Who do you think you are?", (you)] + } + } + permit ["as dave: $"] to ["make $ $"] +} +approve +as bill: {approve} +as dave: {join} +open election ["tom", "dick", "harry"] +vote for "dick" +as bill: {vote for "dick"} -[[ -def: $who is a member - return it is true that $who "is a member" +propose { + "take a shit" := {say "shit taken."} +} +approve +as bill: {approve} +as dave: {approve} -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" +sudo { + "everyone approves" := { + (# (players)) == (# (* "votes" (yes))) + } + ["approve", "vote yes", "vote yea"] := { + # (you) "votes" = (yes) + if (everyone approves) { + do pending action + } + } + ["disapprove", "vote no", "vote nay", "veto"] := { + say "The proposal has failed." + # (you) "approves" = (yes) + if (everyone approves) { + do pending action + } } +} -def: propose $def - if (you are a member) then { - remember that $def "is pending" - vote yes on $def +sudo { + "everyone approves" := { + say "Going into this code" + no } +} +sudo { + say "BROKEN" +} -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 + +propose { + "arbitrarily define $signature := $body" := { + if ($signature == "butts") { + $signature := $body + } else { + say "Not my style." + } } + permit "arbitrarily define $ := $" to "$ := $" + say "Arbitrary is a go." +} +as bill: {approve} +as dave: {approve} +approve +arbitrarily define "butts" := {say "BUTTS"} +butts -def: vote yes on $def - make (you) vote yes on $def +arbitrarily define "ass" := {say "ASS"} +ass -]] +]=] diff --git a/game2.moon b/game2.moon deleted file mode 100755 index 4227faa..0000000 --- a/game2.moon +++ /dev/null @@ -1,317 +0,0 @@ -#!/usr/bin/env moon -Game = require 'nomic2' -core_game = require 'core2' - -game = Game(core_game) - -game\def "# $key $relation = $value", (args)=> - --print "Setting #{args.key} #{args.relation} = #{args.value}" - @relations[args.relation][args.key] = args.value - return args.value - -game\def "set all * $relation = $value", (args)=> - rels = @relations[args.relation] - for k,_ in pairs rels - rels[k] = args.value - if next(rels) == nil - @relations[args.relation] = nil - return args.value - -game\def "$key $relation ?", (args)=> - return @relations[args.relation][args.key] - -deep_pairs = (t)-> - coroutine.wrap -> - for k,v in pairs(t) - coroutine.yield k, v - mt = getmetatable(t) - return unless mt and mt.__index and type(mt.__index) == 'table' and mt.__index != t - for k,v in deep_pairs mt.__index - if t[k] == nil - coroutine.yield k,v - -game\def "* $relation = $value", (args)=> - --print "Finding all * #{args.relation} = #{args.value}" - [key for key, value in deep_pairs(@relations[args.relation]) when value == args.value] - -game\def {"restrict $actions to $whitelist"}, (args)=> - with args - actions = @canonicalize(if type(.actions) == 'table' then .actions else {.actions}) - whitelist = @all_aliases(if type(.whitelist) == 'table' then .whitelist else {.whitelist}) - @\set_whitelist actions, whitelist - for action in *actions - print("Restricting #{Game.repr(action)} to #{Game.repr(whitelist)}") - -game\def {"permit $whitelist to $actions"}, (args)=> - with args - actions = @canonicalize(if type(.actions) == 'table' then .actions else {.actions}) - whitelist = @all_aliases(if type(.whitelist) == 'table' then .whitelist else {.whitelist}) - for action in *actions - if not @authorized[action] - print "#{action} is already available to everyone." - continue - print("Permitting #{Game.repr(action)} to #{Game.repr(whitelist)}") - for w in *whitelist - @authorized[action][w] = true - -game\def {"revoke $actions rights from $whitelist"}, (args)=> - with args - actions = @canonicalize(if type(.actions) == 'table' then .actions else {.actions}) - whitelist = @all_aliases(if type(.whitelist) == 'table' then .whitelist else {.whitelist}) - for action in *actions - if not @authorized[action] - print "#{action} is available to everyone, it can't be restricted." - continue - print("Revoking the right of #{Game.repr(action)} to use #{Game.repr(whitelist)}") - for w in *whitelist - @authorized[action][w] = nil - -game\def "print callstack", (args)=> - print("Callstack:") - for fn in *@callstack - print fn - -game\def {"do $action"}, (args)=> - (args.action)(@, {}) - -game\def {"make $who $action"}, (args)=> - with args - old_you = @you - print("Setting you=#{.who}") - rawset(@, "you", .who) - (.action)(@, {}) - rawset(@, "you", old_you) - -game\def "you", (args)=> @you - -game\run[=[ -say "====================================================" -say " NEW GAME" -say "====================================================" -"everyone approves $action" := {yes} - -"sudo $action" := { - if (everyone approves $action) { - do $action - } else { - say "You do not have the will of the people! >:(" - } -} -restrict "$ := $" to "sudo $" -restrict "make $ $" to "sudo $" -restrict "set all * $ = $" to "sudo $" -restrict "# $ $ = $" to "sudo $" -restrict "restrict $ to $" to "sudo $" - -sudo { - "propose $action" := { - if ("pending proposal" "is" ?) { - say "Sorry, an action is already pending." - } else { - say "Proposing..." - # "pending proposal" "is" = $action - } - } - "unpropose" := { - let "pending" = ("pending proposal" "is" ?) - set all * $pending = (nil) - # "pending proposal" "is" = (nil) - } - - - "mark $who as approving $action" := { - # $who $action = "approved" - } - - "mark $who as rejecting $action" := { - # $who $action = "rejected" - } - - ["approve", "vote yes", "vote yea"] := { - let "pending" = ("pending proposal" "is" ?) - mark (you) as approving $pending - say "Voted yes." - if (everyone approves $pending) { - sudo $pending - unpropose - } - } - - ["reject", "vote no", "vote nay", "veto", "disapprove"] := { - let "pending" = ("pending proposal" "is" ?) - mark (you) as rejecting $pending - say "Voted no." - unpropose - } - - ["players", "everyone", "everybody", "all players"] := { - * "is a player" = (yes) - } - - "join" := { - # (you) "is a player" = (yes) - printf ["Welcome to the game, ",(you),"!"] - } - permit "unpropose" to "set all * $ = $" - permit ["join", "mark $ as approving $", "mark $ as rejecting $", "propose $", "unpropose"] to "# $ $ = $" - restrict "unpropose" to ["approve", "reject"] - restrict "mark $ as approving $" to ["propose $", "approve"] - restrict "mark $ as rejecting $" to ["propose $", "reject"] - - "everyone approves $action" := { - (# (players)) == (# (* $action = "approved")) - } -} - -join - -propose { - say "fart" -} -approve - - -"cheat" := { - say "CHEATER!!!" -} -sudo { - say "CHEATER!!!" -} - -propose { - # "democracy" "is possible" = (yes) - if ("democracy" "is possible" ?) { - say "DEMOCRACY WORKS!!!" - } -} -approve - -propose { - "fart" := { - say "poot" - } -} -approve -fart - -propose { - "open election $candidates" := { - if ("candidates" "are" ?) { - say "An election is already in progress." - } else { - # "candidates" "are" = $candidates - } - } - - "close election" := { - let "pending" = ("pending proposal" "is" ?) - set all * "votes for" = (nil) - # "candidates" "are" = (nil) - } - - "vote for $candidate" := { - # (you) "votes for" = $candidate - let "vote-percent" = ((# (* "votes for" = $candidate)) / (# (players))) - printf ["Vote cast. ",$candidate," now has ",(100 * $vote-percent),"% of the votes."] - if ($vote-percent > 0.5) { - printf ["The winner of the election is:", $candidate] - close election - } - } - - permit ["open election $", "close election", "vote for $"] to ["# $ $ = $"] - permit ["close election"] to ["set all * $ = $"] -} -approve - -propose { - "as bill: $action" := { - if ((you) == "Anonymous") { - make "bill" $action - } else { - printf ["Who do you think you are?", (you)] - } - } - permit ["as bill: $"] to ["make $ $"] -} -approve -as bill: {join} - -propose { - "as dave: $action" := { - if ((you) == "Anonymous") { - make "dave" $action - } else { - printf ["Who do you think you are?", (you)] - } - } - permit ["as dave: $"] to ["make $ $"] -} -approve -as bill: {approve} -as dave: {join} - -open election ["tom", "dick", "harry"] -vote for "dick" -as bill: {vote for "dick"} - -propose { - "take a shit" := {say "shit taken."} -} -approve -as bill: {approve} -as dave: {approve} - - - -sudo { - "everyone approves" := { - (# (players)) == (# (* "votes" (yes))) - } - ["approve", "vote yes", "vote yea"] := { - # (you) "votes" = (yes) - if (everyone approves) { - do pending action - } - } - ["disapprove", "vote no", "vote nay", "veto"] := { - say "The proposal has failed." - # (you) "approves" = (yes) - if (everyone approves) { - do pending action - } - } -} - -sudo { - "everyone approves" := { - say "Going into this code" - no - } -} -sudo { - say "BROKEN" -} - - -propose { - "arbitrarily define $signature := $body" := { - if ($signature == "butts") { - $signature := $body - } else { - say "Not my style." - } - } - permit "arbitrarily define $ := $" to "$ := $" - say "Arbitrary is a go." -} -as bill: {approve} -as dave: {approve} -approve -arbitrarily define "butts" := {say "BUTTS"} -butts - -arbitrarily define "ass" := {say "ASS"} -ass - -]=] @@ -3,173 +3,273 @@ lpeg = require 'lpeg' moon = require 'moon' type = moon.type +is_list = (t)-> + i = 0 + for _ in pairs(t) + i += 1 + if t[i] == nil then return false + return true + +repr = (x)-> + if type(x) == 'table' + if is_list x + "[#{table.concat([repr(i) for i in *x], ", ")}]" + else + "{#{table.concat(["#{k}: #{v}" for k,v in pairs x], ", ")}}" + else + tostring(x) + currently_parsing = nil +macros = nil +indentation = 0 +indents = -> + (" ")\rep(indentation) +indent = -> + export indentation + indentation += 1 +dedent = -> + export indentation + indentation -= 1 +indent_block = (block)-> + block = block\gsub("\n", "\n"..indents!) + return indents!..block +add_line = (lines, new_line)-> + table.insert lines, (indents!..new_line) -as_value = (x, game, locals)-> - assert (game and locals), "Shit's fucked" - if type(x) == 'number' or type(x) == 'string' or type(x) == 'table' - return x - ret = x\as_value game, locals - return ret - -class Var - new:(@name)=> - as_value:(game, locals)=> - if locals[@name] == nil - error("Attempt to access undefined variable: #{@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, ";") - @args = [t for t in *tokens when type(t) != Word] - - __tostring:=> "Action(#{@name})" - - as_value:(game, locals)=> - assert((game and locals), "f'd up") - ret = @\run game, locals - return ret +compactify_invocation = (raw_invocation)-> + 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 + invocation = table.concat name_bits, " " + return invocation, arg_names - run:(game, locals)=> - assert((game and locals), "f'd up") - rule = game.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, game, locals) - - ret = rule.fn(game, new_locals) - return ret +Number = (s)-> s -class Thunk - new:(@actions, @body_str)=> - if @actions.startPos - assert currently_parsing, "Not currently parsing!" - unless @body_str - @body_str = currently_parsing\sub(@actions.startPos, @actions.endPos-1) +String = (s)-> '"'..s..'"' - as_value:=>@ +List = (t)-> "{" .. table.concat(t, ", ") .. "}" - run:(game, locals)=> - assert((game and locals), "f'd up") - ret = nil - for a in *@actions - ret = a\run game,locals - return ret +Var = (s)-> "locals[\"#{s}\"]" + +Word = (s)-> setmetatable({type:'word', text:s}, {__tostring:=> error("Cannot convert word \"#{@text}\" to string")}) + +Conditional = (condition, if_block, else_block)-> + ret = {} + add_line ret, "(function()" + indent! + add_line ret, "local ret" + table.insert ret, indent_block("local condition = #{condition}") - __tostring:=> - --"Thunk(#{table.concat([tostring(a) for a in *@actions], ", ") .. tostring(@actions.returnValue or "") })" - "{#{@body_str}}" + add_line ret, "if condition then" + indent! + table.insert ret, indent_block("ret = (#{if_block})(game, locals)") + if else_block + dedent! + add_line ret, "else" + indent! + table.insert ret, indent_block("ret = (#{else_block})(game, locals)") + dedent! + add_line ret, "end" + add_line ret, "return ret" + dedent! + add_line ret, "end)()" + code = table.concat(ret, "\n") + return code + +FunctionCall = (tokens)-> + words = [(if t.type == 'word' then t.text else "$") for t in *tokens] + args = [t for t in *tokens when t.type != 'word'] + rule_name = table.concat(words, " ") + if rule_name == "$" + error("Empty rule: #{repr(tokens)}") + + if macros[rule_name] + return macros[rule_name](unpack(args)) + + if #args == 0 + return indent_block("game:call(\"#{rule_name}\")") + + ret = {} + add_line ret, "game:call(\"#{rule_name}\"," + indent! + arg_strs = [indent_block(arg) for arg in *args] + dedent! + table.insert ret, table.concat(arg_strs, ",\n") + add_line ret, ")" + + code = table.concat(ret, "\n") + return code + +Thunk = (lines)-> + ret = {} + add_line ret, "function(game, locals)" + indent! + for i,line in ipairs lines + if line\match "locals%[\".*\"%] = .*" + table.insert ret, indent_block(line) + elseif i == #lines + table.insert ret, indent_block("return "..line..";") + else + table.insert ret, indent_block(line..";") + dedent! + add_line ret, "end" + return table.concat(ret, "\n") lingo = [[ actions <- {| {:startPos: {}:} (%ws*) (%nl %ws*)* (action ((%nl %ws*)+ action)*)? (%nl %ws*)* {:endPos: {}:} |} -> Thunk - action <- {| token (%ws+ token)* %ws* |} -> Action + action <- (&conditional conditional) / ({| token (%ws+ token)* %ws* |} -> FunctionCall) + conditional <- ("if" !%wordchars %ws* expression %ws* thunk (%ws* "else" %ws* thunk %ws*)?) -> Conditional token <- expression / (!"$" {%wordchars+} -> Word) expression <- number / string / list / variable / thunk / subexpression - number <- ('-'? [0-9]+ ("." [0-9]+)?) -> tonumber - string <- ('"' {(("\\" .) / [^"])*} '"') -> tostring - list <- {| '[' (expression (',' (%ws*) expression)*)? ']' |} + number <- ('-'? [0-9]+ ("." [0-9]+)?) -> Number + string <- ('"' {(("\\" .) / [^"])*} '"') -> String + list <- ({| '[' %ws* (%nl %ws*)* (expression (',' %ws* (%nl %ws*)* expression)*)? %ws* (%nl %ws*)* ']' |}) -> List variable <- ("$" {%wordchars+}) -> Var subexpression <- ("(" %ws* action ")") thunk <- ("{" actions "}") + keywords <- "if" / "else" / "let" ]] defs = { - Var: (...)->Var(...) - Word: (...)->Word(...) - Action: (...)->Action(...) - Thunk: (...)->Thunk(...) - tostring: tostring - tonumber: tonumber + :Var + :Word + :String + :Number + :List + :FunctionCall + :Thunk + :Conditional ws: lpeg.S(" \t") - wordchars: lpeg.P(1)-lpeg.S(' \t\n{}[]()"') + wordchars: lpeg.P(1)-lpeg.S(' \t\n,{}[]()"') } lingo = re.compile lingo, defs -class Rule - new:(invocations, action, docstring)=> - if type(action) == Thunk - @body_str = docstring or tostring(action) - @fn = (game,locals)-> action\run(game, locals) - elseif type(action) == 'function' - @body_str = docstring or "<lua function>" - @fn = action - else - error("Invalid action type: #{type(action)}") +cross_compile = (nomic_code)-> + export currently_parsing + old_parsing = currently_parsing + currently_parsing = nomic_code + lua_code = lingo\match nomic_code + currently_parsing = old_parsing + return lua_code - eq = (x,y)-> - if #x != #y then return false - for i=1,#x - if x[i] != y[i] then return false - return true +defaulttable = (table,key)-> + new = {} + table[key] = new + return new - @raw_invocations = invocations - @invocations = {} +class Game + new:(@parent)=> + @rules = setmetatable({}, {__index:parent and parent.rules or nil}) + @macros = setmetatable({}, {__index:parent and parent.macros or nil}) + @arg_names = setmetatable({}, {__index:parent and parent.arg_names or nil}) + @relations = setmetatable({}, {__index:parent and parent.relations or defaulttable}) + @invocations = setmetatable({}, {__index:parent and parent.invocations or nil}) + @authorized = setmetatable({}, {__index:parent and parent.authorized or nil}) + @callstack = {} + @debug = false + @you = "Anonymous" + + def: (invocations, fn)=> + if not fn then fn = false + invocations = if type(invocations) == 'table' then invocations else {invocations} + if fn then @invocations[fn] = {} + else @invocations[fn] = false 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") + invocation, arg_names = compactify_invocation raw_invocation + if invocation == "$" + error("Anonymous function: #{raw_invocation}") + @rules[invocation] = fn + if fn + table.insert @invocations[fn], invocation + @arg_names[invocation] = arg_names 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 ")}" - + @arg_names[invocation] = false + + macro: (invocation, fn)=> + invocation, _ = compactify_invocation invocation + @macros[invocation] = fn + + undefine: (invocations)=> + @\def invocations, false + + all_aliases: (invocations)=> + if type(invocations) != 'table' then invocations = {invocations} + all_aliases = {} + for i in *invocations + all_aliases[i] = true + if not @invocations[@rules[i]] + error "Could not find aliases of [[#{i}]]" + for alias in *@invocations[@rules[i]] + all_aliases[alias] = true + return [a for a in pairs(all_aliases)] -class Game - new:(parent)=> - @rules = setmetatable({}, {__index:parent and parent.rules or nil}) - @relations = setmetatable({}, {__index:parent and parent.relations or nil}) + canonicalize: (invocations)=> + if type(invocations) == 'string' + return @invocations[@rules[invocations]][1] + canonicals = {} + for i in *invocations + if @rules[i] == nil + error "Attempt to canonicalize invalid invocation: #{i}" + canonicals[@invocations[@rules[i]][1]] = true + return [c for c in pairs canonicals] - def: (invocation, action, docstring)=> - invocations = if type(invocation) == 'table' then invocation else {invocation} - rule = Rule(invocations, action, docstring) - for invocation in *rule.invocations - @rules[invocation] = rule + set_whitelist: (actions, whitelist)=> + if is_list whitelist then whitelist = {w,true for w in *whitelist} + for action in *@all_aliases(actions) + @authorized[action] = whitelist - proxy: (rule)=> - for i in *rule.invocations - @rules[i] = rule + check_authorization:(action)=> + authority = @authorized[action] + return true if authority == nil + for call in *@callstack + if authority[call] then return true + return false run: (str, user)=> - export currently_parsing - old_parsing = currently_parsing - currently_parsing = str - ok,thunk = pcall lingo.match, lingo, str - currently_parsing = old_parsing - if not ok - error(thunk) - - unless thunk - error("failed to parse nomic:\n#{str}") - - prev_you = @you - @you = user or "anon" - ok,ret = pcall thunk.run, thunk, @, {} - @you = prev_you - if not ok - error(ret) - if ret != nil - print("= #{ret}") + user or= "anon" + + if @debug + print("SOURCE NOMIC CODE:\n#{str}") + export macros + old_macros = macros + macros = (self.macros) + lua_code = cross_compile str + macros = old_macros + if @debug + print("\nGENERATED LUA CODE:\n#{lua_code}") + + lua_thunk, err = loadstring("return "..lua_code) + if not lua_thunk + print("Parsing: "..lua_code) + error(err) + lua_fn = lua_thunk! + + ret = lua_fn @, {} return ret + call: (invocation, ...)=> + if not @rules[invocation] + error "Could not find rule: '#{invocation}'" + if not @\check_authorization invocation + print "Not authorized to #{invocation} from callstack: #{repr(@callstack)}" + return + table.insert @callstack, invocation + arg_names = @arg_names[invocation] + args = {...} + ret = (@rules[invocation])(@, {arg_names[i],arg for i, arg in ipairs(args)}) + table.remove @callstack + return ret + + run_debug:(...)=> + @debug = true + print("Debugging:") + @run ... + @debug = false + repl:=> while true io.write(">> ") @@ -180,4 +280,6 @@ class Game break @\run buf + repr: repr + return Game diff --git a/nomic2.moon b/nomic2.moon deleted file mode 100644 index 2a03080..0000000 --- a/nomic2.moon +++ /dev/null @@ -1,285 +0,0 @@ -re = require 're' -lpeg = require 'lpeg' -moon = require 'moon' -type = moon.type - -is_list = (t)-> - i = 0 - for _ in pairs(t) - i += 1 - if t[i] == nil then return false - return true - -repr = (x)-> - if type(x) == 'table' - if is_list x - "[#{table.concat([repr(i) for i in *x], ", ")}]" - else - "{#{table.concat(["#{k}: #{v}" for k,v in pairs x], ", ")}}" - else - tostring(x) - -currently_parsing = nil -macros = nil -indentation = 0 -indents = -> - (" ")\rep(indentation) -indent = -> - export indentation - indentation += 1 -dedent = -> - export indentation - indentation -= 1 -indent_block = (block)-> - block = block\gsub("\n", "\n"..indents!) - return indents!..block -add_line = (lines, new_line)-> - table.insert lines, (indents!..new_line) - -compactify_invocation = (raw_invocation)-> - 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 - invocation = table.concat name_bits, " " - return invocation, arg_names - -Number = (s)-> s - -String = (s)-> '"'..s..'"' - -List = (t)-> "{" .. table.concat(t, ", ") .. "}" - -Var = (s)-> "locals[\"#{s}\"]" - -Word = (s)-> setmetatable({type:'word', text:s}, {__tostring:=> error("Cannot convert word \"#{@text}\" to string")}) - -Conditional = (condition, if_block, else_block)-> - ret = {} - add_line ret, "(function()" - indent! - add_line ret, "local ret" - table.insert ret, indent_block("local condition = #{condition}") - - add_line ret, "if condition then" - indent! - table.insert ret, indent_block("ret = (#{if_block})(game, locals)") - if else_block - dedent! - add_line ret, "else" - indent! - table.insert ret, indent_block("ret = (#{else_block})(game, locals)") - dedent! - add_line ret, "end" - add_line ret, "return ret" - dedent! - add_line ret, "end)()" - code = table.concat(ret, "\n") - return code - -FunctionCall = (tokens)-> - words = [(if t.type == 'word' then t.text else "$") for t in *tokens] - args = [t for t in *tokens when t.type != 'word'] - rule_name = table.concat(words, " ") - if rule_name == "$" - error("Empty rule: #{repr(tokens)}") - - if macros[rule_name] - return macros[rule_name](unpack(args)) - - if #args == 0 - return indent_block("game:call(\"#{rule_name}\")") - - ret = {} - add_line ret, "game:call(\"#{rule_name}\"," - indent! - arg_strs = [indent_block(arg) for arg in *args] - dedent! - table.insert ret, table.concat(arg_strs, ",\n") - add_line ret, ")" - - code = table.concat(ret, "\n") - return code - -Thunk = (lines)-> - ret = {} - add_line ret, "function(game, locals)" - indent! - for i,line in ipairs lines - if line\match "locals%[\".*\"%] = .*" - table.insert ret, indent_block(line) - elseif i == #lines - table.insert ret, indent_block("return "..line..";") - else - table.insert ret, indent_block(line..";") - dedent! - add_line ret, "end" - return table.concat(ret, "\n") - -lingo = [[ - actions <- {| {:startPos: {}:} (%ws*) (%nl %ws*)* (action ((%nl %ws*)+ action)*)? (%nl %ws*)* {:endPos: {}:} |} -> Thunk - action <- (&conditional conditional) / ({| token (%ws+ token)* %ws* |} -> FunctionCall) - conditional <- ("if" !%wordchars %ws* expression %ws* thunk (%ws* "else" %ws* thunk %ws*)?) -> Conditional - token <- expression / (!"$" {%wordchars+} -> Word) - expression <- number / string / list / variable / thunk / subexpression - number <- ('-'? [0-9]+ ("." [0-9]+)?) -> Number - string <- ('"' {(("\\" .) / [^"])*} '"') -> String - list <- ({| '[' %ws* (%nl %ws*)* (expression (',' %ws* (%nl %ws*)* expression)*)? %ws* (%nl %ws*)* ']' |}) -> List - variable <- ("$" {%wordchars+}) -> Var - subexpression <- ("(" %ws* action ")") - thunk <- ("{" actions "}") - keywords <- "if" / "else" / "let" -]] -defs = { - :Var - :Word - :String - :Number - :List - :FunctionCall - :Thunk - :Conditional - ws: lpeg.S(" \t") - wordchars: lpeg.P(1)-lpeg.S(' \t\n,{}[]()"') -} -lingo = re.compile lingo, defs - -cross_compile = (nomic_code)-> - export currently_parsing - old_parsing = currently_parsing - currently_parsing = nomic_code - lua_code = lingo\match nomic_code - currently_parsing = old_parsing - return lua_code - -defaulttable = (table,key)-> - new = {} - table[key] = new - return new - -class Game - new:(@parent)=> - @rules = setmetatable({}, {__index:parent and parent.rules or nil}) - @macros = setmetatable({}, {__index:parent and parent.macros or nil}) - @arg_names = setmetatable({}, {__index:parent and parent.arg_names or nil}) - @relations = setmetatable({}, {__index:parent and parent.relations or defaulttable}) - @invocations = setmetatable({}, {__index:parent and parent.invocations or nil}) - @authorized = setmetatable({}, {__index:parent and parent.authorized or nil}) - @callstack = {} - @debug = false - @you = "Anonymous" - - def: (invocations, fn)=> - if not fn then fn = false - invocations = if type(invocations) == 'table' then invocations else {invocations} - if fn then @invocations[fn] = {} - else @invocations[fn] = false - for raw_invocation in *invocations - invocation, arg_names = compactify_invocation raw_invocation - if invocation == "$" - error("Anonymous function: #{raw_invocation}") - @rules[invocation] = fn - if fn - table.insert @invocations[fn], invocation - @arg_names[invocation] = arg_names - else - @arg_names[invocation] = false - - macro: (invocation, fn)=> - invocation, _ = compactify_invocation invocation - @macros[invocation] = fn - - undefine: (invocations)=> - @\def invocations, false - - all_aliases: (invocations)=> - if type(invocations) != 'table' then invocations = {invocations} - all_aliases = {} - for i in *invocations - all_aliases[i] = true - if not @invocations[@rules[i]] - error "Could not find aliases of [[#{i}]]" - for alias in *@invocations[@rules[i]] - all_aliases[alias] = true - return [a for a in pairs(all_aliases)] - - canonicalize: (invocations)=> - if type(invocations) == 'string' - return @invocations[@rules[invocations]][1] - canonicals = {} - for i in *invocations - if @rules[i] == nil - error "Attempt to canonicalize invalid invocation: #{i}" - canonicals[@invocations[@rules[i]][1]] = true - return [c for c in pairs canonicals] - - set_whitelist: (actions, whitelist)=> - if is_list whitelist then whitelist = {w,true for w in *whitelist} - for action in *@all_aliases(actions) - @authorized[action] = whitelist - - check_authorization:(action)=> - authority = @authorized[action] - return true if authority == nil - for call in *@callstack - if authority[call] then return true - return false - - run: (str, user)=> - user or= "anon" - - if @debug - print("SOURCE NOMIC CODE:\n#{str}") - export macros - old_macros = macros - macros = (self.macros) - lua_code = cross_compile str - macros = old_macros - if @debug - print("\nGENERATED LUA CODE:\n#{lua_code}") - - lua_thunk, err = loadstring("return "..lua_code) - if not lua_thunk - print("Parsing: "..lua_code) - error(err) - lua_fn = lua_thunk! - - ret = lua_fn @, {} - return ret - - call: (invocation, ...)=> - if not @rules[invocation] - error "Could not find rule: '#{invocation}'" - if not @\check_authorization invocation - print "Not authorized to #{invocation} from callstack: #{repr(@callstack)}" - return - table.insert @callstack, invocation - arg_names = @arg_names[invocation] - args = {...} - ret = (@rules[invocation])(@, {arg_names[i],arg for i, arg in ipairs(args)}) - table.remove @callstack - return ret - - run_debug:(...)=> - @debug = true - print("Debugging:") - @run ... - @debug = false - - repl:=> - while true - io.write(">> ") - buf = "" - while buf\sub(-2,-1) != "\n\n" - buf ..= io.read("*line").."\n" - if buf == "exit\n\n" or buf == "quit\n\n" - break - @\run buf - - repr: repr - -return Game |
