Updated to undo some of the block/thunk stuff. Thunks are thunks, and

expressions can be grouped with parens, and they have a clear
distinction.
This commit is contained in:
Bruce Hill 2017-10-02 17:21:22 -07:00
parent e2bbbfe161
commit dcd3391b36
12 changed files with 538 additions and 613 deletions

View File

@ -3,28 +3,35 @@ require "lib/testing.nom"
test: say "foo"
..yields ".."
|Call [say %]:
| "foo"
|FunctionCall:
| Word:
| "say"
| String:
| "foo"
test: say (foo)
..yields ".."
|Call [say %]:
| Call [foo]!
|FunctionCall:
| Word:
| "say"
| FunctionCall:
| Word:
| "foo"
test:
rule [fart] =: say "poot"
rule (fart) =: say "poot"
..yields ".."
|Call [rule % = %]:
| List:
| Call [fart]!
| Thunk:
| Call [fart]:
| Block:
| Call [say %]:
| "poot"
test: say (subexpressions work)
..yields ".."
|Call [say %]:
| Call [subexpressions work]!
| Call [subexpressions work]:
test: say ["lists", "work"]
..yields ".."
@ -33,9 +40,9 @@ test: say ["lists", "work"]
| "lists"
| "work"
test (: say []) yields ".."
test (say []) yields ".."
|Call [say %]:
| <Empty List>
| List:
test:
say [..]
@ -69,50 +76,50 @@ test:
..yields ".."
|Call [if % % else %]:
| 1
| Thunk:
| Call [yes]!
| Thunk:
| Call [no]!
| Block:
| Call [yes]:
| Block:
| Call [no]:
test:
if 1 (: yes) else: no
if 1 (yes) else: no
..yields ".."
|Call [if % % else %]:
| 1
| Thunk:
| Call [yes]!
| Thunk:
| Call [no]!
| Block:
| Call [yes]:
| Block:
| Call [no]:
test: say (do: return 5)
test: say (do (return 5))
..yields ".."
|Call [say %]:
| Call [do %]:
| Thunk:
| Block:
| Call [return %]:
| 5
test:
say (..)
say:
fn call
..yields ".."
|Call [say %]:
| Call [fn call]!
| Call [fn call]:
test:
do: say "one liner"
..also: say "another one liner"
..yields ".."
|Call [do % also %]:
| Thunk:
| Block:
| Call [say %]:
| "one liner"
| Thunk:
| Block:
| Call [say %]:
| "another one liner"
test:
say (..)
say:
do:
say "hi"
return 5
@ -120,7 +127,7 @@ test:
..yields ".."
|Call [say %]:
| Call [do %]:
| Thunk:
| Block:
| Call [say %]:
| "hi"
| Call [return %]:
@ -150,21 +157,21 @@ test:
..yields ".."
|Call [if % % else %]:
| Var["x"]
| Thunk:
| Call [x]!
| Thunk:
| Block:
| Call [x]:
| Block:
| Call [if % % else %]:
| Var["y"]
| Thunk:
| Call [y]!
| Thunk:
| Call [z]!
| Block:
| Call [y]:
| Block:
| Call [z]:
test:
don't fuck this up
..yields ".."
|Call [don 't fuck this up]!
|Call [don 't fuck this up]:
test:
%Brian's hat
@ -173,3 +180,4 @@ test:
| Var["Brian"]
say "All tests passed!"

View File

@ -5,122 +5,116 @@ require "lib/operators.nom"
# List/dict functions:
# Indexing
parse:
%index st in %list; %index nd in %list; %index rd in %list
%index th in %list; %index in %list
parse [..]
%index st in %list, %index nd in %list, %index rd in %list
%index th in %list, %index in %list
..as: %list -> %index
compile:
%index st to last in %list; %index nd to last in %list; %index rd to last in %list
compile [..]
%index st to last in %list, %index nd to last in %list, %index rd to last in %list
%index th to last in %list
..to: "nomsu.utils.nth_to_last(\(%list as lua), \(%index as lua))"
parse (first in %list; first %list) as: 1 st in %list
parse (last in %list; last %list) as: 1 st to last in %list
parse [first in %list, first %list] as: 1 st in %list
parse [last in %list, last %list] as: 1 st to last in %list
# Dict iteration convenience function. This could also be accomplished with: for all (entries in %dict): ...
compile (for %key -> %value in %dict %body) to block: ".."
|for k, v in pairs(\(%dict as lua)) do
| \(%key as lua), \(%value as lua) = k, v
| \(%body as lua statements)
|end
compile [for %key -> %value in %dict %body] to code: ".."
|do;
| for k, v in pairs(\(%dict as lua)) do;
| \(%key as lua), \(%value as lua) = k, v;
| \(%body as lua statements)
| end;
|end;
# Membership testing
rule (%item is in %list; %list contains %item; %list has %item) =:
rule [%item is in %list, %list contains %item, %list has %item] =:
for %key -> %value in %list:
if (%key == %item): return (yes)
return (no)
rule:
%item isn't in %list; %item is not in %list
%list doesn't contain %item; %list does not contain %item
%list doesn't have %item; %list does not have %item
rule [..]
%item isn't in %list, %item is not in %list
%list doesn't contain %item, %list does not contain %item
%list doesn't have %item, %list does not have %item
..=:
for %key -> %value in %list:
if (%key == %item): return (no)
return (yes)
compile (%list has key %index; %list has index %index) to: ".."
|(\(%list as lua)[\(%index as lua)] ~= nil)
compile [%list has key %index, %list has index %index] to: ".."
|((\(%list as lua))[\(%index as lua)] ~= nil)
compile:
%list doesn't have key %index; %list does not have key %index
%list doesn't have index %index; %list does not have index %index
..to: "(\(%list as lua)[\(%index as lua)] ~= nil)"
compile [..]
%list doesn't have key %index, %list does not have key %index
%list doesn't have index %index, %list does not have index %index
..to: "((\(%list as lua))[\(%index as lua)] ~= nil)"
compile (length of %list; size of %list; size %list; number of %list; len %list) to:
compile [length of %list, size of %list, size %list, number of %list, len %list] to:
"nomsu.utils.size(\(%list as lua))"
# Chained lookup
compile (%list ->* %indices) to:
compile [%list ->* %indices] to:
assert ((%indices's "type") == "List") ".."
|Expected List for chained lookup, not \(%indices's "type")
%ret =: "\(%list as lua)"
%ret = "\(%list as lua)"
for %index in (%indices's "value"):
%ret join=: "[\(%index as lua)]"
%ret join= "[\(%index as lua)]"
"\(%ret)"
# Assignment
compile:
%list's %index = %new_value; %index st in %list = %new_value; %index nd in %list = %new_value
%index rd in %list = %new_value; %index th in %list = %new_value; %index in %list = %new_value
compile [..]
%list's %index = %new_value, %index st in %list = %new_value, %index nd in %list = %new_value
%index rd in %list = %new_value, %index th in %list = %new_value, %index in %list = %new_value
%list -> %index = %new_value
..to code:
assert ((%new_value's "type") == "Block") ".."
|Dict assignment operation has the wrong type for the right hand side.
|Expected Block, but got \(%new_value's "type").
|Maybe you used "=" instead of "=:"?
assert ((size of (%new_value's "value")) == 1) ".."
|Dict assignment operation has the wrong number of values on the right hand side.
|Expected 1 value, but got \(repr %new_value)
%new_value =: %new_value ->* ["value",1]
"\(%list as lua)[\(%index as lua)] = \(%new_value as lua)"
"(\(%list as lua))[\(%index as lua)] = \(%new_value as lua)"
compile (append %item to %list; add %item to %list) to:
compile [append %item to %list, add %item to %list] to:
"table.insert(\(%list as lua), \(%item as lua))"
rule (flatten %lists) =:
%flat =: []
rule [flatten %lists] =:
%flat = []
for %list in %lists:
for %item in %list:
add %item to %flat
%flat
rule (dict %items) =:
%dict =: []
rule [dict %items] =:
%dict = []
for %pair in %items:
%dict -> (first in %pair) =: last in %pair
%dict -> (%pair -> 1) = (%pair -> 2)
%dict
rule (entries in %dict) =:
%entries =: []
rule [entries in %dict] =:
%entries = []
for %k -> %v in %dict:
add (dict [["key",%k],["value",%v]]) to %entries
%entries
rule (keys in %dict) =:
%keys =: []
rule [keys in %dict] =:
%keys = []
for %k -> %v in %dict: add %k to %keys
%keys
rule (values in %dict) =:
%values =: []
rule [values in %dict] =:
%values = []
for %k -> %v in %dict: add %v to %values
%values
# List Comprehension
compile (%expression for %var in %iterable) to:
compile [%expression for %var in %iterable] to:
assert ((%var's "type") == "Var") ".."
|List comprehension has the wrong type for the loop variable. Expected Var, but got: \(%var's "type")
".."
|(function(game, vars)
| local comprehension = {}
| for i,value in ipairs(\(%iterable as lua)) do
| \(%var as lua) = value
| comprehension[i] = \(%expression as lua)
| end
| return comprehension
|(function(game, vars);
| local comprehension = {};
| for i,value in ipairs(\(%iterable as lua)) do;
| \(%var as lua) = value;
| comprehension[i] = \(%expression as lua);
| end;
| return comprehension;
|end)(game, setmetatable({}, {__index=vars}))
parse (%expression for all %iterable) as: %expression for % in %iterable
parse [%expression for all %iterable] as: %expression for % in %iterable
# TODO: maybe make a generator/coroutine?

View File

@ -3,168 +3,174 @@ require "lib/operators.nom"
require "lib/utils.nom"
# Conditionals
compile (if %condition %if_body) to code: ".."
|if \(%condition as lua) then
compile [if %condition %if_body] to code: ".."
|if \(%condition as lua) then;
| \(%if_body as lua statements)
|end
|end;
compile (if %condition %if_body else %else_body) to code: ".."
|if \(%condition as lua) then
compile [if %condition %if_body else %else_body] to code: ".."
|if \(%condition as lua) then;
| \(%if_body as lua statements)
|else
|else;
| \(%else_body as lua statements)
|end
|end;
# Return
compile (return) to code: "do return end"
compile (return %return-value) to code: "do return \(%return-value as lua) end"
compile [return] to code: "do; return; end;"
compile [return %return-value] to code: "do; return \(%return-value as lua); end;"
# GOTOs
compile (-> %label) to code: ".."
|::label_\(nomsu "var_to_lua_identifier" [%label])::
compile (go to %label) to code: ".."
|goto label_\(nomsu "var_to_lua_identifier" [%label])
compile [-> %label] to code: ".."
|::label_\(nomsu "var_to_lua_identifier" [%label])::;
compile [go to %label] to code: ".."
|goto label_\(nomsu "var_to_lua_identifier" [%label]);
# Loop control flow
compile (stop; stop loop; break) to code: "break"
compile (stop for; stop for-loop; break for) to code: "goto break_for"
compile (stop repeat; stop repeat-loop; break repeat) to code: "goto break_repeat"
compile (stop %var; break %var) to code: ".."
|goto break_\(nomsu "var_to_lua_identifier" [%var])
compile [stop, stop loop, break] to code: "break"
compile [stop for, stop for-loop, break for] to code: "goto break_for"
compile [stop repeat, stop repeat-loop, break repeat] to code: "goto break_repeat"
compile [stop %var, break %var] to code: ".."
|goto break_\(nomsu "var_to_lua_identifier" [%var]);
compile (continue; continue loop) to code: "continue"
compile (continue for; continue for-loop) to code: "goto continue_for"
compile (continue repeat; continue repeat-loop) to code: "goto continue_repeat"
compile (continue %var; go to next %var; on to the next %var) to code: ".."
|goto continue_\(nomsu "var_to_lua_identifier" [%var])
compile [continue, continue loop] to code: "continue"
compile [continue for, continue for-loop] to code: "goto continue_for"
compile [continue repeat, continue repeat-loop] to code: "goto continue_repeat"
compile [continue %var, go to next %var, on to the next %var] to code: ".."
|goto continue_\(nomsu "var_to_lua_identifier" [%var]);
# While loops
compile (repeat while %condition %body) to block: ".."
|while \(%condition as lua) do
| \(%body as lua statements)
| ::continue_repeat::
|end
|::break_repeat::
parse (repeat %body) as: repeat while (true) %body
parse (repeat until %condition %body) as: repeat while (not %condition) %body
compile [repeat while %condition %body] to code: ".."
|do;
| while \(%condition as lua) do;
| \(%body as lua statements)
| ::continue_repeat::;
| end;
| ::break_repeat::;
|end;
parse [repeat %body] as: repeat while (true) %body
parse [repeat until %condition %body] as: repeat while (not %condition) %body
# Numeric range for loops
compile:
compile [..]
for %var from %start to %stop by %step %body
for %var from %start to %stop via %step %body
..to block: ".."
|for i=\(%start as lua),\(%stop as lua),\(%step as lua) do
..to code: ".."
|do;
| for i=\(%start as lua),\(%stop as lua),\(%step as lua) do;
# This trashes the loop variables, just like in Python.
| \(%var as lua) = i
| \(%body as lua statements)
| ::continue_for::
| ::continue_\(nomsu "var_to_lua_identifier" [%var])::
|end
|::break_for::
|::break_\(nomsu "var_to_lua_identifier" [%var])::
parse (for %var from %start to %stop %body) as: for %var from %start to %stop via 1 %body
parse:
| \(%var as lua) = i;
| \(%body as lua statements)
| ::continue_for::;
| ::continue_\(nomsu "var_to_lua_identifier" [%var])::;
| end;
| ::break_for::;
| ::break_\(nomsu "var_to_lua_identifier" [%var])::;
|end;
parse [for %var from %start to %stop %body] as: for %var from %start to %stop via 1 %body
parse [..]
for all %start to %stop by %step %body
for all %start to %stop via %step %body
..as: for % from %start to %stop via %step %body
parse (for all %start to %stop %body) as: for all %start to %stop via 1 %body
parse [for all %start to %stop %body] as: for all %start to %stop via 1 %body
compile (for %var in %iterable %body) to block:
compile [for %var in %iterable %body] to code:
".."
|for i,value in ipairs(\(%iterable as lua)) do
|do;
| for i,value in ipairs(\(%iterable as lua)) do;
# This trashes the loop variables, just like in Python.
| \(%var as lua) = value
| \(%body as lua statements)
| ::continue_for::
| ::continue_\(nomsu "var_to_lua_identifier" [%var])::
|end
|::break_for::
|::break_\(nomsu "var_to_lua_identifier" [%var])::
parse (for all %iterable %body) as: for % in %iterable %body
| \(%var as lua) = value;
| \(%body as lua statements)
| ::continue_for::;
| ::continue_\(nomsu "var_to_lua_identifier" [%var])::;
| end;
| ::break_for::;
| ::break_\(nomsu "var_to_lua_identifier" [%var])::;
|end;
parse [for all %iterable %body] as: for % in %iterable %body
# Switch statement/multi-branch if
compile (when %body) to block:
%result =: ""
%fallthroughs =: []
compile [when %body] to code:
%result = "do;\n"
%fallthroughs = []
for %func-call in (%body's "value"):
assert ((%func-call's "type") == "FunctionCall") ".."
|Invalid format for 'when' statement. Only '*' blocks are allowed.
%tokens =: %func-call's "value"
%star =: %tokens -> 1
%tokens = (%func-call's "value")
%star = (%tokens -> 1)
assert (lua expr "vars.star and vars.star.type == 'Word' and vars.star.value == '*'") ".."
|Invalid format for 'when' statement. Lines must begin with '*'
%condition =: %tokens -> 2
%condition = (%tokens -> 2)
assert %condition ".."
|Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else"
%action =: %tokens -> 3
%action = (%tokens -> 3)
if (%action == (nil)):
lua block "table.insert(vars.fallthroughs, vars.condition)"
go to next %func-call
if (lua expr "vars.condition.type == 'Word' and vars.condition.value == 'else'"):
%result join=: ".."
%result join= ".."
|
|do
|do;
..else:
%condition =: %condition as lua
%condition = (%condition as lua)
for all %fallthroughs:
%condition join=: " or \(% as lua)"
%result join=: ".."
%condition join= " or \(% as lua)"
%result join= ".."
|
|if \(%condition) then
%result join=: ".."
|if \(%condition) then;
%result join= ".."
|
| \(%action as lua statements)
| goto finished_when
|end
| goto finished_when;
|end;
%fallthroughs =: []
%fallthroughs = []
%result join=: "\n::finished_when::"
%result join= "\n::finished_when::;\nend;"
%result
# Switch statement
compile (when %branch-value == ? %body) to block:
%result =: "local branch_value = \(%branch-value as lua)"
%fallthroughs =: []
compile [when %branch-value == ? %body] to code:
%result = "do;\nlocal branch_value = \(%branch-value as lua)"
%fallthroughs = []
for %func-call in (%body's "value"):
assert ((%func-call's "type") == "FunctionCall") ".."
|Invalid format for 'when' statement. Only '*' blocks are allowed.
%tokens =: %func-call's "value"
%star =: %tokens -> 1
%tokens = (%func-call's "value")
%star = (%tokens -> 1)
assert (lua expr "vars.star and vars.star.type == 'Word' and vars.star.value == '*'") ".."
|Invalid format for 'when' statement. Lines must begin with '*'
%condition =: %tokens -> 2
%condition = (%tokens -> 2)
assert %condition ".."
|Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else"
%action =: %tokens -> 3
%action = (%tokens -> 3)
if (%action == (nil)):
lua block "table.insert(vars.fallthroughs, vars.condition)"
go to next %func-call
if (lua expr "vars.condition.type == 'Word' and vars.condition.value == 'else'"):
%result join=: ".."
%result join= ".."
|
|do
|do;
..else:
%condition =: "branch_value == (\(%condition as lua))"
%condition = "branch_value == (\(%condition as lua))"
for all %fallthroughs:
%condition join=: " or (branch_value == \(% as lua))"
%result join=: ".."
%condition join= " or (branch_value == \(% as lua))"
%result join= ".."
|
|if \(%condition) then
%result join=: ".."
|if \(%condition) then;
%result join= ".."
|
| \(%action as lua statements)
| goto finished_when
|end
| goto finished_when;
|end;
%fallthroughs =: []
%fallthroughs = []
%result join=: "\n::finished_when::"
%result join= "\n::finished_when::;\nend;"
%result

View File

@ -4,127 +4,105 @@
# Rule to make rules:
lua code ".."
|nomsu:defmacro("rule %rule_def = %body", function(nomsu, vars)
| local aliases = nomsu:typecheck(vars, "rule_def", "Block").value
| local canonical = aliases[1]
| local body = nomsu:typecheck(vars, "body", "Block")
| local thunk = nomsu:tree_to_lua({type="Thunk", value={type="Statements", value=body.value, src=body.src}, src=body.src})
| local lua = ([[
|nomsu:defmacro("rule %signature = %body", (function(nomsu, vars)
| local signature = nomsu:typecheck(vars, "signature", "List").value;
| local body = nomsu:typecheck(vars, "body", "Thunk");
| return ([[
|nomsu:def(%s, %s, %s)
|]]):format(nomsu:repr(canonical.src), thunk, nomsu:repr(body.src))
| if #aliases > 1 then
| lua = lua .. "\n" .. ([[
|do
| local aliased = %s
| local src = %s
| local function dealiaser(nomsu, vars)
| return nomsu:tree_to_lua(nomsu:replaced_vars(aliased, vars))
| end
|]]):format(nomsu:repr(canonical), nomsu:repr(canonical.src))
| for i=2,#aliases do
| lua = lua .. ([[
| nomsu:defmacro(%s, dealiaser, %s)
|]]):format(nomsu:repr(aliases[i].src), nomsu:repr(canonical.src))
| end
| lua = lua .. [[
|end]]
| end
| return nil, lua
|end, "<source can be found in lib/metaprogramming.nom>")
|]]):format(nomsu:repr(signature), nomsu:tree_to_lua(body), nomsu:repr(body.src)), nil;
|end), "<source can be found in lib/metaprogramming.nom>");
# Rule to make nomsu macros:
rule (escaped parse %shorthand as %longhand) =:
rule [escaped parse %shorthand as %longhand] =:
lua code ".."
|local aliases = nomsu:typecheck(vars, "shorthand", "Block").value
|local template = nomsu:typecheck(vars, "longhand", "Block")
|local aliases = nomsu:typecheck(vars, "shorthand", "List").value;
|if #vars.longhand.value ~= 1 then;
| nomsu:error("Expected only 1 line to parse to, but got "..tostring(#vars.longhand.value));
|end;
|local template = nomsu:typecheck(vars, "longhand", "Thunk").value[1];
|local function parsing_as(nomsu, vars)
| local replacement = nomsu:replaced_vars(template, vars)
| return nomsu:tree_to_lua(replacement)
|end
|for _,call in ipairs(aliases) do
| nomsu:defmacro(call, parsing_as, template.src)
|end
escaped parse \(parse %shorthand as %longhand) as \: escaped parse \%shorthand as \%longhand
| local replacement = nomsu:replaced_vars(template, vars);
| return nomsu:tree_to_lua(replacement);
|end;
|nomsu:defmacro(aliases, parsing_as, template.src);
escaped parse \[parse %shorthand as %longhand] as \: escaped parse \%shorthand as \%longhand
# Rule to make lua macros:
rule (escaped compile %macro_def to %body) =:
rule [escaped compile %macro_def to %body] =:
lua code ".."
|local aliases = nomsu:typecheck(vars, "macro_def", "Block").value
|local body = nomsu:typecheck(vars, "body", "Block")
|local thunk = nomsu:tree_to_value({type="Thunk", value={type="Statements", value=body.value, src=body.src}, src=body.src})
|for _,alias in ipairs(aliases) do
| nomsu:defmacro(alias, thunk, body.src)
|end
rule (escaped compile %macro_def to code %body) =:
|local aliases = nomsu:typecheck(vars, "macro_def", "List").value;
|local body = nomsu:typecheck(vars, "body", "Thunk");
|local thunk = nomsu:tree_to_value(body);
|nomsu:defmacro(aliases, thunk, body.src);
rule [escaped compile %macro_def to code %body] =:
lua code ".."
|local aliases = nomsu:typecheck(vars, "macro_def", "Block").value
|local body = nomsu:typecheck(vars, "body", "Block")
|local thunk = nomsu:tree_to_value({type="Thunk", value={type="Statements", value=body.value, src=body.src}, src=body.src})
|local thunk2 = function(nomsu, vars) return nil, thunk(nomsu, vars) end
|for _,alias in ipairs(aliases) do
| nomsu:defmacro(alias, thunk2)
|end
parse (compile %macro_def to %body) as: escaped compile \%macro_def to \%body
parse (compile %macro_def to code %body) as: escaped compile \%macro_def to code \%body
parse (compile %macro_def to block %body) as: escaped compile \%macro_def to code\: ".."
|do
| \(%body)
|end
|local aliases = nomsu:typecheck(vars, "macro_def", "List").value;
|local body = nomsu:typecheck(vars, "body", "Thunk");
|local thunk = nomsu:tree_to_value(body);
|local thunk_wrapper = function(nomsu, vars) return nil, thunk(nomsu, vars); end;
|nomsu:defmacro(aliases, thunk_wrapper, body.src);
parse [compile %macro_def to %body] as: escaped compile \%macro_def to \%body
parse [compile %macro_def to code %body] as: escaped compile \%macro_def to code \%body
rule (do %) =: %
rule [do %] =: %
rule (%tree as lua) =:
rule [%tree as lua] =:
lua expr "nomsu:tree_to_lua(\(%tree))"
rule (%tree as value) =:
rule [%tree as value] =:
lua expr "nomsu:tree_to_value(\(%tree), vars)"
compile (repr %obj) to:
compile [repr %obj] to:
"nomsu:repr(\(%obj as lua))"
parse (lua block %block) as: lua code ".."
|do
parse [lua block %block] as: lua code ".."
|do;
| \(%block)
|end
rule (%tree as lua statement) =:
|end;
rule [%tree as lua statement] =:
lua block ".."
|local _,statement = nomsu:tree_to_lua(\(%tree))
|return statement
rule (%tree as lua statements) =:
|local _,statement = nomsu:tree_to_lua(\(%tree));
|return statement;
rule [%tree as lua statements] =:
lua block ".."
|local statements_tree = {type='Statements', value=\(%tree).value, src=\(%tree).src}
|local _,statements = nomsu:tree_to_lua(statements_tree)
|return statements
|local lua_bits = {};
|local statements = nomsu:typecheck(vars, "tree", "Thunk").value;
|for _,bit in ipairs(statements) do;
| local expr, statement = nomsu:tree_to_lua(bit);
| if statement then; table.insert(lua_bits, statement); end;
| if expr then; table.insert(lua_bits, "ret = "..expr..";"); end;
|end;
|return table.concat(lua_bits, "\\n");
compile (nomsu) to: "nomsu"
compile (nomsu's %key) to: "nomsu[\(%key as lua)]"
compile (nomsu %method %args) to: "nomsu[\(%method as lua)](nomsu, unpack(\(%args as lua)))"
compile [nomsu] to: "nomsu"
compile [nomsu's %key] to: "nomsu[\(%key as lua)]"
compile [nomsu %method %args] to: "nomsu[\(%method as lua)](nomsu, unpack(\(%args as lua)))"
# Get the source code for a function
rule (help %rule) =:
rule [help %rule] =:
lua block ".."
|local fn_def = nomsu:get_fn_def(vars.rule)
|if not fn_def then
| nomsu:writeln("Rule not found: "..nomsu:repr(vars.rule))
|else
| nomsu:writeln("rule "..nomsu:repr(nomsu.utils.keys(fn_def.invocation))
| .." ="..(fn_def.src or ":\\n <unknown source code>"))
|end
|local fn_def = nomsu:get_fn_def(vars.rule);
|if not fn_def then;
| nomsu:writeln("Rule not found: "..nomsu:repr(vars.rule));
|else;
| nomsu:writeln("rule "..nomsu:repr(nomsu.utils.keys(fn_def.stub))
| .." ="..(fn_def.src or ":\\n <unknown source code>"));
|end;
# Compiler tools
parse (eval %code; run %code) as: nomsu "run" [%code]
rule (source code from tree %tree) =:
parse [eval %code, run %code] as: nomsu "run" [%code]
rule [source code from tree %tree] =:
lua block ".."
|local _,_,leading_space = vars.tree.src:find("\\n(%s*)%S")
|if leading_space then
| local chunk1, chunk2 = vars.tree.src:match(":%s*([^\\n]*)(\\n.*)")
| chunk2 = chunk2:gsub("\\n"..leading_space, "\\n")
| return chunk1..chunk2.."\\n"
|else
| return vars.tree.src:match(":%s*(%S.*)").."\\n"
|end
parse (source code %body) as: source code from tree \%body
|local _,_,leading_space = vars.tree.src:find("\\n(%s*)%S");
|if leading_space then;
| local chunk1, chunk2 = vars.tree.src:match(":%s*([^\\n]*)(\\n.*)");
| chunk2 = chunk2:gsub("\\n"..leading_space, "\\n");
| return chunk1..chunk2.."\\n";
|else;
| return vars.tree.src:match(":%s*(%S.*)").."\\n";
|end;
parse [source code %body] as: source code from tree \%body
parse (parse tree %code) as: nomsu "tree_to_str" [\%code]
parse [parse tree %code] as: nomsu "tree_to_str" [\%code]
parse (enable debugging) as: lua code "nomsu.debug = true"
parse (disable debugging) as: lua code "nomsu.debug = false"
parse [enable debugging] as: lua code "nomsu.debug = true"
parse [disable debugging] as: lua code "nomsu.debug = false"

View File

@ -1,30 +1,30 @@
require "lib/metaprogramming.nom"
# Moonscript!
macro block [moonscript block %moonscript_code] =:
parse [moonscript block %moonscript_code] as:
lua block ".."
|local parse, compile = require('moonscript.parse'), require('moonscript.compile')
|local moon_code = nomsu:tree_to_value(vars.moonscript_code, vars)
|local tree, err = parse.string(moon_code)
|if not tree then
| nomsu:error("Failed to parse moonscript: "..err)
|end
|local lua_code, err, pos = compile.tree(tree)
|if not lua_code then
| nomsu:error(compile.format_error(err, pos, moon_code))
|end
|return "do\\n"..lua_code.."\\nend"
|local parse, compile = require('moonscript.parse'), require('moonscript.compile');
|local moon_code = nomsu:tree_to_value(vars.moonscript_code, vars);
|local tree, err = parse.string(moon_code);
|if not tree then;
| nomsu:error("Failed to parse moonscript: "..err);
|end;
|local lua_code, err, pos = compile.tree(tree);
|if not lua_code then;
| nomsu:error(compile.format_error(err, pos, moon_code));
|end;
|return "do\\n"..lua_code.."\\nend";
macro [moonscript %moonscript_code] =:
parse [moonscript %moonscript_code] as:
lua block ".."
|local parse, compile = require('moonscript.parse'), require('moonscript.compile')
|local moon_code = nomsu:tree_to_value(vars.moonscript_code, vars)
|local tree, err = parse.string(moon_code)
|if not tree then
| nomsu:error("Failed to parse moonscript: "..err)
|end
|local lua_code, err, pos = compile.tree(tree)
|if not lua_code then
| nomsu:error(compile.format_error(err, pos, moon_code))
|end
|return "(function(nomsu, vars)\\n"..lua_code.."\\nend)(nomsu, vars)"
|local parse, compile = require('moonscript.parse'), require('moonscript.compile');
|local moon_code = nomsu:tree_to_value(vars.moonscript_code, vars);
|local tree, err = parse.string(moon_code);
|if not tree then;
| nomsu:error("Failed to parse moonscript: "..err);
|end;
|local lua_code, err, pos = compile.tree(tree);
|if not lua_code then;
| nomsu:error(compile.format_error(err, pos, moon_code));
|end;
|return "(function(nomsu, vars)\\n"..lua_code.."\\nend)(nomsu, vars)";

View File

@ -1,124 +1,122 @@
require "lib/metaprogramming.nom"
# Literals
compile (true; yes) to: "true"
compile (false; no) to: "false"
compile (nil; null) to: "nil"
compile (inf; infinity) to: "math.huge"
compile (nan; NaN; not a number) to: "(0/0)"
compile (pi; PI) to: "math.pi"
compile (tau; TAU) to: "(2*math.pi)"
compile (phi; PHI; golden ratio) to: "((1+math.sqrt(5))/2)"
compile (nop; pass) to code: ""
compile [true, yes] to: "true"
compile [false, no] to: "false"
compile [nil, null] to: "nil"
compile [inf, infinity] to: "math.huge"
compile [nan, NaN, not a number] to: "(0/0)"
compile [pi, PI] to: "math.pi"
compile [tau, TAU] to: "(2*math.pi)"
compile [phi, PHI, golden ratio] to: "((1+math.sqrt(5))/2)"
compile [nop, pass] to code: ""
# Ternary operator
compile (%if_expr if %condition else %else_expr) to: ".."
compile [%if_expr if %condition else %else_expr] to: ".."
|(function(nomsu, vars)
# TODO: fix compiler bug that breaks this code if comments immediately follow ".."
#.. Note: this uses a function instead of (condition and if_expr or else_expr)
because that breaks if %if_expr is falsey.
| if \(%condition) then
| return \(%if_expr)
| else
| return \(%else_expr)
| end
| if \(%condition) then;
| return \(%if_expr);
| else;
| return \(%else_expr);
| end;
|end)(nomsu, vars)
# Indexing:
compile (%obj's %key; %obj -> %key) to: "\(%obj as lua)[\(%key as lua)]"
compile [%obj's %key, %obj -> %key] to: "(\(%obj as lua))[\(%key as lua)]"
# Variable assignment operator, and += type versions
compile (%var = %val) to code: "\(%var as lua) = \(%val as lua)"
compile (%var += %val) to code: "\(%var as lua) = \(%var as lua) + \(%val as lua)"
compile (%var -= %val) to code: "\(%var as lua) = \(%var as lua) - \(%val as lua)"
compile (%var *= %val) to code: "\(%var as lua) = \(%var as lua) * \(%val as lua)"
compile (%var /= %val) to code: "\(%var as lua) = \(%var as lua) / \(%val as lua)"
compile (%var ^= %val) to code: "\(%var as lua) = \(%var as lua) ^ \(%val as lua)"
compile (%var and= %val) to code: "\(%var as lua) = \(%var as lua) and\(%val as lua)"
compile (%var or= %val) to code: "\(%var as lua) = \(%var as lua) or \(%val as lua)"
compile (%var join= %val) to code: "\(%var as lua) = \(%var as lua) .. \(%val as lua)"
compile (%var mod= %val) to code: "\(%var as lua) = \(%var as lua) % \(%val as lua)"
%x =: 5
compile [%var = %val] to code: "\(%var as lua) = \(%val as lua);"
compile [%var += %val] to code: "\(%var as lua) = \(%var as lua) + \(%val as lua);"
compile [%var -= %val] to code: "\(%var as lua) = \(%var as lua) - \(%val as lua);"
compile [%var *= %val] to code: "\(%var as lua) = \(%var as lua) * \(%val as lua);"
compile [%var /= %val] to code: "\(%var as lua) = \(%var as lua) / \(%val as lua);"
compile [%var ^= %val] to code: "\(%var as lua) = \(%var as lua) ^ \(%val as lua);"
compile [%var and= %val] to code: "\(%var as lua) = \(%var as lua) and\(%val as lua);"
compile [%var or= %val] to code: "\(%var as lua) = \(%var as lua) or \(%val as lua);"
compile [%var join= %val] to code: "\(%var as lua) = \(%var as lua) .. \(%val as lua);"
compile [%var mod= %val] to code: "\(%var as lua) = \(%var as lua) % \(%val as lua);"
# Binary Operators
lua block ".."
|local binops = {"-","/","<","<=",">",">=","^",{"===","=="},{"!==","~="},{"mod","%"}}
|for _,op in ipairs(binops) do
| local nomsu_alias = op
| if type(op) == 'table' then
| nomsu_alias, op = unpack(op)
| end
|local binops = {"-","/","<","<=",">",">=","^",{"===","=="},{"!==","~="},{"mod","%"}};
|for _,op in ipairs(binops) do;
| local nomsu_alias = op;
| if type(op) == 'table' then;
| nomsu_alias, op = unpack(op);
| end;
| nomsu:defmacro("%a "..nomsu_alias.." %b", (function(nomsu, vars)
| return "("..nomsu:tree_to_lua(vars.a).." "..op.." "..nomsu:tree_to_lua(vars.b)..")"
| end), [["(\\(%a) ]]..op..[[ \\(%b))"]])
|end
| return "("..nomsu:tree_to_lua(vars.a).." "..op.." "..nomsu:tree_to_lua(vars.b)..")";
| end), [["(\\(%a) ]]..op..[[ \\(%b))"]]);
|end;
# TODO: implement OR, XOR, AND for multiple operands
compile (%a OR %b; %a | %b) to: "bit32.bor(\(%a as lua), \(%b as lua))"
compile (%a XOR %b) to: "bit32.bxor(\(%a as lua), \(%b as lua))"
compile (%a AND %b; %a & %b) to: "bit32.band(\(%a as lua), \(%b as lua))"
compile (NOT %; ~ %) to: "bit32.bnot(\(% as lua))"
compile (%x LSHIFT %shift; %x << %shift) to: "bit32.lshift(\(%x as lua), \(%shift as lua))"
compile (%x RSHIFT %shift) to: "bit32.rshift(\(%x as lua), \(%shift as lua))"
compile (%x ARSHIFT %shift; %x >> %shift) to: "bit32.arshift(\(%x as lua), \(%shift as lua))"
compile [%a OR %b, %a | %b] to: "bit32.bor(\(%a as lua), \(%b as lua))"
compile [%a XOR %b] to: "bit32.bxor(\(%a as lua), \(%b as lua))"
compile [%a AND %b, %a & %b] to: "bit32.band(\(%a as lua), \(%b as lua))"
compile [NOT %, ~ %] to: "bit32.bnot(\(% as lua))"
compile [%x LSHIFT %shift, %x << %shift] to: "bit32.lshift(\(%x as lua), \(%shift as lua))"
compile [%x RSHIFT %shift] to: "bit32.rshift(\(%x as lua), \(%shift as lua))"
compile [%x ARSHIFT %shift, %x >> %shift] to: "bit32.arshift(\(%x as lua), \(%shift as lua))"
# == and != do equivalence checking, rather than identity checking
compile (%a == %b) to: "nomsu.utils.equivalent(\(%a as lua), \(%b as lua))"
compile (%a != %b) to: "(not nomsu.utils.equivalent(\(%a as lua), \(%b as lua)))"
compile [%a == %b] to: "nomsu.utils.equivalent(\(%a as lua), \(%b as lua))"
compile [%a != %b] to: "(not nomsu.utils.equivalent(\(%a as lua), \(%b as lua)))"
# Commutative Operators defined for up to 8 operands
# TODO: work out solution for commutative operators using more clever macros
lua block ".."
|local max_operands = 8
|local comops = {"+","*","and","or"}
|for _,_op in ipairs(comops) do
| local op = _op
| local spec = "%1 "
| for n=2,max_operands do
| spec = spec .." "..op.." %"..tostring(n)
|local max_operands = 8;
|local comops = {"+","*","and","or"};
|for _,_op in ipairs(comops) do;
| local op = _op;
| local spec = "%1 ";
| for n=2,max_operands do;
| spec = spec .." "..op.." %"..tostring(n);
| nomsu:defmacro(spec, (function(nomsu, vars)
| local bits = {}
| for i=1,n do
| table.insert(bits, (nomsu:tree_to_lua(vars[tostring(i)])))
| end
| return "("..table.concat(bits, " "..op.." ")..")"
| end))
| end
|end
| local bits = {};
| for i=1,n do;
| table.insert(bits, (nomsu:tree_to_lua(vars[tostring(i)])));
| end;
| return "("..table.concat(bits, " "..op.." ")..")";
| end));
| end;
|end;
# Chained compairsions (e.g. x < y <= z) are defined up to 3 operands
lua block ".."
|local max_operands = 3
|for _,chainers in ipairs({{"<","<="},{">",">="}}) do
|local max_operands = 3;
|for _,chainers in ipairs({{"<","<="},{">",">="}}) do;
| local function recurse(chainers, chain)
# The 1-op versions are already more efficiently defined, and a 0-op version doesnt make sense
| if #chain >= 2 then
| local spec = "%1"
| for i,op in ipairs(chain) do
| spec = spec .. " "..op.." %"..tostring(i+1)
| end
| if #chain >= 2 then;
| local spec = "%1";
| for i,op in ipairs(chain) do;
| spec = spec .. " "..op.." %"..tostring(i+1);
| end;
# Chained comparisons need to be functions to avoid re-evaluating their arguments :\
| nomsu:def(spec, function(nomsu, vars)
| for i,op in ipairs(chain) do
| local a, b, result = vars[i], vars[i+1]
| if op == "<" then result = a < b
| elseif op == "<=" then result = a <= b
| elseif op == ">" then result = a > b
| elseif op == ">=" then result = a >= b end
| for i,op in ipairs(chain) do;
| local a, b, result = vars[i], vars[i+1];
| if op == "<" then; result = a < b;
| elseif op == "<=" then; result = a <= b;
| elseif op == ">" then; result = a > b;
| elseif op == ">=" then; result = a >= b; end;
# Short circuit
| if not result then return false end
| end
| end)
| end
| if #chain + 1 >= max_operands then return end
| for _,c in ipairs(chainers) do
| table.insert(chain, c)
| recurse(chainers, chain)
| table.remove(chain)
| end
| end
| recurse(chainers, {})
|end
| if not result then; return false; end;
| end;
| end);
| end;
| if #chain + 1 >= max_operands then; return; end;
| for _,c in ipairs(chainers) do;
| table.insert(chain, c);
| recurse(chainers, chain);
| table.remove(chain);
| end;
| end;
| recurse(chainers, {});
|end;
# Unary operators
compile (- %) to: "-(\(% as lua))"
compile (not %) to: "not (\(% as lua))"
compile [- %] to: "-(\(% as lua))"
compile [not %] to: "not (\(% as lua))"

View File

@ -4,44 +4,44 @@ require "lib/operators.nom"
require "lib/collections.nom"
# Permission functions
rule (standardize rules %rules) =:
rule [standardize rules %rules] =:
if (lua expr "type(vars.rules) == 'string'"): %rules = [%rules]
(nomsu "get_stub" [%]) for all %rules
rule (restrict %rules to within %elite-rules) =:
%rules =: standardize rules %rules
%elite-rules =: standardize rules %elite-rules
rule [restrict %rules to within %elite-rules] =:
%rules = (standardize rules %rules)
%elite-rules = (standardize rules %elite-rules)
say "Restricting \(%rules) to within \(%elite-rules)"
for all (flatten [%elite-rules, %rules]):
assert ((nomsu's "defs") has key %) "Undefined function: \(%)"
for %rule in %rules:
assert (nomsu "check_permission" [%]) ".."
|You do not have permission to restrict permissions for function: \(%)
((nomsu) ->* ["defs",%rule,"whiteset"]) =:
((nomsu) ->* ["defs",%rule,"whiteset"]) = (..)
dict: [%, yes] for all %elite-rules
rule (allow %elite-rules to use %rules) =:
%rules =: standardize rules %rules
%elite-rules =: standardize rules %elite-rules
rule [allow %elite-rules to use %rules] =:
%rules = (standardize rules %rules)
%elite-rules = (standardize rules %elite-rules)
say "Allowing \(%elite-rules) to use \(%rules)"
for all (flatten [%elite-rules, %rules]):
assert ((nomsu's "defs") has key %) "Undefined function: \(%)"
for %rule in %rules:
assert (nomsu "check_permission" [%rule]) ".."
|You do not have permission to grant permissions for function: \(%rule)
%whiteset =: (nomsu) ->* ["defs",%rule,"whiteset"]
%whiteset = ((nomsu) ->* ["defs",%rule,"whiteset"])
if (not %whiteset): go to next %rule
for all %elite-rules: %whiteset -> % = (yes)
rule (forbid %pleb-rules to use %rules) =:
%rules =: standardize rules %rules
%pleb-rules =: standardize rules %pleb-rules
rule [forbid %pleb-rules to use %rules] =:
%rules = (standardize rules %rules)
%pleb-rules = (standardize rules %pleb-rules)
say "Forbidding \(%pleb-rules) to use \(%rules)"
for all (flatten [%pleb-rules, %used]):
assert ((nomsu's "defs") has key %) "Undefined function: \(%)"
for all %rules:
assert (nomsu "check_permission" [%]) ".."
|You do not have permission to grant permissions for function: \(%)
%whiteset =: (nomsu) ->* ["defs",%,"whiteset"]
%whiteset = ((nomsu) ->* ["defs",%,"whiteset"])
assert %whiteset ".."
|Cannot individually restrict permissions for \(%) because it is currently
|available to everyone. Perhaps you meant to use "restrict % to within %" instead?

View File

@ -4,32 +4,32 @@ require "lib/secrets.nom"
# Plurals
with secrets:
lua block ".."
|local endings = setmetatable({x="es",c="es",s="es"}, {__index=function() return "s" end})
|local endings = setmetatable({x="es",c="es",s="es"}, {__index=function() return "s"; end});
|secrets.plurals = setmetatable({}, {__index=function(self,key)
| return key..endings[key:sub(-1)]
|end})
| return key..endings[key:sub(-1)];
|end});
|secrets.singulars = setmetatable({}, {__index=function(self,key)
| if key:sub(-2) == "es" and rawget(endings, key:sub(-3,-3)) then return key:sub(1,-3) end
| if key:sub(-1) == "s" then return key:sub(1,-2) end
| return key
|end})
| if key:sub(-2) == "es" and rawget(endings, key:sub(-3,-3)) then; return key:sub(1,-3); end;
| if key:sub(-1) == "s" then; return key:sub(1,-2); end;
| return key;
|end});
|secrets.canonicals = setmetatable({}, {__index=function(self,key)
| if key:sub(-1) == "s" then return secrets.singulars[key] end
| return key
|end})
| if key:sub(-1) == "s" then; return secrets.singulars[key]; end;
| return key;
|end});
rule (the plural of %singular is %plural) =:
(secret %plurals)->%singular =: %plural
(secret %singulars)->%plural =: %singular
(secret %canonicals)->%plural =: %singular
rule [the plural of %singular is %plural] =:
(secret %plurals)->%singular = %plural
(secret %singulars)->%plural = %singular
(secret %canonicals)->%plural = %singular
rule (singular %plural) =:
rule [singular %plural] =:
%plural in (secret %singulars)
rule (plural %singular) =:
rule [plural %singular] =:
%singular in (secret %plurals)
rule (canonicalize %item-name) =:
rule [canonicalize %item-name] =:
%item-name in (secret %canonicals)
rule (rules that change plurals) =: ["the plural of % is %"]
rule [rules that change plurals] =: ["the plural of % is %"]

View File

@ -1,20 +1,22 @@
require "lib/core.nom"
compile (with secrets %block) to block: ".."
|local secrets = {}
|\(%block as lua statements)
compile [with secrets %block] to code: ".."
|do;
| local secrets = {}
| \(%block as lua statements)
|end;
# Access the lua variable that should be within scope
compile (secrets) to: "secrets"
compile [secrets] to: "secrets"
compile (secret %key; secret value of %key; secret value for %key) to:
compile [secret %key; secret value of %key; secret value for %key] to:
assert ((%key's "type") == "Var") ".."
|Wrong type, expected Var, but got: \(%key's "type")
"secrets[\(repr (%key's "value"))]"
compile (secret %key = %new_value) to code:
compile [secret %key = %new_value] to code:
assert ((%key's "type") == "Var") ".."
|Wrong type, expected Var, but got: \(%key's "type")
"secrets[\(repr (%key's "value"))] = \(%new_value as lua)"
rule (rules about secrecy) =: ["with secrets %"]
rule [rules about secrecy] =: ["with secrets %"]

View File

@ -1,83 +1,13 @@
require "lib/metaprogramming.nom"
# For unit testing
macro block [test %code yields %expected] =:
_yield_tree: (tree, indent_level=0)=>
ind = (s) -> INDENT\rep(indent_level)..s
switch tree.type
when "File"
coroutine.yield(ind"File:")
@_yield_tree(tree.value.body, indent_level+1)
when "Errors" then coroutine.yield(ind"Error:\n#{tree.value}")
when "Block"
for chunk in *tree.value
@_yield_tree(chunk, indent_level)
when "Thunk"
coroutine.yield(ind"Thunk:")
@_yield_tree(tree.value, indent_level+1)
when "Statement" then @_yield_tree(tree.value, indent_level)
when "FunctionCall"
alias = @get_alias tree
args = [a for a in *tree.value when a.type != "Word"]
if #args == 0
coroutine.yield(ind"Call [#{alias}]!")
else
coroutine.yield(ind"Call [#{alias}]:")
for a in *args
@_yield_tree(a, indent_level+1)
when "String" then coroutine.yield(ind(repr(tree.value)))
when "Longstring" then coroutine.yield(ind(repr(tree.value)))
when "Number" then coroutine.yield(ind(tree.value))
when "Var" then coroutine.yield ind"Var[#{repr(tree.value)}]"
when "List"
if #tree.value == 0
coroutine.yield(ind("<Empty List>"))
else
coroutine.yield(ind"List:")
for item in *tree.value
@_yield_tree(item, indent_level+1)
else error("Unknown/unimplemented thingy: #{tree.type}")
print_tree:(tree)=>
for line in coroutine.wrap(-> @_yield_tree(tree))
@writeln(line)
stringify_tree:(tree)=>
result = {}
for line in coroutine.wrap(-> @_yield_tree(tree))
insert(result, line)
return concat result, "\n"
test: (src, filename, expected)=>
i = 1
while i != nil
start,stop = src\find("\n\n", i)
test = src\sub(i,start)
i = stop
start,stop = test\find"==="
if not start or not stop then
@error("WHERE'S THE ===? in:\n#{test}")
test_src, expected = test\sub(1,start-1), test\sub(stop+1,-1)
expected = expected\match'[\n]*(.*[^\n])'
tree = @parse(test_src, filename)
got = @stringify_tree(tree.value.body)
if got != expected
@error"TEST FAILED!\nSource:\n#{test_src}\nExpected:\n#{expected}\n\nGot:\n#{got}"
%generated =: repr (nomsu "stringify_tree" [%code's "value"])
%expected =: %expected as lua
rule [test tree %generated == %expected] =:
if (%generated != %expected):
say "Test failed!"
say "Expected:"
say %expected
say "But got:"
say %generated
error!
return ""
error ".."
|Test Failed!
|Expected:
|\(%expected)
|But got:
|\(%generated)
parse [test %code yields %expected] as:
test tree (nomsu "tree_to_str" [\%code]) == %expected

View File

@ -1,83 +1,83 @@
require "lib/metaprogramming.nom"
# Error functions
rule (error!; panic!; fail!; abort!) =:
rule [error!, panic!, fail!, abort!] =:
nomsu "error" []
rule (error %msg) =:
rule [error %msg] =:
nomsu "error"[%msg]
compile (assert %condition %msg) to code: ".."
compile [assert %condition %msg] to code: ".."
|if not (\(%condition as lua)) then
| nomsu:error(\(%msg as lua))
|end
parse (assert %condition) as: assert %condition (nil)
parse [assert %condition] as: assert %condition (nil)
# String functions
rule (join %strs with glue %glue) =:
rule [join %strs with glue %glue] =:
lua block ".."
|local str_bits = {}
|for i,bit in ipairs(vars.strs) do str_bits[i] = nomsu.utils.repr_if_not_string(bit) end
|return table.concat(str_bits, vars.glue)
parse (join %strs) as: join %strs with glue ""
parse [join %strs] as: join %strs with glue ""
compile (capitalize %str; %str capitalized) to:
compile [capitalize %str, %str capitalized] to:
"(\(%str as lua)):gsub('%l', string.upper, 1)"
compile (say %str) to: ".."
compile [say %str] to: ".."
|nomsu:writeln(\(%str as lua))
# Number ranges
compile (%start to %stop by %step; %start to %stop via %step) to: ".."
compile [%start to %stop by %step, %start to %stop via %step] to: ".."
|nomsu.utils.range(\(%start as lua), \(%stop as lua), \(%step as lua))
parse (%start to %stop) as: %start to %stop by 1
parse [%start to %stop] as: %start to %stop by 1
# Random functions
compile (random number; random; rand) to: "math.random()"
compile (random int %n; random integer %n; randint %n) to: "math.random(\(%n as lua))"
compile (random from %low to %high; random number from %low to %high; rand %low %high) to:
compile [random number, random, rand] to: "math.random()"
compile [random int %n, random integer %n, randint %n] to: "math.random(\(%n as lua))"
compile [random from %low to %high, random number from %low to %high, rand %low %high] to:
"math.random(\(%low as lua), \(%high as lua))"
rule (random choice from %elements; random choice %elements; random %elements) =:
rule [random choice from %elements, random choice %elements, random %elements] =:
lua expr "\(%elements)[math.random(#\(%elements))]"
# Math functions
compile (abs %; absolute value of %; | % |) to: "math.abs(\(% as lua))"
compile (sqrt %; square root of %) to: "math.sqrt(\(% as lua))"
compile (sin %; sine %) to: "math.sin(\(% as lua))"
compile (cos %; cosine %) to: "math.cos(\(% as lua))"
compile (tan %; tangent %) to: "math.tan(\(% as lua))"
compile (asin %; arc sine %) to: "math.asin(\(% as lua))"
compile (acos %; arc cosine %) to: "math.acos(\(% as lua))"
compile (atan %; arc tangent %) to: "math.atan(\(% as lua))"
compile (atan2 %y %x) to: "math.atan2(\(%y as lua), \(%x as lua))"
compile (sinh %; hyperbolic sine %) to: "math.sinh(\(% as lua))"
compile (cosh %; hyperbolic cosine %) to: "math.cosh(\(% as lua))"
compile (tanh %; hyperbolic tangent %) to: "math.tanh(\(% as lua))"
compile (ceil %; ceiling %) to: "math.ceil(\(% as lua))"
compile (exp %; e^ %) to: "math.exp(\(% as lua))"
compile (log %; ln %; natural log %) to: "math.log(\(% as lua))"
compile (log % base %base) to: "math.log(\(% as lua), \(%base as lua))"
compile (floor %) to: "math.floor(\(% as lua))"
compile (round %; % rounded) to: "math.floor(\(% as lua) + .5)"
rule (%n rounded to the nearest %rounder) =:
compile [abs %, absolute value of %, | % |] to: "math.abs(\(% as lua))"
compile [sqrt %, square root of %] to: "math.sqrt(\(% as lua))"
compile [sin %, sine %] to: "math.sin(\(% as lua))"
compile [cos %, cosine %] to: "math.cos(\(% as lua))"
compile [tan %, tangent %] to: "math.tan(\(% as lua))"
compile [asin %, arc sine %] to: "math.asin(\(% as lua))"
compile [acos %, arc cosine %] to: "math.acos(\(% as lua))"
compile [atan %, arc tangent %] to: "math.atan(\(% as lua))"
compile [atan2 %y %x] to: "math.atan2(\(%y as lua), \(%x as lua))"
compile [sinh %, hyperbolic sine %] to: "math.sinh(\(% as lua))"
compile [cosh %, hyperbolic cosine %] to: "math.cosh(\(% as lua))"
compile [tanh %, hyperbolic tangent %] to: "math.tanh(\(% as lua))"
compile [ceil %, ceiling %] to: "math.ceil(\(% as lua))"
compile [exp %, e^ %] to: "math.exp(\(% as lua))"
compile [log %, ln %, natural log %] to: "math.log(\(% as lua))"
compile [log % base %base] to: "math.log(\(% as lua), \(%base as lua))"
compile [floor %] to: "math.floor(\(% as lua))"
compile [round %, % rounded] to: "math.floor(\(% as lua) + .5)"
rule [%n rounded to the nearest %rounder] =:
lua expr "(\(%rounder))*math.floor(\(%n)/\(%rounder) + .5)"
# Common utility functions
compile (sum of %items; sum %items) to: "nomsu.utils.sum(\(%items as lua))"
compile (product of %items; product %items) to: "nomsu.utils.product(\(%items as lua))"
compile (all of %items) to: "nomsu.utils.all(\(%items as lua))"
compile (any of %items) to: "nomsu.utils.any(\(%items as lua))"
rule (avg of %items; average of %items) =:
compile [sum of %items, sum %items] to: "nomsu.utils.sum(\(%items as lua))"
compile [product of %items, product %items] to: "nomsu.utils.product(\(%items as lua))"
compile [all of %items] to: "nomsu.utils.all(\(%items as lua))"
compile [any of %items] to: "nomsu.utils.any(\(%items as lua))"
rule [avg of %items, average of %items] =:
lua expr "(nomsu.utils.sum(\(%items))/#\(%items))"
compile (min of %items; smallest of %items; lowest of %items) to:
compile [min of %items, smallest of %items, lowest of %items] to:
"nomsu.utils.min(\(%items as lua))"
compile (max of %items; biggest of %items; largest of %items; highest of %items) to:
compile [max of %items, biggest of %items, largest of %items, highest of %items] to:
"nomsu.utils.max(\(%items as lua))"
compile (min of %items by %value_expr) to:
compile [min of %items by %value_expr] to:
".."
|nomsu.utils.min(\(%items as lua), function(item)
| local vars = setmetatable({['']=item}, {__index=vars})
| return \(%value_expr as lua)
|end)
compile (max of %items by %value_expr) to:
compile [max of %items by %value_expr] to:
".."
|nomsu.utils.max(\(%items as lua), function(item)
| local vars = setmetatable({['']=item}, {__index=vars})

View File

@ -50,7 +50,7 @@ check_nodent = (subject,end_pos,spaces)->
return end_pos
-- TYPES:
-- Number 1, "String", %Var, [List], (Block), \(Nomsu), FunctionCall, File
-- Number 1, "String", %Var, [List], (expression), {Thunk}, \Nomsu, FunctionCall, File
nomsu = [=[
file <- ({ {| shebang?
@ -69,20 +69,25 @@ nomsu = [=[
noeol_statement <- noeol_functioncall / noeol_expression
inline_statement <- inline_functioncall / inline_expression
inline_block <- ({ {| "(" inline_statements ")" |} }) -> Block
eol_block <- ({ {| ":" %ws? noeol_statements eol |} }) -> Block
indented_block <- ({ {| (":" / "(..)") indent
inline_thunk <- ({ {| "{" inline_statements "}" |} }) -> Thunk
eol_thunk <- ({ {| ":" %ws? noeol_statements eol |} }) -> Thunk
indented_thunk <- ({ {| (":" / "{..}") indent
statements (nodent statements)*
(dedent / (({.+} ("" -> "Error while parsing block")) => error))
|} }) -> Block
(dedent / (({.+} ("" -> "Error while parsing thunk")) => error))
|} }) -> Thunk
inline_nomsu <- ({ ("\" inline_expression) }) -> Nomsu
eol_nomsu <- ({ ("\" noeol_expression) }) -> Nomsu
indented_nomsu <- ({ ("\" expression) }) -> Nomsu
inline_expression <- number / variable / inline_string / inline_list / inline_block / inline_nomsu
noeol_expression <- indented_string / indented_block / indented_nomsu / indented_list / inline_expression
expression <- eol_block / eol_nomsu / noeol_expression
inline_expression <- number / variable / inline_string / inline_list / inline_nomsu
/ inline_thunk / ("(" inline_statement ")")
noeol_expression <- indented_string / indented_nomsu / indented_list / indented_thunk
/ ("(..)" indent
statement
(dedent / (({.+} ("" -> "Error while parsing indented expression"))))
) / inline_expression
expression <- eol_thunk / eol_nomsu / noeol_expression
-- Function calls need at least one word in them
inline_functioncall <- ({ {|
@ -107,7 +112,7 @@ nomsu = [=[
|} (dedent / (({.+} ("" -> "Error while parsing String")) => error))
}) -> String
indented_string_line <- "|" ({~ (("\\" -> "\") / (!string_interpolation [^%nl]))+ ~} / string_interpolation)*
string_interpolation <- "\" (inline_block / indented_block / dotdot)
string_interpolation <- "\" ((noeol_expression dotdot?) / dotdot)
number <- ({ (("-"? (([0-9]+ "." [0-9]+) / ("." [0-9]+) / ([0-9]+)))-> tonumber) }) -> Number
@ -189,16 +194,21 @@ class NomsuCompiler
@write(...)
@write("\n")
def: (invocation, thunk, src)=>
stub, arg_names = @get_stub invocation
assert stub, "NO STUB FOUND: #{repr invocation}"
if @debug then @writeln "#{colored.bright "DEFINING RULE:"} #{colored.underscore colored.magenta repr(stub)} #{colored.bright "WITH ARGS"} #{colored.dim repr(arg_names)}"
for i=1,#arg_names-1 do for j=i+1,#arg_names
if arg_names[i] == arg_names[j] then @error "Duplicate argument in function #{stub}: '#{arg_names[i]}'"
with @defs[stub] = {:thunk, :invocation, :arg_names, :src, is_macro:false} do nil
def: (signature, thunk, src, is_macro=false)=>
assert type(thunk) == 'function', "Bad thunk: #{repr thunk}"
canonical_args = nil
for {stub, arg_names} in *@get_stubs(signature)
assert stub, "NO STUB FOUND: #{repr signature}"
if @debug then @writeln "#{colored.bright "DEFINING RULE:"} #{colored.underscore colored.magenta repr(stub)} #{colored.bright "WITH ARGS"} #{colored.dim repr(arg_names)}"
for i=1,#arg_names-1 do for j=i+1,#arg_names
if arg_names[i] == arg_names[j] then @error "Duplicate argument in function #{stub}: '#{arg_names[i]}'"
if canonical_args
assert utils.equivalent(utils.set(arg_names), canonical_args), "Mismatched args"
else canonical_args = utils.set(arg_names)
@defs[stub] = {:thunk, :stub, :arg_names, :src, :is_macro}
defmacro: (invocation, thunk, src)=>
with @def(invocation, thunk, src) do .is_macro = true
defmacro: (signature, thunk, src)=>
@def(signature, thunk, src, true)
call: (stub,...)=>
def = @defs[stub]
@ -279,31 +289,37 @@ class NomsuCompiler
code_for_statement = ([[
return (function(nomsu, vars)
%s
return %s
end)]])\format(statements or "", expr or "")
return %s;
end);]])\format(statements or "", expr or "ret")
if @debug
@writeln "#{colored.bright "RUNNING LUA:"}\n#{colored.blue colored.bright(code_for_statement)}"
lua_thunk, err = load(code_for_statement)
if not lua_thunk
error("Failed to compile generated code:\n#{colored.bright colored.blue code_for_statement}\n\n#{err}\n\nProduced by statement:\n#{colored.bright colored.yellow statement.src}")
n = 1
fn = ->
n = n + 1
("\n%-3d|")\format(n)
code = "1 |"..code_for_statement\gsub("\n", fn)
error("Failed to compile generated code:\n#{colored.bright colored.blue code}\n\n#{err}\n\nProduced by statement:\n#{colored.bright colored.yellow statement.src}")
run_statement = lua_thunk!
ok,ret = pcall(run_statement, self, vars)
if expr then return_value = ret
if not ok
@writeln "#{colored.red "Error occurred in statement:"}\n#{colored.yellow statement.src}"
@error(repr return_value)
@writeln debug.traceback!
@error(ret)
insert buffer, "#{statements or ''}\n#{expr and "ret = #{expr}" or ''}"
lua_code = ([[
return function(nomsu, vars)
local ret
return (function(nomsu, vars)
local ret;
%s
return ret
end]])\format(concat(buffer, "\n"))
return ret;
end);]])\format(concat(buffer, "\n"))
return return_value, lua_code
tree_to_value: (tree, vars)=>
code = "return (function(nomsu, vars)\nreturn #{@tree_to_lua(tree)}\nend)"
code = "return (function(nomsu, vars)\nreturn #{@tree_to_lua(tree)};\nend);"
if @debug
@writeln "#{colored.bright "RUNNING LUA TO GET VALUE:"}\n#{colored.blue colored.bright(code)}"
lua_thunk, err = load(code)
@ -324,32 +340,18 @@ class NomsuCompiler
when "Nomsu"
return repr(tree.value), nil
when "Thunk" -- This is not created by the parser, it's just a helper
_,body = @tree_to_lua tree.value
return ([[
(function(nomsu, vars)
local ret
%s
return ret
end)]])\format(body), nil
when "Block"
if #tree.value == 0
return "nil",nil
if #tree.value == 1
expr,statement = @tree_to_lua tree.value[1]
if not statement
return expr, nil
thunk_lua = @tree_to_lua {type:"Thunk", value:{type:"Statements", value:tree.value, src:tree.src}, src:tree.src}
return ("%s(nomsu, vars)")\format(thunk_lua), nil
when "Statements"
when "Thunk"
lua_bits = {}
for arg in *tree.value
expr,statement = @tree_to_lua arg
if statement then insert lua_bits, statement
if expr then insert lua_bits, "ret = #{expr}"
return nil, concat(lua_bits, "\n")
if expr then insert lua_bits, "ret = #{expr};"
return ([[
(function(nomsu, vars)
local ret;
%s
return ret;
end)]])\format(concat(lua_bits, "\n"))
when "FunctionCall"
stub = @get_stub(tree)
@ -418,7 +420,7 @@ class NomsuCompiler
if type(tree) != 'table' or not tree.type
return
switch tree.type
when "List", "File", "Block", "FunctionCall", "String"
when "List", "File", "Thunk", "FunctionCall", "String"
for v in *tree.value
@walk_tree(v, depth+1)
else @walk_tree(tree.value, depth+1)
@ -465,7 +467,7 @@ class NomsuCompiler
when "Var"
if vars[tree.value] ~= nil
tree = vars[tree.value]
when "File", "Nomsu", "Thunk", "Block", "List", "FunctionCall", "String"
when "File", "Nomsu", "Thunk", "List", "FunctionCall", "String"
new_value = @replaced_vars tree.value, vars
if new_value != tree.value
tree = {k,v for k,v in pairs(tree)}
@ -506,9 +508,16 @@ class NomsuCompiler
arg_names = nil
insert args, token
return concat(stub," "), arg_names, args
when "Block"
@writeln debug.traceback!
@error "Please pass in a single line from a block, not the whole thing:\n#{@tree_to_str x}"
else @error "Unsupported get stub type: #{x.type}"
get_stubs: (x)=>
if type(x) != 'table' then return {{@get_stub(x)}}
switch x.type
when nil
return [{@get_stub(i)} for i in *x]
when "List"
return [{@get_stub(i)} for i in *x.value]
return {{@get_stub(x)}}
var_to_lua_identifier: (var)=>
-- Converts arbitrary nomsu vars to valid lua identifiers by replacing illegal
@ -584,12 +593,12 @@ if arg and arg[1]
io.output()
else io.open(arg[2], 'w')
output\write ([[
local NomsuCompiler = require('nomsu')
local c = NomsuCompiler()
local run = function(nomsu, vars)
local NomsuCompiler = require('nomsu');
local c = NomsuCompiler();
local run = (function(nomsu, vars)
%s
end
return run(c, {})
end);
return run(c, {});
]])\format(code)
--ProFi\stop()
--ProFi\writeReport( 'MyProfilingReport.txt' )