Kinda mostly working, except for closure vars like in lib/secrets.nom.
This commit is contained in:
parent
10d61df78b
commit
ac25e20b9f
@ -5,149 +5,122 @@ require "lib/operators.nom"
|
||||
# List/dict functions:
|
||||
|
||||
# Indexing
|
||||
macro [..]
|
||||
%list's %index, %index st in %list, %index nd in %list, %index rd in %list
|
||||
%index th in %list, %index in %list, %list -> %index
|
||||
..=:
|
||||
".."|\%list as lua\[\%index as lua\]
|
||||
".."|\%list as lua\[\%index as lua\]
|
||||
macro [..]
|
||||
%index st to last in %list, %index nd to last in %list, %index rd to last 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
|
||||
%index th to last in %list
|
||||
..=:
|
||||
".."|nomsu.utils.nth_to_last(\%list as lua\, \%index as lua\)
|
||||
..to: "nomsu.utils.nth_to_last(\(%list as lua), \(%index as lua))"
|
||||
|
||||
macro [first in %list, first %list] =:
|
||||
".."|\%list as lua\[1]
|
||||
macro [last in %list, last %list] =:
|
||||
".."|nomsu.utils.nth_to_last(\%list as lua\, 1)
|
||||
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): ...
|
||||
macro block [for %key -> %value in %dict %body] =:
|
||||
assert ((%key's "type") == "Var") ".."
|
||||
|For loop has the wrong type for the key variable. Expected Var, but got: \%key's "type"\
|
||||
assert ((%value's "type") == "Var") ".."
|
||||
|For loop has the wrong type for the value variable. Expected Var, but got: \%value's "type"\
|
||||
".."
|
||||
|local vars = setmetatable({}, {__index=vars})
|
||||
|for k, v in pairs(\%dict as lua\) do
|
||||
| \%key as lua\, \%value as lua\ = k, v
|
||||
| \%body as lua\
|
||||
|end
|
||||
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
|
||||
|
||||
# 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)
|
||||
macro [%list has key %index, %list has index %index] =: ".."
|
||||
|(\%list as lua\[\%index as lua\] ~= nil)
|
||||
|
||||
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)
|
||||
|
||||
macro [..]
|
||||
%list doesn't have key %index, %list does not have key %index
|
||||
%list doesn't have index %index, %list does not have index %index
|
||||
..=: ".."|(\%list as lua\[\%index as lua\] ~= nil)
|
||||
compile (%list has key %index; %list has index %index) to: ".."
|
||||
|(\(%list as lua)[\(%index as lua)] ~= nil)
|
||||
|
||||
macro [length of %list, size of %list, size %list, number of %list, len %list] =:
|
||||
".."|nomsu.utils.size(\%list as lua\)
|
||||
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:
|
||||
"nomsu.utils.size(\(%list as lua))"
|
||||
|
||||
# Chained lookup
|
||||
macro [%list ->* %indices] =:
|
||||
compile (%list ->* %indices) to:
|
||||
assert ((%indices's "type") == "List") ".."
|
||||
|Expected List for chained lookup, not \%indices's "type"\
|
||||
%ret =: ".."|(\%list as lua\)
|
||||
|Expected List for chained lookup, not \(%indices's "type")
|
||||
%ret =: "\(%list as lua)"
|
||||
for %index in (%indices's "value"):
|
||||
%ret join=: ".."|[\%index as lua\]
|
||||
".."|(\%ret\)
|
||||
%ret join=: "[\(%index as lua)]"
|
||||
"\(%ret)"
|
||||
|
||||
# Assignment
|
||||
macro statement [..]
|
||||
%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
|
||||
..=:
|
||||
assert ((%new_value's "type") == "Thunk") ".."
|
||||
..to code:
|
||||
assert ((%new_value's "type") == "Block") ".."
|
||||
|Dict assignment operation has the wrong type for the right hand side.
|
||||
|Expected Thunk, but got \%new_value's "type"\.
|
||||
|Expected Block, but got \(%new_value's "type").
|
||||
|Maybe you used "=" instead of "=:"?
|
||||
assert ((size of (%new_value ->*["value","value"])) == 1) ".."
|
||||
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","value",1]
|
||||
if ((%new_value's "type") == "Statement"): %new_value =: %new_value's "value"
|
||||
".."|\%list as lua\[\%index as lua\] = \%new_value as lua\
|
||||
|Expected 1 value, but got \(repr %new_value)
|
||||
%new_value =: %new_value ->* ["value",1]
|
||||
"\(%list as lua)[\(%index as lua)] = \(%new_value as lua)"
|
||||
|
||||
macro [append %item to %list, add %item to %list] =:
|
||||
".."|table.insert(\%list as lua\, \%item as lua\)
|
||||
compile (append %item to %list; add %item to %list) to:
|
||||
"table.insert(\(%list as lua), \(%item as lua))"
|
||||
|
||||
rule [flatten %lists] =:
|
||||
rule (flatten %lists) =:
|
||||
%flat =: []
|
||||
for %list in %lists:
|
||||
for %item in %list:
|
||||
add %item to %flat
|
||||
%flat
|
||||
|
||||
rule [dict %items] =:
|
||||
rule (dict %items) =:
|
||||
%dict =: []
|
||||
for %pair in %items:
|
||||
%dict -> (first in %pair) =: last in %pair
|
||||
%dict
|
||||
|
||||
rule [entries in %dict] =:
|
||||
lua block ".."
|
||||
|local items = {}
|
||||
|for k,v in pairs(vars.dict) do
|
||||
| table.insert(items, {key=k,value=v})
|
||||
|end
|
||||
|return items
|
||||
rule (entries in %dict) =:
|
||||
%entries =: []
|
||||
for %k -> %v in %dict:
|
||||
add (dict [["key",%k],["value",%v]]) to %entries
|
||||
%entries
|
||||
|
||||
rule [keys in %dict] =:
|
||||
lua block ".."
|
||||
|local items = {}
|
||||
|for k,v in pairs(vars.dict) do
|
||||
| table.insert(items, k)
|
||||
|end
|
||||
|return items
|
||||
rule (keys in %dict) =:
|
||||
%keys =: []
|
||||
for %k -> %v in %dict: add %k to %keys
|
||||
%keys
|
||||
|
||||
rule [values in %dict] =:
|
||||
lua block ".."
|
||||
|local items = {}
|
||||
|for k,v in pairs(vars.dict) do
|
||||
| table.insert(items, v)
|
||||
|end
|
||||
|return items
|
||||
rule (values in %dict) =:
|
||||
%values =: []
|
||||
for %k -> %v in %dict: add %v to %values
|
||||
%values
|
||||
|
||||
# List Comprehension
|
||||
macro [%expression for %var in %iterable] =:
|
||||
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"\
|
||||
|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
|
||||
|end)(game, setmetatable({}, {__index=vars}))
|
||||
macro [%expression for all %iterable] =:
|
||||
".."|(function(game, vars)
|
||||
| local comprehension = {}
|
||||
| for i,value in ipairs(\%iterable as lua\) do
|
||||
| vars[''] = value
|
||||
| comprehension[i] = \%expression as lua\
|
||||
| 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
|
||||
|
||||
# TODO: maybe make a generator/coroutine?
|
||||
|
||||
|
@ -3,73 +3,60 @@ require "lib/operators.nom"
|
||||
require "lib/utils.nom"
|
||||
|
||||
# Conditionals
|
||||
parse (if %condition %if_body) as lua code ".."
|
||||
|if \(%condition) then
|
||||
| \(lua expr "vars.if_body.value")
|
||||
compile (if %condition %if_body) to code: ".."
|
||||
|if \(%condition as lua) then
|
||||
| \(%if_body as lua statements)
|
||||
|end
|
||||
|
||||
parse (if %condition %if_body else %else_body) as lua code ".."
|
||||
|if \(%condition) then
|
||||
| \(lua expr "vars.if_body.value")
|
||||
compile (if %condition %if_body else %else_body) to code: ".."
|
||||
|if \(%condition as lua) then
|
||||
| \(%if_body as lua statements)
|
||||
|else
|
||||
| \(lua expr "vars.else_body.value")
|
||||
| \(%else_body as lua statements)
|
||||
|end
|
||||
|
||||
# Return
|
||||
parse (return) as lua code "do return end"
|
||||
parse (return %return-value) as lua code "do return \(%return-value)"
|
||||
parse (do %action) as lua expr ".."
|
||||
|(\(%action))(nomsu, setmetatable({}, {__index=vars}))
|
||||
|
||||
compile (return) to code: "do return end"
|
||||
compile (return %return-value) to code: "do return \(%return-value as lua) end"
|
||||
|
||||
# GOTOs
|
||||
parse (-> %label) as lua code ".."
|
||||
compile (-> %label) to code: ".."
|
||||
|::label_\(nomsu "var_to_lua_identifier" [%label])::
|
||||
parse (go to %label) as lua code ".."
|
||||
compile (go to %label) to code: ".."
|
||||
|goto label_\(nomsu "var_to_lua_identifier" [%label])
|
||||
|
||||
# Loop control flow
|
||||
parse (stop; stop loop; break) as lua code "break"
|
||||
parse (stop for; stop for-loop; break for) as lua code "goto break_for"
|
||||
parse (stop repeat; stop repeat-loop; break repeat) as lua code "goto break_repeat"
|
||||
parse (stop %var; break %var) as lua code ".."
|
||||
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])
|
||||
|
||||
parse (continue; continue loop) as lua code "continue"
|
||||
parse (continue for; continue for-loop) as lua code "goto continue_for"
|
||||
parse (continue repeat; continue repeat-loop) as lua code "goto continue_repeat"
|
||||
parse (continue %var; go to next %var; on to the next %var) as lua code ".."
|
||||
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
|
||||
parse (repeat %body) as lua block ".."
|
||||
|while true do
|
||||
| \(lua expr "vars.body.value")
|
||||
| ::continue_repeat::
|
||||
|end
|
||||
|::break_repeat::
|
||||
parse (repeat while %condition %body) as lua block ".."
|
||||
compile (repeat while %condition %body) to block: ".."
|
||||
|while \(%condition) do
|
||||
| \(lua expr "vars.body.value")
|
||||
| ::continue_repeat::
|
||||
|end
|
||||
|::break_repeat::
|
||||
parse (repeat until %condition %body) as lua block ".."
|
||||
|while not (\(%condition)) do
|
||||
| \(lua expr "vars.body.value")
|
||||
| \(%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
|
||||
|
||||
# Numeric range for loops
|
||||
parse:
|
||||
compile:
|
||||
for %var from %start to %stop by %step %body
|
||||
for %var from %start to %stop via %step %body
|
||||
..as lua block ".."
|
||||
|for i=\(%start),\(%stop),\(%step) do
|
||||
..to block: ".."
|
||||
|for i=\(%start as lua),\(%stop as lua),\(%step as lua) do
|
||||
# This trashes the loop variables, just like in Python.
|
||||
| \(%var) = i
|
||||
| \(lua expr "vars.body.value")
|
||||
| \(%var as lua) = i
|
||||
| \(%body as lua statements)
|
||||
| ::continue_for::
|
||||
| ::continue_\(nomsu "var_to_lua_identifier" [%var])::
|
||||
|end
|
||||
@ -82,38 +69,39 @@ parse:
|
||||
..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 %var in %iterable %body) as lua block ".."
|
||||
|for i,value in ipairs(\(%iterable)) do
|
||||
# This trashes the loop variables, just like in Python.
|
||||
| \(%var) = value
|
||||
| \(lua expr "vars.body.value")
|
||||
| ::continue_for::
|
||||
| ::continue_\(nomsu "var_to_lua_identifier" [%var])::
|
||||
|end
|
||||
|::break_for::
|
||||
|::break_\(nomsu "var_to_lua_identifier" [%var])::
|
||||
compile (for %var in %iterable %body) to block:
|
||||
".."
|
||||
|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
|
||||
|
||||
|
||||
# Switch statement/multi-branch if
|
||||
parse (when %body) as lua block:
|
||||
compile (when %body) to block:
|
||||
%result =: ""
|
||||
%fallthroughs =: []
|
||||
for %statement in (lua expr "vars.body.value.value"):
|
||||
%func-call =: lua expr "vars.statement.value"
|
||||
assert ((lua expr "vars['func-call'].type") == "FunctionCall") ".."
|
||||
for %statement in (%body's "value"):
|
||||
%func-call =: %statement's "value"
|
||||
assert ((%func-call's "type") == "FunctionCall") ".."
|
||||
|Invalid format for 'when' statement. Only '*' blocks are allowed.
|
||||
%tokens =: lua expr "vars['func-call'].value"
|
||||
|
||||
%star =: lua expr "vars.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 =: lua expr "vars.tokens[2]"
|
||||
%condition =: %tokens -> 2
|
||||
assert %condition ".."
|
||||
|Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else"
|
||||
|
||||
%thunk =: lua expr "vars.tokens[3]"
|
||||
if (%thunk == (nil)):
|
||||
%action =: %tokens -> 3
|
||||
if (%action == (nil)):
|
||||
lua block "table.insert(vars.fallthroughs, vars.condition)"
|
||||
go to next %statement
|
||||
|
||||
@ -123,13 +111,14 @@ parse (when %body) as lua block:
|
||||
|do
|
||||
..else:
|
||||
%condition =: %condition as lua
|
||||
for all %fallthroughs: %condition join= " or \(%)"
|
||||
for all %fallthroughs:
|
||||
%condition join=: " or \(% as lua)"
|
||||
%result join=: ".."
|
||||
|
|
||||
|if \(%condition) then
|
||||
%result join=: ".."
|
||||
|
|
||||
| \(lua expr "vars.thunk.value")
|
||||
| \(%action as lua statements)
|
||||
| goto finished_when
|
||||
|end
|
||||
|
||||
@ -139,25 +128,24 @@ parse (when %body) as lua block:
|
||||
%result
|
||||
|
||||
# Switch statement
|
||||
parse (when %branch-value == ? %body) as lua block:
|
||||
compile (when %branch-value == ? %body) to block:
|
||||
%result =: "local branch_value = \(%branch-value)"
|
||||
%fallthroughs =: []
|
||||
for %statement in (lua expr "vars.body.value.value"):
|
||||
%func-call =: lua expr "vars.statement.value"
|
||||
assert ((lua expr "vars['func-call'].type") == "FunctionCall") ".."
|
||||
for %statement in (%body's "value"):
|
||||
%func-call =: %statement's "value"
|
||||
assert ((%func-call's "type") == "FunctionCall") ".."
|
||||
|Invalid format for 'when' statement. Only '*' blocks are allowed.
|
||||
%tokens =: lua expr "vars['func-call'].value"
|
||||
|
||||
%star =: lua expr "vars.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 =: lua expr "vars.tokens[2]"
|
||||
%condition =: %tokens -> 2
|
||||
assert %condition ".."
|
||||
|Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else"
|
||||
|
||||
%thunk =: lua expr "vars.tokens[3]"
|
||||
if (%thunk == (nil)):
|
||||
%action =: %tokens -> 3
|
||||
if (%action == (nil)):
|
||||
lua block "table.insert(vars.fallthroughs, vars.condition)"
|
||||
go to next %statement
|
||||
|
||||
@ -166,14 +154,15 @@ parse (when %branch-value == ? %body) as lua block:
|
||||
|
|
||||
|do
|
||||
..else:
|
||||
%condition =: "branch_value == (\(%condition))"
|
||||
for all %fallthroughs: %condition join= " or (branch_value == \(%))"
|
||||
%condition =: "branch_value == (\(%condition as lua))"
|
||||
for all %fallthroughs:
|
||||
%condition join=: " or (branch_value == \(% as lua))"
|
||||
%result join=: ".."
|
||||
|
|
||||
|if \%condition\ then
|
||||
|if \(%condition) then
|
||||
%result join=: ".."
|
||||
|
|
||||
| \((lua expr "vars.thunk.value"))
|
||||
| \(%action as lua statements)
|
||||
| goto finished_when
|
||||
|end
|
||||
|
||||
|
@ -2,80 +2,86 @@
|
||||
This File contains rules for making rules and macros and some helper functions to make
|
||||
that easier.
|
||||
|
||||
# Nil
|
||||
# Rule to make rules:
|
||||
lua code ".."
|
||||
|nomsu:defmacro("nil", function(nomsu, vars) return "nil", nil end)
|
||||
|
||||
# Macros:
|
||||
lua code ".."
|
||||
|local function parse_as(nomsu, vars)
|
||||
| if vars.shorthand.type ~= "Block" then
|
||||
| nomsu:error("Expected shorthand to be Block, but got "..vars.shorthand.type)
|
||||
|nomsu:def("escaped rule %rule_def = %body", function(nomsu, vars)
|
||||
| local aliases = nomsu:typecheck(vars, "rule_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 canonical = aliases[1]
|
||||
| nomsu:def(canonical, thunk, body.src)
|
||||
| local function rewriter(nomsu, vars)
|
||||
| return nomsu:tree_to_lua(nomsu:replaced_vars(canonical, vars))
|
||||
| end
|
||||
| if vars.longhand.type ~= "Block" then
|
||||
| nomsu:error("Expected longhand to be Block, but got "..vars.longhand.type)
|
||||
| for i=2,#aliases do
|
||||
| nomsu:defmacro(aliases[i], rewriter, body.src)
|
||||
| end
|
||||
| local template = vars.longhand
|
||||
| local function parsing_as(nomsu, vars)
|
||||
| local expanded = nomsu:replaced_vars(template, vars)
|
||||
| local expr,statement = nomsu:tree_to_lua(expanded)
|
||||
| return expr, statement
|
||||
| end
|
||||
| for _,call in ipairs(vars.shorthand.value) do
|
||||
| nomsu:defmacro(call, parsing_as)
|
||||
| end
|
||||
|end
|
||||
|nomsu:def("parse nomsu %shorthand as nomsu %longhand", parse_as)
|
||||
parse nomsu \(parse %shorthand as %longhand) as nomsu \(parse nomsu \%shorthand as nomsu \%longhand)
|
||||
|
||||
lua code ".."
|
||||
|nomsu:defmacro("lua expr %code", function(nomsu, vars)
|
||||
| return nomsu:tree_to_value(vars.code, vars), nil
|
||||
|end)
|
||||
|
||||
# Rule to make nomsu macros:
|
||||
escaped rule \(escaped parse %shorthand as %longhand) = \:
|
||||
lua code ".."
|
||||
|local aliases = nomsu:typecheck(vars, "shorthand", "Block").value
|
||||
|local template = nomsu:typecheck(vars, "longhand", "Block")
|
||||
|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
|
||||
parse (rule %rule_def = %body) as: escaped rule \%rule_def = \%body
|
||||
|
||||
# Rule that lets you make new rules
|
||||
lua code ".."
|
||||
|nomsu:defmacro("rule %rule_def = %body", function(nomsu, vars)
|
||||
| if vars.rule_def.type ~= "Block" then
|
||||
| nomsu:error("Wrong type for rule definition, expected Block, but got "..vars.rule_def.type)
|
||||
| end
|
||||
| local thunk = nomsu:tree_to_lua({type="Thunk", value=vars.body, src=vars.body.src})
|
||||
| local fn_name = "fn_"..nomsu:var_to_lua_identifier(nomsu:get_stub(vars.rule_def.value[1]))
|
||||
| local lua = ([[
|
||||
# Rule to make lua macros:
|
||||
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) =:
|
||||
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
|
||||
| local %s = %s
|
||||
| for _,alias in ipairs(%s) do
|
||||
| nomsu:def(alias, %s)
|
||||
| end
|
||||
|end]]):format(fn_name, thunk, nomsu:repr(vars.rule_def.value), fn_name)
|
||||
| return nil, lua
|
||||
|end)
|
||||
parse (rule %rule_name) as: lua expr "nomsu.defs[(nomsu:get_stub(\(%rule_name).value[1]))]"
|
||||
|
||||
|
||||
# Macro helper functions
|
||||
rule (%tree as value) =:
|
||||
lua expr "nomsu:tree_to_value(vars.tree, vars)"
|
||||
| \(%body)
|
||||
|end
|
||||
|
||||
rule (%tree as lua) =:
|
||||
lua expr "nomsu:tree_to_lua(vars.tree)"
|
||||
lua expr "nomsu:tree_to_lua(\(%tree))"
|
||||
rule (%tree as value) =:
|
||||
lua expr "nomsu:tree_to_value(\(%tree), vars)"
|
||||
compile (repr %obj) to:
|
||||
"nomsu:repr(\(%obj as lua))"
|
||||
|
||||
parse (lua block %block) as: lua code ".."
|
||||
|do
|
||||
| \(%block)
|
||||
|end
|
||||
rule (%tree as lua statement) =:
|
||||
lua block ".."
|
||||
|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
|
||||
|
||||
parse (lua block %block) as:
|
||||
lua code ".."
|
||||
|do
|
||||
| \(%block)
|
||||
|end
|
||||
|
||||
parse (nomsu) as: lua expr "nomsu"
|
||||
parse (nomsu's %key) as:
|
||||
lua expr "nomsu['\(%key)']"
|
||||
parse (nomsu %method %args) as:
|
||||
lua expr "nomsu['\(%method)'](nomsu, unpack(\(%args)))"
|
||||
parse (repr %) as: nomsu "repr" [%]
|
||||
repr 5
|
||||
|
||||
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) =:
|
||||
@ -89,7 +95,7 @@ rule (help %rule) =:
|
||||
|end
|
||||
|
||||
# Compiler tools
|
||||
rule (eval %code; run %code) =: nomsu "run" [%code]
|
||||
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")
|
||||
@ -100,23 +106,10 @@ rule (source code from tree %tree) =:
|
||||
|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: repr (nomsu "tree_to_str" [\%code])
|
||||
parse (parse tree %code) as: nomsu "tree_to_str" [\%code]
|
||||
|
||||
parse (parse %code as lua code %lua) as:
|
||||
parse nomsu \%code as nomsu:
|
||||
nomsu "replaced_vars" [\(lua code %lua), lua expr "vars"]
|
||||
parse (enable debugging) as: lua code "nomsu.debug = true"
|
||||
parse (disable debugging) as: lua code "nomsu.debug = false"
|
||||
|
||||
parse (parse %code as lua expr %lua) as:
|
||||
parse nomsu \%code as nomsu:
|
||||
nomsu "replaced_vars" [\(lua expr %lua), lua expr "vars"]
|
||||
|
||||
parse (parse %code as lua block %lua) as:
|
||||
parse nomsu \%code as nomsu:
|
||||
nomsu "replaced_vars" [\(lua block %lua), lua expr "vars"]
|
||||
|
||||
parse (pass) as lua code: ""
|
||||
|
||||
pass
|
||||
|
@ -1,15 +1,18 @@
|
||||
require "lib/metaprogramming.nom"
|
||||
|
||||
# Literals
|
||||
parse (true; yes) as lua expr "true"
|
||||
parse (false; no) as lua expr "false"
|
||||
parse (nil; null) as lua expr "nil"
|
||||
parse (inf; infinity) as lua expr "math.huge"
|
||||
parse (nan; NaN; not a number) as lua expr "(0/0)"
|
||||
parse (nop; pass) as lua 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
|
||||
parse (%if_expr if %condition else %else_expr) as lua expr ".."
|
||||
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)
|
||||
@ -21,54 +24,46 @@ parse (%if_expr if %condition else %else_expr) as lua expr ".."
|
||||
| end
|
||||
|end)(nomsu, vars)
|
||||
|
||||
# Indexing:
|
||||
compile (%obj's %key; %obj -> %key) to: "\(%obj as lua)[\(%key as lua)]"
|
||||
|
||||
# Variable assignment operator, and += type versions
|
||||
lua block ".."
|
||||
|local function assign(callback)
|
||||
| return function(nomsu, vars, kind)
|
||||
| if kind == "Expression" then
|
||||
| nomsu:error("Cannot use an assignment operation as an expression value.")
|
||||
| end
|
||||
| if vars.var.type ~= "Var" then
|
||||
| nomsu:error("Assignment operation has the wrong type for the left hand side. "
|
||||
| .."Expected Var, but got: "..vars.var.type.."\\nMaybe you forgot a percent sign on the variable name?")
|
||||
| end
|
||||
| if vars.rhs.type ~= "Thunk" then
|
||||
| nomsu:error("Assignment operation has the wrong type for the right hand side. "
|
||||
| .."Expected Thunk, but got: "..vars.rhs.type.."\\nMaybe you used '=' instead of '=:'?")
|
||||
| end
|
||||
| if #vars.rhs.value.value > 1 then
|
||||
| nomsu:error("Assignment operation should not have more than one value on the right hand side.")
|
||||
| end
|
||||
| return callback(nomsu:tree_to_lua(vars.var),
|
||||
| nomsu:tree_to_lua(vars.rhs.value.value[1].value)), true
|
||||
| end
|
||||
|end
|
||||
|nomsu:defmacro("%var = %rhs", assign(function(var,result) return var.." = "..result end))
|
||||
|nomsu:defmacro("%var += %rhs", assign(function(var,result) return var.." = "..var.." + "..result end))
|
||||
|nomsu:defmacro("%var -= %rhs", assign(function(var,result) return var.." = "..var.." - "..result end))
|
||||
|nomsu:defmacro("%var *= %rhs", assign(function(var,result) return var.." = "..var.." * "..result end))
|
||||
|nomsu:defmacro("%var /= %rhs", assign(function(var,result) return var.." = "..var.." / "..result end))
|
||||
|nomsu:defmacro("%var ^= %rhs", assign(function(var,result) return var.." = "..var.." ^ "..result end))
|
||||
|nomsu:defmacro("%var and= %rhs", assign(function(var,result) return var.." = "..var.." and "..result end))
|
||||
|nomsu:defmacro("%var or= %rhs", assign(function(var,result) return var.." = "..var.." or "..result end))
|
||||
|nomsu:defmacro("%var join= %rhs", assign(function(var,result) return var.." = "..var.." .. "..result end))
|
||||
|nomsu:defmacro("%var mod= %rhs", assign(function(var,result) return var.." = "..var.." % "..result end))
|
||||
compile (%var = %val) to code: "\(%var as lua) = \(%val as lua)"
|
||||
compile (%var += %val) to code: "\(%var as lua) += \(%val as lua)"
|
||||
compile (%var -= %val) to code: "\(%var as lua) -= \(%val as lua)"
|
||||
compile (%var *= %val) to code: "\(%var as lua) *= \(%val as lua)"
|
||||
compile (%var /= %val) to code: "\(%var as lua) /= \(%val as lua)"
|
||||
compile (%var ^= %val) to code: "\(%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
|
||||
|
||||
# Binary Operators
|
||||
lua block ".."
|
||||
|local binops = {"+","-","*","/","<","<=",">",">=","^",{"===","=="},{"!==","~="},"and","or",{"mod","%"}}
|
||||
|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, kind)
|
||||
| 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
|
||||
# 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))"
|
||||
# == and != do equivalence checking, rather than identity checking
|
||||
parse (%a == %b) as lua expr "nomsu.utils.equivalent(\(%a), \(%b))"
|
||||
parse (%a != %b) as lua expr "(not nomsu.utils.equivalent(\(%a), \(%b)))"
|
||||
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
|
||||
@ -77,10 +72,10 @@ lua block ".."
|
||||
|local comops = {"+","*","and","or"}
|
||||
|for _,_op in ipairs(comops) do
|
||||
| local op = _op
|
||||
| local spec = "%1 "..op.." %2"
|
||||
| for n=3,max_operands do
|
||||
| local spec = "%1 "
|
||||
| for n=2,max_operands do
|
||||
| spec = spec .." "..op.." %"..tostring(n)
|
||||
| nomsu:defmacro(spec, (function(nomsu, vars, kind)
|
||||
| nomsu:defmacro(spec, (function(nomsu, vars)
|
||||
| local bits = {}
|
||||
| for i=1,n do
|
||||
| table.insert(bits, (nomsu:tree_to_lua(vars[tostring(i)])))
|
||||
@ -125,5 +120,5 @@ lua block ".."
|
||||
|end
|
||||
|
||||
# Unary operators
|
||||
parse (- %a) as lua expr "-(\(%a))"
|
||||
parse (not %a) as lua expr "not (\(%a))"
|
||||
compile (- %) to: "-(\(% as lua))"
|
||||
compile (not %) to: "not (\(% as lua))"
|
||||
|
@ -4,43 +4,36 @@ require "lib/operators.nom"
|
||||
require "lib/collections.nom"
|
||||
|
||||
# Permission functions
|
||||
rule [restrict %rules to within %elite-rules] =:
|
||||
say ".."|Restricting \%rules\ to within \%elite-rules\
|
||||
%rules =: keys in (nomsu "get_aliases" [%rules])
|
||||
%elite-rules =: keys in (nomsu "get_aliases" [%elite-rules])
|
||||
rule (restrict %rules to within %elite-rules) =:
|
||||
say "Restricting \(%rules) to within \(%elite-rules)"
|
||||
for all (flatten [%elite-rules, %rules]):
|
||||
assert ((nomsu's "defs") has key %it) ".."|Undefined function: \%it\
|
||||
assert ((nomsu's "defs") has key %) "Undefined function: \(%)"
|
||||
for all %rules:
|
||||
assert (nomsu "check_permission" [%it]) ".."
|
||||
|You do not have permission to restrict permissions for function: \%it\
|
||||
%foo =: dict (..)
|
||||
[%it, yes] for %it in %elite-rules
|
||||
((nomsu's "defs")'s %it)'s "whiteset" =: %foo
|
||||
assert (nomsu "check_permission" [%]) ".."
|
||||
|You do not have permission to restrict permissions for function: \(%)
|
||||
((nomsu) ->* ["defs",%,"whiteset"]) =:
|
||||
dict: [%, yes] for %it in %elite-rules
|
||||
|
||||
rule [allow %elite-rules to use %rules] =:
|
||||
say ".."|Allowing \%elite-rules\ to use \%rules\
|
||||
%rules =: keys in (nomsu "get_aliases" [%rules])
|
||||
%elite-rules =: keys in (nomsu "get_aliases" [%elite-rules])
|
||||
rule (allow %elite-rules to use %rules) =:
|
||||
say "Allowing \(%elite-rules) to use \(%rules)"
|
||||
for all (flatten [%elite-rules, %rules]):
|
||||
assert ((nomsu's "defs") has key %it) ".."|Undefined function: \%it\
|
||||
for %fn in %rules:
|
||||
assert (nomsu "check_permission" [%fn]) ".."
|
||||
|You do not have permission to grant permissions for function: \%fn\
|
||||
%whiteset =: ((nomsu's "defs")'s %fn)'s "whiteset"
|
||||
if (not %whiteset): on to the next %fn
|
||||
for all %elite-rules: %whiteset's %it =: yes
|
||||
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"]
|
||||
if (not %whiteset): go to next %rule
|
||||
for all %elite-rules: %whiteset -> % = (yes)
|
||||
|
||||
rule [forbid %pleb-rules to use %rules] =:
|
||||
say ".."|Forbidding \%pleb-rules\ to use \%rules\
|
||||
%rules =: keys in (nomsu "get_aliases" [%rules])
|
||||
%pleb-rules =: keys in (nomsu "get_aliases" [%pleb-rules])
|
||||
rule (forbid %pleb-rules to use %rules) =:
|
||||
say "Forbidding \(%pleb-rules) to use \(%rules)"
|
||||
for all (flatten [%pleb-rules, %used]):
|
||||
assert ((nomsu's "defs") has key %it) ".."|Undefined function: \%it\
|
||||
assert ((nomsu's "defs") has key %) "Undefined function: \(%)"
|
||||
for all %rules:
|
||||
assert (nomsu "check_permission" [%it]) ".."
|
||||
assert (nomsu "check_permission" [%]) ".."
|
||||
|You do not have permission to grant permissions for function: \%it\
|
||||
%whiteset =: ((nomsu's "defs")'s %it)'s "whiteset"
|
||||
%whiteset =: (nomsu) ->* ["defs",%,"whiteset"]
|
||||
assert %whiteset ".."
|
||||
|Cannot individually restrict permissions for \%it\ because it is currently
|
||||
|Cannot individually restrict permissions for \(%) because it is currently
|
||||
|available to everyone. Perhaps you meant to use "restrict % to within %" instead?
|
||||
for all %pleb-rules: %whiteset's %it =: nil
|
||||
for all %pleb-rules: %whiteset's % = (nil)
|
||||
|
@ -1,33 +1,30 @@
|
||||
require "lib/core.nom"
|
||||
|
||||
macro block [with secrets %block] =: ".."
|
||||
compile (with secrets %block) to block: ".."
|
||||
|local secrets = {}
|
||||
|\(%block's "value") as lua\
|
||||
|
||||
macro block [with secrets as %secret_name %block] =: ".."
|
||||
|local \%secret_name as value\ = {}
|
||||
|\(%block's "value") as lua\
|
||||
|\(%block as lua statements)
|
||||
|
||||
# Access the lua variable that should be within scope
|
||||
macro [secrets] =: "secrets"
|
||||
compile (secrets) to: "secrets"
|
||||
|
||||
macro [secret %key, secret value of %key, secret value for %key] =:
|
||||
if ((%key's "type") != "Var"):
|
||||
error ".."
|
||||
|Wrong type, expected Var, but got: \%key's "type"\
|
||||
".."|secrets[\repr (%key's "value")\]
|
||||
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"))]"
|
||||
|
||||
macro block [secret %key = %new_value] =:
|
||||
lua block ".."
|
||||
|if vars.key.type ~= "Var" then
|
||||
| nomsu:error("Assignment operation has the wrong type for the left hand side. "
|
||||
| .."Expected Var, but got: "..vars.key.type)
|
||||
|end
|
||||
|if vars.new_value.type ~= "Thunk" then
|
||||
| nomsu:error("Assignment operation has the wrong type for the right hand side. "
|
||||
| .."Expected Thunk, but got: "..vars.new_value.type.."\\nMaybe you used '=' instead of '=:'?")
|
||||
|end
|
||||
".."
|
||||
|local ret
|
||||
|\lua expr "nomsu:tree_to_lua(vars.new_value.value)"\
|
||||
|secrets[\repr (%key's "value")\] = ret
|
||||
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)"
|
||||
|
||||
enable debugging
|
||||
with secrets:
|
||||
secret %foo = 5
|
||||
rule (plumb %) =:
|
||||
secret %foo = %
|
||||
rule (frop) =:
|
||||
secret %foo
|
||||
|
||||
say (frop)
|
||||
pumb 99
|
||||
say (frop)
|
||||
|
108
lib/utils.nom
108
lib/utils.nom
@ -5,62 +5,82 @@ rule (error!; panic!; fail!; abort!) =:
|
||||
nomsu "error" []
|
||||
rule (error %msg) =:
|
||||
nomsu "error"[%msg]
|
||||
parse (assert %condition) as: lua code ".."
|
||||
|if not (\(%condition)) then
|
||||
| nomsu:error()
|
||||
compile (assert %condition %msg) to code: ".."
|
||||
|if not (\(%condition as lua)) then
|
||||
| nomsu:error(\(%msg as lua))
|
||||
|end
|
||||
parse (assert %condition %msg) as: lua code ".."
|
||||
|if not (\(%condition)) then
|
||||
| nomsu:error(\(%msg))
|
||||
|end
|
||||
|
||||
parse (show generated lua %block) as: lua code ".."
|
||||
|nomsu:writeln(\(repr (nomsu "tree_to_lua" [%block])))
|
||||
|
||||
parse (assert %condition) as: assert %condition (nil)
|
||||
|
||||
# String functions
|
||||
rule (join %strs) =:
|
||||
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)
|
||||
|
||||
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 (capitalize %str; %str capitalized) as: lua expr "(\(%str)):gsub('%l', string.upper, 1)"
|
||||
compile (capitalize %str; %str capitalized) to:
|
||||
"(\(%str as lua)):gsub('%l', string.upper, 1)"
|
||||
|
||||
parse (say %str) as: lua block ".."
|
||||
|nomsu:writeln(nomsu.utils.repr_if_not_string(\(%str)))
|
||||
compile (say %str) to: ".."
|
||||
|nomsu:writeln(\(%str as lua))
|
||||
|
||||
# Number ranges
|
||||
parse (%start to %stop) as: lua expr ".."
|
||||
|nomsu.utils.range(\(%start), \(%stop))
|
||||
parse (%start to %stop by %step; %start to %stop via %step) as: lua expr ".."
|
||||
|nomsu.utils.range(\(%start), \(%stop), \(%step))
|
||||
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
|
||||
|
||||
# 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:
|
||||
"math.random(\(%low as lua), \(%high as lua))"
|
||||
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) =:
|
||||
lua expr "(\(%rounder))*math.floor(\(%n)/\(%rounder) + .5)"
|
||||
|
||||
# Common utility functions
|
||||
parse (random number; random; rand) as: lua expr "math.random()"
|
||||
parse (random int %n; random integer %n; randint %n) as: lua expr "math.random(\(%n))"
|
||||
parse (random from %low to %high; random number from %low to %high; rand %low %high) as:
|
||||
lua expr "math.random(\(%low), \(%high))"
|
||||
rule (random choice from %elements; random choice %elements; random %elements) =:
|
||||
lua expr "vars.elements[math.random(#vars.elements)]"
|
||||
parse (sum of %items; sum %items) as: lua expr "nomsu.utils.sum(\(%items))"
|
||||
parse (product of %items; product %items) as: lua expr "nomsu.utils.product(\(%items))"
|
||||
parse (all of %items) as: lua expr "nomsu.utils.all(\(%items))"
|
||||
parse (any of %items) as: lua expr "nomsu.utils.any(\(%items))"
|
||||
# This is a rule, not a macro so we can use vars.items twice without running it twice.
|
||||
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(vars.items)/#vars.items)"
|
||||
parse (min of %items; smallest of %items; lowest of %items) as:
|
||||
lua expr "nomsu.utils.min(\(%items))"
|
||||
parse (max of %items; biggest of %items; largest of %items; highest of %items) as:
|
||||
lua expr "nomsu.utils.max(\(%items))"
|
||||
parse (min of %items with respect to %keys) as:
|
||||
lua expr "nomsu.utils.min(\(%items), \(%keys))"
|
||||
parse (max of %items with respect to %keys) as:
|
||||
lua expr "nomsu.utils.max(\(%items), \(%keys))"
|
||||
lua expr "(nomsu.utils.sum(\(%items))/#\(%items))"
|
||||
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:
|
||||
"nomsu.utils.max(\(%items as lua))"
|
||||
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:
|
||||
".."
|
||||
|nomsu.utils.max(\(%items as lua), function(item)
|
||||
| local vars = setmetatable({['']=item}, {__index=vars})
|
||||
| return \(%value_expr as lua)
|
||||
|end)
|
||||
|
||||
|
142
nomsu.lua
142
nomsu.lua
@ -2,6 +2,14 @@ local re = require('re')
|
||||
local lpeg = require('lpeg')
|
||||
local utils = require('utils')
|
||||
local repr = utils.repr
|
||||
local colors = require('ansicolors')
|
||||
local colored = setmetatable({ }, {
|
||||
__index = function(_, color)
|
||||
return (function(msg)
|
||||
return colors[color] .. msg .. colors.reset
|
||||
end)
|
||||
end
|
||||
})
|
||||
local insert, remove, concat
|
||||
do
|
||||
local _obj_0 = table
|
||||
@ -191,7 +199,7 @@ do
|
||||
local stub, arg_names = self:get_stub(invocation)
|
||||
assert(stub, "NO STUB FOUND: " .. tostring(repr(invocation)))
|
||||
if self.debug then
|
||||
self:writeln("Defining rule: " .. tostring(repr(stub)) .. " with args " .. tostring(repr(arg_names)))
|
||||
self:writeln(tostring(colored.bright("DEFINING RULE:")) .. " " .. tostring(colored.underscore(colored.magenta(repr(stub)))) .. " " .. tostring(colored.bright("WITH ARGS")) .. " " .. tostring(colored.dim(repr(arg_names))))
|
||||
end
|
||||
for i = 1, #arg_names - 1 do
|
||||
for j = i + 1, #arg_names do
|
||||
@ -242,7 +250,8 @@ do
|
||||
args = _tbl_0
|
||||
end
|
||||
if self.debug then
|
||||
self:writeln("Calling " .. tostring(repr(stub)) .. " with args: " .. tostring(repr(args)))
|
||||
self:write(tostring(colored.bright("CALLING")) .. " " .. tostring(colored.magenta(colored.underscore(stub))) .. " ")
|
||||
self:writeln(tostring(colored.bright("WITH ARGS:")) .. " " .. tostring(colored.dim(repr(args))))
|
||||
end
|
||||
insert(self.callstack, stub)
|
||||
local rets = {
|
||||
@ -255,7 +264,11 @@ do
|
||||
if kind == nil then
|
||||
kind = "Expression"
|
||||
end
|
||||
local stub, args = self:get_stub(tree)
|
||||
local stub, arg_names, args = self:get_stub(tree)
|
||||
if self.debug then
|
||||
self:write(tostring(colored.bright("RUNNING MACRO")) .. " " .. tostring(colored.underscore(colored.magenta(stub))) .. " ")
|
||||
self:writeln(tostring(colored.bright("WITH ARGS:")) .. " " .. tostring(colored.dim(repr(args))))
|
||||
end
|
||||
insert(self.callstack, "#macro")
|
||||
local expr, statement = self:call(stub, unpack(args))
|
||||
remove(self.callstack)
|
||||
@ -284,7 +297,7 @@ do
|
||||
end,
|
||||
parse = function(self, str, filename)
|
||||
if self.debug then
|
||||
self:writeln("PARSING:\n" .. tostring(str))
|
||||
self:writeln(tostring(colored.bright("PARSING:")) .. "\n" .. tostring(str))
|
||||
end
|
||||
str = str:gsub("\r", "")
|
||||
local old_indent_stack
|
||||
@ -311,12 +324,13 @@ do
|
||||
for _index_0 = 1, #_list_0 do
|
||||
local statement = _list_0[_index_0]
|
||||
if self.debug then
|
||||
self:writeln("RUNNING TREE:")
|
||||
self:writeln(tostring(colored.bright("RUNNING NOMSU:")) .. "\n" .. tostring(colored.bright(colored.yellow(statement.src))))
|
||||
self:writeln(colored.bright("PARSED TO TREE:"))
|
||||
self:print_tree(statement)
|
||||
end
|
||||
local ok, expr, statements = pcall(self.tree_to_lua, self, statement)
|
||||
if not ok then
|
||||
self:writeln("Error occurred in statement:\n" .. tostring(statement.src))
|
||||
self:writeln(tostring(colored.red("Error occurred in statement:")) .. "\n" .. tostring(colored.bright(colored.yellow(statement.src))))
|
||||
self:error(expr)
|
||||
end
|
||||
local code_for_statement = ([[ return (function(nomsu, vars)
|
||||
@ -324,11 +338,11 @@ do
|
||||
return %s
|
||||
end)]]):format(statements or "", expr or "")
|
||||
if self.debug then
|
||||
self:writeln("RUNNING LUA:\n" .. tostring(code_for_statement))
|
||||
self:writeln(tostring(colored.bright("RUNNING LUA:")) .. "\n" .. tostring(colored.blue(colored.bright(code_for_statement))))
|
||||
end
|
||||
local lua_thunk, err = load(code_for_statement)
|
||||
if not lua_thunk then
|
||||
error("Failed to compile generated code:\n" .. tostring(code_for_statement) .. "\n\n" .. tostring(err) .. "\n\nProduced by statement:\n" .. tostring(statement.src))
|
||||
error("Failed to compile generated code:\n" .. tostring(colored.bright(colored.blue(code_for_statement))) .. "\n\n" .. tostring(err) .. "\n\nProduced by statement:\n" .. tostring(colored.bright(colored.yellow(statement.src))))
|
||||
end
|
||||
local run_statement = lua_thunk()
|
||||
local ret
|
||||
@ -337,7 +351,7 @@ do
|
||||
return_value = ret
|
||||
end
|
||||
if not ok then
|
||||
self:writeln("Error occurred in statement:\n" .. tostring(statement.src))
|
||||
self:writeln(tostring(colored.red("Error occurred in statement:")) .. "\n" .. tostring(colored.yellow(statement.src)))
|
||||
self:error(repr(return_value))
|
||||
end
|
||||
insert(buffer, tostring(statements or '') .. "\n" .. tostring(expr and "ret = " .. tostring(expr) or ''))
|
||||
@ -350,10 +364,13 @@ do
|
||||
return return_value, lua_code
|
||||
end,
|
||||
tree_to_value = function(self, tree, vars)
|
||||
local code = "\n return (function(nomsu, vars)\nreturn " .. tostring(self:tree_to_lua(tree)) .. "\nend)"
|
||||
local code = "return (function(nomsu, vars)\nreturn " .. tostring(self:tree_to_lua(tree)) .. "\nend)"
|
||||
if self.debug then
|
||||
self:writeln(tostring(colored.bright("RUNNING LUA TO GET VALUE:")) .. "\n" .. tostring(colored.blue(colored.bright(code))))
|
||||
end
|
||||
local lua_thunk, err = load(code)
|
||||
if not lua_thunk then
|
||||
error("Failed to compile generated code:\n" .. tostring(code) .. "\n\n" .. tostring(err))
|
||||
self:error("Failed to compile generated code:\n" .. tostring(colored.bright(colored.blue(code))) .. "\n\n" .. tostring(colored.red(err)))
|
||||
end
|
||||
return (lua_thunk())(self, vars or { })
|
||||
end,
|
||||
@ -369,23 +386,12 @@ do
|
||||
elseif "Nomsu" == _exp_0 then
|
||||
return repr(tree.value), nil
|
||||
elseif "Thunk" == _exp_0 then
|
||||
local lua_bits = { }
|
||||
local _list_0 = tree.value.value
|
||||
for _index_0 = 1, #_list_0 do
|
||||
local arg = _list_0[_index_0]
|
||||
local expr, statement = self:tree_to_lua(arg)
|
||||
if statement then
|
||||
insert(lua_bits, statement)
|
||||
end
|
||||
if expr then
|
||||
insert(lua_bits, "ret = " .. tostring(expr))
|
||||
end
|
||||
end
|
||||
local _, body = self:tree_to_lua(tree.value)
|
||||
return ([[ (function(nomsu, vars)
|
||||
local ret
|
||||
%s
|
||||
return ret
|
||||
end)]]):format(concat(lua_bits, "\n"))
|
||||
end)]]):format(body), nil
|
||||
elseif "Block" == _exp_0 then
|
||||
if #tree.value == 0 then
|
||||
return "nil", nil
|
||||
@ -398,10 +404,28 @@ do
|
||||
end
|
||||
local thunk_lua = self:tree_to_lua({
|
||||
type = "Thunk",
|
||||
value = tree,
|
||||
value = {
|
||||
type = "Statements",
|
||||
value = tree.value,
|
||||
src = tree.src
|
||||
},
|
||||
src = tree.src
|
||||
})
|
||||
return ("%s(nomsu, vars)"):format(thunk_lua), nil
|
||||
elseif "Statements" == _exp_0 then
|
||||
local lua_bits = { }
|
||||
local _list_0 = tree.value
|
||||
for _index_0 = 1, #_list_0 do
|
||||
local arg = _list_0[_index_0]
|
||||
local expr, statement = self:tree_to_lua(arg)
|
||||
if statement then
|
||||
insert(lua_bits, statement)
|
||||
end
|
||||
if expr then
|
||||
insert(lua_bits, "ret = " .. tostring(expr))
|
||||
end
|
||||
end
|
||||
return nil, concat(lua_bits, "\n")
|
||||
elseif "FunctionCall" == _exp_0 then
|
||||
local stub = self:get_stub(tree)
|
||||
if self.defs[stub] and self.defs[stub].is_macro then
|
||||
@ -432,6 +456,10 @@ do
|
||||
end
|
||||
return self.__class:comma_separated_items("nomsu:call(", args, ")"), nil
|
||||
elseif "String" == _exp_0 then
|
||||
if self.debug then
|
||||
self:writeln((colored.bright("STRING:")))
|
||||
self:print_tree(tree)
|
||||
end
|
||||
local concat_parts = { }
|
||||
local string_buffer = ""
|
||||
local _list_0 = tree.value
|
||||
@ -449,6 +477,11 @@ do
|
||||
string_buffer = ""
|
||||
end
|
||||
local expr, statement = self:tree_to_lua(bit)
|
||||
if self.debug then
|
||||
self:writeln((colored.bright("INTERP:")))
|
||||
self:print_tree(bit)
|
||||
self:writeln(tostring(colored.bright("EXPR:")) .. " " .. tostring(expr) .. ", " .. tostring(colored.bright("STATEMENT:")) .. " " .. tostring(statement))
|
||||
end
|
||||
if statement then
|
||||
self:error("Cannot use [[" .. tostring(bit.src) .. "]] as a string interpolation value, since it's not an expression.")
|
||||
end
|
||||
@ -464,8 +497,11 @@ do
|
||||
end
|
||||
if #concat_parts == 0 then
|
||||
return "''", nil
|
||||
elseif #concat_parts == 1 then
|
||||
return concat_parts[1], nil
|
||||
else
|
||||
return "(" .. tostring(concat(concat_parts, "..")) .. ")", nil
|
||||
end
|
||||
return "(" .. tostring(concat(concat_parts, "..")) .. ")", nil
|
||||
elseif "List" == _exp_0 then
|
||||
local items = { }
|
||||
local _list_0 = tree.value
|
||||
@ -479,9 +515,9 @@ do
|
||||
end
|
||||
return self.__class:comma_separated_items("{", items, "}"), nil
|
||||
elseif "Number" == _exp_0 then
|
||||
return repr(tree.value)
|
||||
return repr(tree.value), nil
|
||||
elseif "Var" == _exp_0 then
|
||||
return "vars[" .. tostring(repr(tree.value)) .. "]"
|
||||
return "vars[" .. tostring(repr(tree.value)) .. "]", nil
|
||||
else
|
||||
return self:error("Unknown/unimplemented thingy: " .. tostring(tree.type))
|
||||
end
|
||||
@ -495,7 +531,7 @@ do
|
||||
return
|
||||
end
|
||||
local _exp_0 = tree.type
|
||||
if "List" == _exp_0 or "File" == _exp_0 or "Nomsu" == _exp_0 or "Block" == _exp_0 or "FunctionCall" == _exp_0 or "String" == _exp_0 then
|
||||
if "List" == _exp_0 or "File" == _exp_0 or "Block" == _exp_0 or "FunctionCall" == _exp_0 or "String" == _exp_0 then
|
||||
local _list_0 = tree.value
|
||||
for _index_0 = 1, #_list_0 do
|
||||
local v = _list_0[_index_0]
|
||||
@ -507,6 +543,7 @@ do
|
||||
return nil
|
||||
end,
|
||||
print_tree = function(self, tree)
|
||||
self:write(colors.bright .. colors.green)
|
||||
for node, depth in coroutine.wrap(function()
|
||||
return self:walk_tree(tree)
|
||||
end) do
|
||||
@ -516,6 +553,7 @@ do
|
||||
self:writeln(tostring((" "):rep(depth)) .. tostring(node.type) .. ":")
|
||||
end
|
||||
end
|
||||
return self:write(colors.reset)
|
||||
end,
|
||||
tree_to_str = function(self, tree)
|
||||
local bits = { }
|
||||
@ -536,7 +574,7 @@ do
|
||||
end
|
||||
local _exp_0 = tree.type
|
||||
if "Var" == _exp_0 then
|
||||
if vars[tree.value] then
|
||||
if vars[tree.value] ~= nil then
|
||||
tree = vars[tree.value]
|
||||
end
|
||||
elseif "File" == _exp_0 or "Nomsu" == _exp_0 or "Thunk" == _exp_0 or "Block" == _exp_0 or "List" == _exp_0 or "FunctionCall" == _exp_0 or "String" == _exp_0 then
|
||||
@ -570,7 +608,7 @@ do
|
||||
end
|
||||
if type(x) == 'string' then
|
||||
local stub = x:gsub("'", " '"):gsub("%%%S+", "%%"):gsub("%s+", " ")
|
||||
local args
|
||||
local arg_names
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
@ -578,15 +616,15 @@ do
|
||||
_accum_0[_len_0] = arg
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
args = _accum_0
|
||||
arg_names = _accum_0
|
||||
end
|
||||
return stub, args
|
||||
return stub, arg_names
|
||||
end
|
||||
local _exp_0 = x.type
|
||||
if "String" == _exp_0 then
|
||||
return self:get_stub(x.value)
|
||||
elseif "FunctionCall" == _exp_0 then
|
||||
local stub, args = { }, { }, { }
|
||||
local stub, arg_names, args = { }, { }, { }
|
||||
local _list_0 = x.value
|
||||
for _index_0 = 1, #_list_0 do
|
||||
local token = _list_0[_index_0]
|
||||
@ -595,13 +633,17 @@ do
|
||||
insert(stub, token.value)
|
||||
elseif "Var" == _exp_1 then
|
||||
insert(stub, "%")
|
||||
insert(args, token.value)
|
||||
if arg_names then
|
||||
insert(arg_names, token.value)
|
||||
end
|
||||
insert(args, token)
|
||||
else
|
||||
insert(stub, "%")
|
||||
arg_names = nil
|
||||
insert(args, token)
|
||||
end
|
||||
end
|
||||
return concat(stub, " "), args
|
||||
return concat(stub, " "), arg_names, args
|
||||
elseif "Block" == _exp_0 then
|
||||
self:writeln(debug.traceback())
|
||||
return self:error("Please pass in a single line from a block, not the whole thing:\n" .. tostring(self:tree_to_str(x)))
|
||||
@ -632,21 +674,39 @@ do
|
||||
self.callstack = { }
|
||||
return error()
|
||||
end,
|
||||
typecheck = function(self, vars, varname, desired_type)
|
||||
local x = vars[varname]
|
||||
if type(x) == desired_type then
|
||||
return x
|
||||
end
|
||||
if type(x) == 'table' and x.type == desired_type then
|
||||
return x
|
||||
end
|
||||
return self:error("Invalid type for %" .. tostring(varname) .. ". Expected " .. tostring(desired_type) .. ", but got " .. tostring(x.type) .. ".")
|
||||
end,
|
||||
initialize_core = function(self)
|
||||
local lua_code
|
||||
lua_code = function(self, vars)
|
||||
local inner_vars = vars
|
||||
local inner_vars = setmetatable({ }, {
|
||||
__index = function(_, key)
|
||||
return "vars[" .. tostring(repr(key)) .. "]"
|
||||
end
|
||||
})
|
||||
local lua = self:tree_to_value(vars.code, inner_vars)
|
||||
return nil, lua
|
||||
end
|
||||
self:defmacro("lua code %code", lua_code)
|
||||
local lua_value
|
||||
lua_value = function(self, vars)
|
||||
local inner_vars = vars
|
||||
local inner_vars = setmetatable({ }, {
|
||||
__index = function(_, key)
|
||||
return "vars[" .. tostring(repr(key)) .. "]"
|
||||
end
|
||||
})
|
||||
local lua = self:tree_to_value(vars.code, inner_vars)
|
||||
return lua, nil
|
||||
end
|
||||
self:defmacro("lua value %code", lua_value)
|
||||
self:defmacro("lua expr %code", lua_value)
|
||||
local _require
|
||||
_require = function(self, vars)
|
||||
if not self.loaded_files[vars.filename] then
|
||||
@ -744,7 +804,9 @@ if arg and arg[1] then
|
||||
end
|
||||
output:write(([[ local NomsuCompiler = require('nomsu')
|
||||
local c = NomsuCompiler()
|
||||
local run = %s
|
||||
local run = function(nomsu, vars)
|
||||
%s
|
||||
end
|
||||
return run(c, {})
|
||||
]]):format(code))
|
||||
end
|
||||
|
102
nomsu.moon
102
nomsu.moon
@ -15,6 +15,8 @@ re = require 're'
|
||||
lpeg = require 'lpeg'
|
||||
utils = require 'utils'
|
||||
repr = utils.repr
|
||||
colors = require 'ansicolors'
|
||||
colored = setmetatable({}, {__index:(_,color)-> ((msg)-> colors[color]..msg..colors.reset)})
|
||||
{:insert, :remove, :concat} = table
|
||||
--pcall = (fn,...)-> true, fn(...)
|
||||
|
||||
@ -189,7 +191,7 @@ class NomsuCompiler
|
||||
def: (invocation, thunk, src)=>
|
||||
stub, arg_names = @get_stub invocation
|
||||
assert stub, "NO STUB FOUND: #{repr invocation}"
|
||||
if @debug then @writeln "Defining rule: #{repr stub} with args #{repr arg_names}"
|
||||
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
|
||||
@ -210,7 +212,8 @@ class NomsuCompiler
|
||||
{:thunk, :arg_names} = def
|
||||
args = {name, select(i,...) for i,name in ipairs(arg_names)}
|
||||
if @debug
|
||||
@writeln "Calling #{repr stub} with args: #{repr(args)}"
|
||||
@write "#{colored.bright "CALLING"} #{colored.magenta(colored.underscore stub)} "
|
||||
@writeln "#{colored.bright "WITH ARGS:"} #{colored.dim repr(args)}"
|
||||
insert @callstack, stub
|
||||
-- TODO: optimize, but still allow multiple return values?
|
||||
rets = {thunk(self,args)}
|
||||
@ -218,7 +221,10 @@ class NomsuCompiler
|
||||
return unpack(rets)
|
||||
|
||||
run_macro: (tree, kind="Expression")=>
|
||||
stub,args = @get_stub tree
|
||||
stub,arg_names,args = @get_stub tree
|
||||
if @debug
|
||||
@write "#{colored.bright "RUNNING MACRO"} #{colored.underscore colored.magenta(stub)} "
|
||||
@writeln "#{colored.bright "WITH ARGS:"} #{colored.dim repr args}"
|
||||
insert @callstack, "#macro"
|
||||
expr, statement = @call(stub, unpack(args))
|
||||
remove @callstack
|
||||
@ -240,7 +246,7 @@ class NomsuCompiler
|
||||
|
||||
parse: (str, filename)=>
|
||||
if @debug
|
||||
@writeln("PARSING:\n#{str}")
|
||||
@writeln("#{colored.bright "PARSING:"}\n#{str}")
|
||||
str = str\gsub("\r","")
|
||||
export indent_stack
|
||||
old_indent_stack, indent_stack = indent_stack, {0}
|
||||
@ -262,11 +268,12 @@ class NomsuCompiler
|
||||
return_value = nil
|
||||
for statement in *tree.value
|
||||
if @debug
|
||||
@writeln "RUNNING TREE:"
|
||||
@writeln "#{colored.bright "RUNNING NOMSU:"}\n#{colored.bright colored.yellow statement.src}"
|
||||
@writeln colored.bright("PARSED TO TREE:")
|
||||
@print_tree statement
|
||||
ok,expr,statements = pcall(@tree_to_lua, self, statement)
|
||||
if not ok
|
||||
@writeln "Error occurred in statement:\n#{statement.src}"
|
||||
@writeln "#{colored.red "Error occurred in statement:"}\n#{colored.bright colored.yellow statement.src}"
|
||||
@error(expr)
|
||||
code_for_statement = ([[
|
||||
return (function(nomsu, vars)
|
||||
@ -274,15 +281,15 @@ class NomsuCompiler
|
||||
return %s
|
||||
end)]])\format(statements or "", expr or "")
|
||||
if @debug
|
||||
@writeln "RUNNING LUA:\n#{code_for_statement}"
|
||||
@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#{code_for_statement}\n\n#{err}\n\nProduced by statement:\n#{statement.src}")
|
||||
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}")
|
||||
run_statement = lua_thunk!
|
||||
ok,ret = pcall(run_statement, self, vars)
|
||||
if expr then return_value = ret
|
||||
if not ok
|
||||
@writeln "Error occurred in statement:\n#{statement.src}"
|
||||
@writeln "#{colored.red "Error occurred in statement:"}\n#{colored.yellow statement.src}"
|
||||
@error(repr return_value)
|
||||
insert buffer, "#{statements or ''}\n#{expr and "ret = #{expr}" or ''}"
|
||||
|
||||
@ -295,11 +302,12 @@ class NomsuCompiler
|
||||
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)
|
||||
if not lua_thunk
|
||||
error("Failed to compile generated code:\n#{code}\n\n#{err}")
|
||||
@error("Failed to compile generated code:\n#{colored.bright colored.blue code}\n\n#{colored.red err}")
|
||||
return (lua_thunk!)(self, vars or {})
|
||||
|
||||
tree_to_lua: (tree)=>
|
||||
@ -316,17 +324,13 @@ class NomsuCompiler
|
||||
return repr(tree.value), nil
|
||||
|
||||
when "Thunk" -- This is not created by the parser, it's just a helper
|
||||
lua_bits = {}
|
||||
for arg in *tree.value.value
|
||||
expr,statement = @tree_to_lua arg
|
||||
if statement then insert lua_bits, statement
|
||||
if expr then insert lua_bits, "ret = #{expr}"
|
||||
_,body = @tree_to_lua tree.value
|
||||
return ([[
|
||||
(function(nomsu, vars)
|
||||
local ret
|
||||
%s
|
||||
return ret
|
||||
end)]])\format(concat lua_bits, "\n")
|
||||
end)]])\format(body), nil
|
||||
|
||||
when "Block"
|
||||
if #tree.value == 0
|
||||
@ -335,9 +339,17 @@ class NomsuCompiler
|
||||
expr,statement = @tree_to_lua tree.value[1]
|
||||
if not statement
|
||||
return expr, nil
|
||||
thunk_lua = @tree_to_lua {type:"Thunk", value:tree, src:tree.src}
|
||||
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"
|
||||
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")
|
||||
|
||||
when "FunctionCall"
|
||||
stub = @get_stub(tree)
|
||||
if @defs[stub] and @defs[stub].is_macro
|
||||
@ -352,6 +364,9 @@ class NomsuCompiler
|
||||
return @@comma_separated_items("nomsu:call(", args, ")"), nil
|
||||
|
||||
when "String"
|
||||
if @debug
|
||||
@writeln (colored.bright "STRING:")
|
||||
@print_tree tree
|
||||
concat_parts = {}
|
||||
string_buffer = ""
|
||||
for bit in *tree.value
|
||||
@ -362,6 +377,10 @@ class NomsuCompiler
|
||||
insert concat_parts, repr(string_buffer)
|
||||
string_buffer = ""
|
||||
expr, statement = @tree_to_lua bit
|
||||
if @debug
|
||||
@writeln (colored.bright "INTERP:")
|
||||
@print_tree bit
|
||||
@writeln "#{colored.bright "EXPR:"} #{expr}, #{colored.bright "STATEMENT:"} #{statement}"
|
||||
if statement
|
||||
@error "Cannot use [[#{bit.src}]] as a string interpolation value, since it's not an expression."
|
||||
insert concat_parts, "nomsu.utils.repr_if_not_string(#{expr})"
|
||||
@ -371,7 +390,9 @@ class NomsuCompiler
|
||||
|
||||
if #concat_parts == 0
|
||||
return "''", nil
|
||||
return "(#{concat(concat_parts, "..")})", nil
|
||||
elseif #concat_parts == 1
|
||||
return concat_parts[1], nil
|
||||
else return "(#{concat(concat_parts, "..")})", nil
|
||||
|
||||
when "List"
|
||||
items = {}
|
||||
@ -383,10 +404,10 @@ class NomsuCompiler
|
||||
return @@comma_separated_items("{", items, "}"), nil
|
||||
|
||||
when "Number"
|
||||
return repr(tree.value)
|
||||
return repr(tree.value), nil
|
||||
|
||||
when "Var"
|
||||
return "vars[#{repr tree.value}]"
|
||||
return "vars[#{repr tree.value}]", nil
|
||||
|
||||
else
|
||||
@error("Unknown/unimplemented thingy: #{tree.type}")
|
||||
@ -396,18 +417,20 @@ class NomsuCompiler
|
||||
if type(tree) != 'table' or not tree.type
|
||||
return
|
||||
switch tree.type
|
||||
when "List", "File", "Nomsu", "Block", "FunctionCall", "String"
|
||||
when "List", "File", "Block", "FunctionCall", "String"
|
||||
for v in *tree.value
|
||||
@walk_tree(v, depth+1)
|
||||
else @walk_tree(tree.value, depth+1)
|
||||
return nil
|
||||
|
||||
print_tree: (tree)=>
|
||||
@write colors.bright..colors.green
|
||||
for node,depth in coroutine.wrap(-> @walk_tree tree)
|
||||
if type(node) != 'table' or not node.type
|
||||
@writeln((" ")\rep(depth)..repr(node))
|
||||
else
|
||||
@writeln("#{(" ")\rep(depth)}#{node.type}:")
|
||||
@write colors.reset
|
||||
|
||||
tree_to_str: (tree)=>
|
||||
bits = {}
|
||||
@ -439,7 +462,7 @@ class NomsuCompiler
|
||||
if type(tree) != 'table' then return tree
|
||||
switch tree.type
|
||||
when "Var"
|
||||
if vars[tree.value]
|
||||
if vars[tree.value] ~= nil
|
||||
tree = vars[tree.value]
|
||||
when "File", "Nomsu", "Thunk", "Block", "List", "FunctionCall", "String"
|
||||
new_value = @replaced_vars tree.value, vars
|
||||
@ -463,23 +486,25 @@ class NomsuCompiler
|
||||
-- (e.g. "say %msg") or function call (e.g. FunctionCall({Word("say"), Var("msg")))
|
||||
if type(x) == 'string'
|
||||
stub = x\gsub("'"," '")\gsub("%%%S+","%%")\gsub("%s+"," ")
|
||||
args = [arg for arg in x\gmatch("%%([^%s']*)")]
|
||||
return stub, args
|
||||
arg_names = [arg for arg in x\gmatch("%%([^%s']*)")]
|
||||
return stub, arg_names
|
||||
switch x.type
|
||||
when "String" then return @get_stub(x.value)
|
||||
when "FunctionCall"
|
||||
stub, args = {}, {}, {}
|
||||
stub, arg_names, args = {}, {}, {}
|
||||
for token in *x.value
|
||||
switch token.type
|
||||
when "Word"
|
||||
insert stub, token.value
|
||||
when "Var"
|
||||
insert stub, "%"
|
||||
insert args, token.value
|
||||
if arg_names then insert arg_names, token.value
|
||||
insert args, token
|
||||
else
|
||||
insert stub, "%"
|
||||
arg_names = nil
|
||||
insert args, token
|
||||
return concat(stub," "), args
|
||||
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}"
|
||||
@ -502,21 +527,28 @@ class NomsuCompiler
|
||||
@writeln " <top level>"
|
||||
@callstack = {}
|
||||
error!
|
||||
|
||||
typecheck: (vars, varname, desired_type)=>
|
||||
x = vars[varname]
|
||||
if type(x) == desired_type then return x
|
||||
if type(x) == 'table' and x.type == desired_type then return x
|
||||
@error "Invalid type for %#{varname}. Expected #{desired_type}, but got #{x.type}."
|
||||
|
||||
initialize_core: =>
|
||||
-- Sets up some core functionality
|
||||
-- Uses named local functions to help out callstack readability
|
||||
lua_code = (vars)=>
|
||||
inner_vars = vars-- setmetatable({}, {__index:(_,key)-> "vars[#{repr(key)}]"})
|
||||
inner_vars = setmetatable({}, {__index:(_,key)-> "vars[#{repr(key)}]"})
|
||||
lua = @tree_to_value(vars.code, inner_vars)
|
||||
return nil, lua
|
||||
@defmacro "lua code %code", lua_code
|
||||
|
||||
lua_value = (vars)=>
|
||||
inner_vars = vars--setmetatable({}, {__index:(_,key)-> "vars[#{repr(key)}]"})
|
||||
inner_vars = setmetatable({}, {__index:(_,key)-> "vars[#{repr(key)}]"})
|
||||
lua = @tree_to_value(vars.code, inner_vars)
|
||||
return lua, nil
|
||||
@defmacro "lua value %code", lua_value
|
||||
|
||||
@defmacro "lua expr %code", lua_value
|
||||
|
||||
_require = (vars)=>
|
||||
if not @loaded_files[vars.filename]
|
||||
file = io.open(vars.filename)
|
||||
@ -553,7 +585,9 @@ if arg and arg[1]
|
||||
output\write ([[
|
||||
local NomsuCompiler = require('nomsu')
|
||||
local c = NomsuCompiler()
|
||||
local run = %s
|
||||
local run = function(nomsu, vars)
|
||||
%s
|
||||
end
|
||||
return run(c, {})
|
||||
]])\format(code)
|
||||
--ProFi\stop()
|
||||
|
Loading…
Reference in New Issue
Block a user