aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/collections.nom204
-rw-r--r--lib/control_flow.nom467
-rw-r--r--lib/core.nom10
-rw-r--r--lib/file_hash.nom46
-rw-r--r--lib/habit_breaker.nom54
-rw-r--r--lib/math.nom95
-rw-r--r--lib/metaprogramming.nom200
-rw-r--r--lib/object.nom2
-rw-r--r--lib/object2.nom135
-rw-r--r--lib/operators.nom209
-rw-r--r--lib/text.nom56
-rw-r--r--lib/training_wheels.nom2
12 files changed, 237 insertions, 1243 deletions
diff --git a/lib/collections.nom b/lib/collections.nom
deleted file mode 100644
index a8cb20f..0000000
--- a/lib/collections.nom
+++ /dev/null
@@ -1,204 +0,0 @@
-#..
- This file contains code that supports manipulating and using collections like lists
- and dictionaries.
-
-use "lib/metaprogramming.nom"
-use "lib/control_flow.nom"
-use "lib/operators.nom"
-
-# List/dict functions:
-
-# Indexing
-immediately:
- 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 {expr:"utils.nth_to_last(\(%list as lua expr), \(%index as lua expr))"}
-
-immediately:
- parse [first in %list, first %list] as: 1 st in %list
- parse [last in %list, last %list] as: 1 st to last in %list
-
-# Membership testing
-immediately:
- action [%item is in %list, %list contains %item, %list has %item]
- for %key = %value in %list
- if (%key is %item): return (yes)
- return (no)
-
- action [..]
- %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 is %item): return (no)
- return (yes)
-
-immediately:
- # Note: it's important to have the space after "[" to prevent confusion if %index is a string
- compile [%list has key %index, %list has index %index] to {..}
- expr: ".."
- ((\(%list as lua expr))[ \(%index as lua expr)] ~= nil)
-
- # Note: it's important to have the space after "[" to prevent confusion if %index is a string
- 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 {expr:"((\(%list as lua expr))[ \(%index as lua expr)] == nil)"}
-
- compile [length of %list, size of %list, size %list, number of %list, len %list] to:
- {expr:"utils.size(\(%list as lua expr))"}
-
- compile [append %item to %list, add %item to %list] to:
- {statements:"table.insert(\(%list as lua expr), \(%item as lua expr))"}
-
- compile [pop from %list, remove last from %list] to:
- {statements:"table.remove(\(%list as lua expr))"}
-
- compile [remove index %index from %list] to:
- {statements:"table.remove(\(%list as lua expr), \(%index as lua expr))"}
-
-# List Comprehension
-immediately:
- compile [%expression for %item in %iterable] to:
- assume ((%item's "type") is "Var") or barf ".."
- List comprehension has the wrong type for the loop variable. Expected Var, but got: \(%item's "type")
- return {..}
- expr:".."
- (function()
- local comprehension = {};
- for i,\(%item as lua expr) in ipairs(\(%iterable as lua expr)) do
- comprehension[i] = \(%expression as lua expr);
- end
- return comprehension;
- end)()
- parse [%expression for all %iterable] as: %expression for % in %iterable
-
- compile [..]
- %expression for %index from %start to %stop via %step
- %expression for %index from %start to %stop by %step
- ..to:
- assume ((%index's "type") is "Var") or barf ".."
- List comprehension has the wrong type for the loop variable. Expected Var, but got: \(%index's "type")
- return {..}
- expr:".."
- (function()
- local comprehension = {};
- for \(%index as lua expr)=\(%start as lua expr),\(%stop as lua expr),\(%step as lua expr) do
- comprehension[\(%index as lua expr)] = \(%expression as lua expr);
- end
- return comprehension;
- end)()
- parse [%expression for all ] as: %expression for % in %iterable
- parse [%expression for %var from %start to %stop] as: %expression for %var from %start to %stop via 1
- parse [..]
- %expression for all %start to %stop by %step
- %expression for all %start to %stop via %step
- ..as: %expression for % from %start to %stop via %step
- parse [%expression for all %start to %stop] as: %expression for all %start to %stop via 1
-
- compile [%expression for %key = %value in %iterable] to:
- assume ((%key's "type") is "Var") or barf ".."
- List comprehension has the wrong type for the key loop variable. Expected Var, but got: \(%key's "type")
- assume ((%value's "type") is "Var") or barf ".."
- List comprehension has the wrong type for the value loop variable. Expected Var, but got: \(%value's "type")
- return {..}
- expr: ".."
- (function()
- local comprehension = {};
- for \(%key as lua expr), \(%value as lua expr) in pairs(\(%iterable as lua expr)) do
- table.insert(comprehension, \(%expression as lua expr));
- end
- return comprehension;
- end)()
-
-# Dict comprehensions
-immediately:
- compile [%key = %value for %item in %iterable] to:
- assume ((%item's "type") is "Var") or barf ".."
- Dict comprehension has the wrong type for the loop variable. Expected Var, but got: \(%item's "type")
- # Note: it's important to have the space after "[" to prevent confusion if %key is a string
- return {..}
- expr: ".."
- (function()
- local comprehension = {};
- for i,\(%item as lua expr) in ipairs(\(%iterable as lua expr)) do
- comprehension[ \(%key as lua expr)] = \(%value as lua expr)
- end
- return comprehension;
- end)()
- parse [%key = %value for all %iterable] as: %key = %value for % in %iterable
-
- compile [%key = %value for %src_key = %src_value in %iterable] to:
- assume ((%src_key's "type") is "Var") or barf ".."
- Dict comprehension has the wrong type for the key loop variable. Expected Var, but got: \(%src_key's "type")
- assume ((%src_value's "type") is "Var") or barf ".."
- Dict comprehension has the wrong type for the value loop variable. Expected Var, but got: \(%src_value's "type")
- # Note: it's important to have the space after "[" to prevent confusion if %key is a string
- return {..}
- expr: ".."
- (function()
- local comprehension = {};
- for \(%src_key as lua expr), \(%src_value as lua expr) in pairs(\(%iterable as lua expr)) do
- comprehension[ \(%key as lua expr)] = \(%value as lua expr);
- end
- return comprehension;
- end)()
-
-immediately:
- action [%lists flattened]:
- %flat <- []
- for %list in %lists:
- for %item in %list:
- add %item to %flat
- return %flat
-
- parse [entries in %dict] as: {key:%k, value:%v} for %k = %v in %dict
- parse [keys in %dict] as: %k for %k = %v in %dict
- parse [values in %dict] as: %v for %k = %v in %dict
-
-# Sorting:
-immediately:
- compile [sort %items] to {statements:"table.sort(\(%items as lua expr))"}
- compile [sort %items by %key_expr] to {..}
- statements: ".."
- utils.sort(\(%items as lua expr), function(\(\% as lua expr))
- return \(%key_expr as lua expr);
- end)
-
-immediately:
- action [%items sorted, sorted %items]:
- %copy <- (% for all %items)
- sort %copy
- return %copy
- action [%items sorted by %key]:
- %copy <- (% for all %items)
- sort %copy by %key
- return %copy
-
- action [unique %items]:
- %unique <- []
- %seen <- {}
- for all %items:
- unless: % in %seen
- add % to %unique
- (% in %seen) <- (yes)
- return %unique
-
-immediately:
- # Metatable stuff
- compile [set %dict's metatable to %metatable] to {..}
- statements: "setmetatable(\(%dict as lua expr), \(%metatable as lua expr));"
-
- compile [new counter] to {expr:"setmetatable({}, {__index=function() return 0; end})"}
-
- compile [new default dict] to {..}
- expr: ".."
- setmetatable({}, {__index=function(self, key)
- t = {};
- self[key] = t;
- return t;
- end})
-
-# TODO: maybe make a generator/coroutine?
diff --git a/lib/control_flow.nom b/lib/control_flow.nom
deleted file mode 100644
index 75cf6fd..0000000
--- a/lib/control_flow.nom
+++ /dev/null
@@ -1,467 +0,0 @@
-#..
- This file contains compile-time actions that define basic control flow structures
- like "if" statements and loops.
-
-use "lib/metaprogramming.nom"
-use "lib/text.nom"
-use "lib/operators.nom"
-
-# No-Op
-immediately:
- compile [do nothing] to {statements:""}
-
-# Conditionals
-immediately:
- compile [if %condition %if_body] to:
- %if_body <- (%if_body as lua)
- return {..}
- locals: %if_body's "locals"
- statements:".."
- if \(%condition as lua expr) then
- \((%if_body's "statements") or "\(%if_body's "expr");")
- end
- parse [unless %condition %unless_body] as: if (not %condition) %unless_body
-
- compile [if %condition %if_body else %else_body, unless %condition %else_body else %if_body] to:
- %if_body <- (%if_body as lua)
- %else_body <- (%else_body as lua)
- lua> ".."
- local \%locals = {unpack(\%if_body.locals or {})};
- for i,loc in ipairs(\%else_body.locals or {}) do table.insert(\%locals, loc); end
- utils.deduplicate(\%locals);
- return {..}
- locals:%locals
- statements:".."
- if \(%condition as lua expr) then
- \((%if_body's "statements") or "\(%if_body's "expr");")
- else
- \((%else_body's "statements") or "\(%else_body's "expr");")
- end
-
-# Conditional expression (ternary operator)
-#.. Note: this uses a function instead of "(condition and if_expr or else_expr)"
- because that breaks if %if_expr is falsey, e.g. "x < 5 and false or 99"
-immediately
- compile [..]
- %when_true_expr if %condition else %when_false_expr
- %when_true_expr if %condition otherwise %when_false_expr
- %when_false_expr unless %condition else %when_true_expr
- %when_false_expr unless %condition then %when_true_expr
- ..to:
- #.. If %when_true_expr is guaranteed to be truthy, we can use Lua's idiomatic
- equivalent of a conditional expression: (cond and if_true or if_false)
- if: (%when_true_expr's "type") in {Text:yes, List:yes, Dict:yes, Number:yes}
- return {..}
- expr:"(\(%condition as lua expr) and \(%when_true_expr as lua expr) or \(%when_false_expr as lua expr))"
- ..else:
- #.. Otherwise, need to do an anonymous inline function (yuck, too bad lua
- doesn't have a proper ternary operator!)
- To see why this is necessary consider: (random()<.5 and false or 99)
- return {..}
- expr: ".."
- (function()
- if \(%condition as lua expr) then
- return \(%when_true_expr as lua expr);
- else
- return \(%when_false_expr as lua expr);
- end
- end)()
-
-# GOTOs
-immediately:
- compile [=== %label ===, --- %label ---, *** %label ***] to {..}
- statements:"::label_\(%label as lua identifier)::;"
- compile [go to %label] to {..}
- statements:"goto label_\(%label as lua identifier);"
-
-# Basic loop control
-immediately:
- compile [do next] to {statements:"continue;"}
- compile [stop] to {statements:"break;"}
-
-# Helper function
-immediately:
- compile [if %tree has subtree %subtree where %condition %body] to:
- %body_lua <- (%body as lua)
- %body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");")
- return {..}
- locals: %body_lua's "locals"
- statements:".."
- for \(%subtree as lua expr) in coroutine.wrap(function() nomsu:walk_tree(\(%tree as lua expr)) end) do
- if type(\(%subtree as lua expr)) == 'table' and \(%subtree as lua expr).type then
- if \(%condition as lua expr) then
- \%body_statements
- break;
- end
- end
- end
-
-# While loops
-immediately:
- compile [do next repeat] to {statements:"goto continue_repeat;"}
- compile [stop repeating] to {statements:"goto stop_repeat;"}
- compile [repeat while %condition %body] to
- %body_lua <- (%body as lua)
- %body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");")
- if %body has subtree % where:
- ((%'s "type") = "FunctionCall") and ((%'s "stub") is "do next repeat")
- ..: %body_statments +<- "\n::continue_repeat::;"
- %code <- ".."
- while \(%condition as lua expr) do
- \%body_statements
- end --while-loop
- if %body has subtree % where:
- ((%'s "type") = "FunctionCall") and ((%'s "stub") is "stop repeating")
- ..:
- %code <- ".."
- do -- scope of "stop repeating" label
- \%code
- ::stop_repeat::;
- end -- end of "stop repeating" label scope
- return {statements:%code, locals:%body_lua's "locals"}
- parse [repeat %body] as: repeat while (yes) %body
- parse [repeat until %condition %body] as: repeat while (not %condition) %body
-
- compile [..]
- repeat %n times %body
- ..to:
- %body_lua <- (%body as lua)
- %body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");")
- if %body has subtree % where
- ((%'s "type") = "FunctionCall") and ((%'s "stub") is "do next repeat")
- ..: %body_statements +<- "\n::continue_repeat::;"
- %code <- ".."
- for i=1,\(%n as lua expr) do
- \%body_statements
- end --numeric for-loop
- if %body has subtree % where:
- ((%'s "type") = "FunctionCall") and ((%'s "stub") is "stop repeating")
- ..:
- %code <- ".."
- do -- scope of "stop repeating" label
- \%code
- ::stop_repeat::;
- end -- end of "stop repeating" label scope
- return {statements:%code, locals:%body_lua's "locals"}
-
-# For loop control flow:
-immediately:
- compile [stop %var] to {..}
- statements:"goto stop_\(%var as lua identifier);"
- compile [do next %var] to {..}
- statements:"goto continue_\(%var as lua identifier);"
-
-# Numeric range for loops
-immediately:
- compile [..]
- for %var from %start to %stop by %step %body
- for %var from %start to %stop via %step %body
- ..to:
- %body_lua <- (%body as lua)
- %body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");")
- if %body has subtree % where:
- ((%'s "type") = "FunctionCall") and
- ((%'s "stub") is "do next %") and
- ((3rd in (%'s "value"))'s "value") is (%var's "value")
- ..: %body_statements +<- "\n::continue_\(%var as lua identifier)::;"
-
- # This uses Lua's approach of only allowing loop-scoped variables in a loop
- assume ((%var's "type") is "Var") or barf "Loop expected variable, not: \(%var's source code)"
- %code <- ".."
- for \(%var as lua expr)=\(%start as lua expr),\(%stop as lua expr),\(%step as lua expr) do
- \%body_statements
- end --numeric for-loop
-
- if %body has subtree % where:
- ((%'s "type") = "FunctionCall") and:
- ((%'s "stub") is "stop %") and:
- ((2nd in (%'s "value"))'s "value") is (%var's "value")
- ..:
- %code <- ".."
- do -- scope for stopping for-loop
- \%code
- ::stop_\(%var as lua identifier)::;
- end -- end of scope for stopping for-loop
-
- return {statements:%code, locals:%body_lua's "locals"}
-
-immediately:
- 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
-
-# For-each loop (lua's "ipairs()")
-immediately:
- compile [for %var in %iterable %body] to:
- %body_lua <- (%body as lua)
- %body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");")
- if %body has subtree % where:
- ((%'s "type") = "FunctionCall") and
- ((%'s "stub") is "do next %") and
- ((3rd in (%'s "value"))'s "value") is (%var's "value")
- ..: %body_statements +<- "\n::continue_\(%var as lua identifier)::;"
- # This uses Lua's approach of only allowing loop-scoped variables in a loop
- assume ((%var's "type") is "Var") or barf "Loop expected variable, not: \(%var's source code)"
- %code <- ".."
- for i,\(%var as lua expr) in ipairs(\(%iterable as lua expr)) do
- \%body_statements
- end --foreach-loop
- if %body has subtree % where:
- ((%'s "type") = "FunctionCall") and
- ((%'s "stub") is "stop %") and
- ((2nd in (%'s "value"))'s "value") is (%var's "value")
- ..:
- %code <- ".."
- do -- scope for stopping for-loop
- \%code
- ::stop_\(%var as lua identifier)::;
- end -- end of scope for stopping for-loop
- return {statements:%code, locals:%body_lua's "locals"}
-
- parse [for all %iterable %body] as: for % in %iterable %body
-
-# Dict iteration (lua's "pairs()")
-immediately:
- compile [for %key = %value in %iterable %body] to:
- %body_lua <- (%body as lua)
- %body_statements <- ((%body_lua's "statements") or "\(%body_lua's "expr");")
- if %body has subtree % where:
- ((%'s "type") = "FunctionCall") and
- ((%'s "stub") is "do next %") and
- ((3rd in (%'s "value"))'s "value") is (%key's "value")
- ..: %body_statements +<- "\n::continue_\(%key as lua identifier)::;"
-
- if %body has subtree % where:
- ((%'s "type") = "FunctionCall") and
- ((%'s "stub") is "do next %") and
- ((3rd in (%'s "value"))'s "value") is (%value's "value")
- ..: %body_statements +<- "\n::continue_\(%value as lua identifier)::;"
-
- # This uses Lua's approach of only allowing loop-scoped variables in a loop
- assume ((%key's "type") is "Var") or barf "Loop expected variable, not: \(%key's source code)"
- assume ((%value's "type") is "Var") or barf "Loop expected variable, not: \(%value's source code)"
- %code <- ".."
- for \(%key as lua expr),\(%value as lua expr) in pairs(\(%iterable as lua expr)) do
- \%body_statements
- end --foreach-loop
-
- %stop_labels <- ""
- if %body has subtree % where:
- ((%'s "type") = "FunctionCall") and
- ((%'s "stub") is "stop %") and
- ((2nd in (%'s "value"))'s "value") is (%key's "value")
- ..: %stop_labels +<- "\n::stop_\(%key as lua identifier)::;"
-
- if %body has subtree % where:
- ((%'s "type") = "FunctionCall") and
- ((%'s "stub") is "stop %") and
- ((2nd in (%'s "value"))'s "value") is (%value's "value")
- ..: %stop_labels +<- "\n::stop_\(%value as lua identifier)::;"
-
- if: %stop_labels is not ""
- %code <- ".."
- do -- scope for stopping for % = % loop
- \%code\%stop_labels
- end
- return {statements:%code, locals:%body_lua's "locals"}
-
-# Switch statement/multi-branch if
-immediately:
- compile [when %body] to:
- %code <- ""
- %fallthroughs <- []
- %locals <- []
- %is_first <- (yes)
- %seen_else <- (no)
- for %func_call in (%body's "value"):
- assume ((%func_call's "type") is "FunctionCall") or barf ".."
- Invalid format for 'when' statement. Only '*' blocks are allowed.
- with [..]
- %tokens <- (%func_call's "value")
- %star <- (1st in %tokens)
- %condition <- (2nd in %tokens)
- %action <- (3rd in %tokens)
- ..:
- assume (=lua "\%star and \%star.type == 'Word' and \%star.value == '*'") or barf ".."
- Invalid format for 'when' statement. Lines must begin with '*'
- assume %condition or barf ".."
- Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else"
- if: %action is (nil)
- lua> "table.insert(\%fallthroughs, \(%condition as lua expr));"
- do next %func_call
- %action <- (%action as lua)
- %action_statements <- ((%action's "statements") or "\(%action's "expr");")
- for %local in ((%action's "locals") or []):
- lua> "table.insert(\%locals, \%local);"
-
- if: =lua "\%condition.type == 'Word' and \%condition.value == 'else'"
- assume (not %is_first) or barf "'else' clause cannot be first in 'when % = ?' block"
- %code +<- ".."
-
- else
- \%action_statements
- %seen_else <- (yes)
- ..else:
- assume (not %seen_else) or barf "'else' clause needs to be last in 'when' block"
- lua> "table.insert(\%fallthroughs, \(%condition as lua expr));"
- %condition_code <- (%fallthroughs joined with " or ")
- %code +<- ".."
-
- \("if" if %is_first else "elseif") \%condition_code then
- \%action_statements
-
- %fallthroughs <- []
- %is_first <- (no)
-
- assume (%fallthroughs = []) or barf "Unfinished fallthrough conditions in 'when' block"
- if: %code is not ""
- %code +<- "\nend"
- lua> "utils.deduplicate(\%locals);"
- return {statements:%code, locals:%locals}
-
-# Switch statement
-immediately:
- compile [when %branch_value = ? %body, when %branch_value is ? %body] to:
- %code <- ""
- %fallthroughs <- []
- %locals <- []
- %is_first <- (yes)
- %seen_else <- (no)
- for %func_call in (%body's "value"):
- assume ((%func_call's "type") is "FunctionCall") or barf ".."
- Invalid format for 'when' statement. Only '*' blocks are allowed.
- %tokens <- (%func_call's "value")
- with [%star<-(1st in %tokens), %condition<-(2nd in %tokens), %action<-(3rd in %tokens)]:
- assume (=lua "\%star and \%star.type == 'Word' and \%star.value == '*'") or barf ".."
- Invalid format for 'when' statement. Lines must begin with '*'
- assume %condition or barf ".."
- Invalid format for 'when' statement. Lines must begin with '*' and have a condition or the word "else"
- if: %action is (nil)
- lua> "table.insert(\%fallthroughs, \(%condition as lua expr))"
- do next %func_call
-
- %action <- (%action as lua)
- %action_statements <- ((%action's "statements") or "\(%action's "expr");")
- for %local in ((%action's "locals") or []):
- lua> "table.insert(\%locals, \%local);"
-
- if: =lua "\%condition.type == 'Word' and \%condition.value == 'else'"
- assume (not %is_first) or barf "'else' clause cannot be first in 'when % = ?' block"
- %code +<- ".."
-
- else
- \%action_statements
- end
- %seen_else <- (yes)
- ..else:
- assume (not %seen_else) or barf "'else' clause needs to be last in 'when % = ?' block"
- lua> "table.insert(\%fallthroughs, \(%condition as lua expr));"
- for %i = % in %fallthroughs
- if: ((%'s "type") is "Text") or ((%'s "type") is "Number")
- (%i'th in %fallthroughs) <- "branch_value == \%"
- ..else
- (%i'th in %fallthroughs) <- "utils.equivalent(branch_value, \%)"
- %clause <- (%fallthroughs joined with " or ")
- %code +<- ".."
-
- \("if" if %is_first else "elseif") \%clause then
- \%action_statements
-
- %fallthroughs <- []
- %is_first <- (no)
-
- assume (%fallthroughs = []) or barf "Unfinished fallthrough conditions in 'when' block"
- assume (%code is not "") or barf "No body for 'when % = ?' block!"
- unless %seen_else
- %code +<- "\nend"
- %code <- ".."
- do --when % = ?
- local branch_value = \(%branch_value as lua expr);\
- ..\%code
- end --when % = ?
- lua> "utils.deduplicate(\%locals);"
- return {statements:%code, locals:%locals}
-
-# Try/except
-immediately:
- compile [..]
- try %action and if it succeeds %success or if it barfs %fallback
- try %action and if it barfs %fallback or if it succeeds %success
- ..to:
- %locals <- []
- %action_lua <- (%action as lua)
- %success_lua <- (%success as lua)
- %fallback_lua <- (%fallback as lua)
- %fallback <- (%fallback as lua)
- for %block in [%action_lua, %success_lua, %fallback_lua]:
- for %local in ((%block's "locals") or []):
- lua> "table.insert(\%locals, \%local);"
- lua> "utils.deduplicate(\%locals);"
- return {..}
- locals: %locals
- statements: ".."
- do
- local fell_through = false;
- local ok, ret = pcall(function()
- \((%action_lua's "statements") or "\(%action_lua's "expr");")
- fell_through = true;
- end);
- if ok then
- \((%success_lua's "statements") or "\(%success_lua's "expr");")
- end
- if not ok then
- \((%fallback_lua's "statements") or "\(%fallback_lua's "expr");")
- elseif not fell_through then
- return ret;
- end
- end
- parse [try %action] as:
- try %action and if it succeeds: do nothing
- ..or if it barfs: do nothing
- parse [try %action and if it barfs %fallback] as:
- try %action and if it succeeds: do nothing
- ..or if it barfs %fallback
- parse [try %action and if it succeeds %success] as:
- try %action and if it succeeds %success or if it barfs: do nothing
-
-# Do/finally:
-immediately:
- compile [do %action] to:
- %action <- (%action as lua)
- return {..}
- locals: %action's "locals"
- statements: ".."
- do
- \((%action's "statements") or "\(%action's "expr");")
- end
-
- compile [do %action then always %final_action] to:
- %action <- (%action as lua)
- %final_action <- (%final_action as lua)
- %locals <- []
- for %sub_locals in [%action's "locals", %final_action's "locals"]:
- for %local in %sub_locals:
- lua> "table.insert(\%locals, \%local);"
- lua> "utils.deduplicate(\%locals);"
- return {..}
- locals: %locals
- statements: ".."
- do
- local fell_through = false;
- local ok, ret1 = pcall(function()
- \((%action's "statements") or "\(%action's "expr");")
- fell_through = true;
- end);
- local ok2, ret2 = pcall(function()
- \((%final_action's "statements") or "\(%final_action's "expr");")
- end);
- if not ok then error(ret1); end
- if not ok2 then error(ret2); end
- if not fell_through then
- return ret1;
- end
- end
-
diff --git a/lib/core.nom b/lib/core.nom
deleted file mode 100644
index 8843104..0000000
--- a/lib/core.nom
+++ /dev/null
@@ -1,10 +0,0 @@
-#..
- This file imports all the commonly used library files, which can be convenient for
- avoiding retyping the whole list.
-
-use "lib/metaprogramming.nom"
-use "lib/text.nom"
-use "lib/operators.nom"
-use "lib/control_flow.nom"
-use "lib/math.nom"
-use "lib/collections.nom"
diff --git a/lib/file_hash.nom b/lib/file_hash.nom
new file mode 100644
index 0000000..4d99e39
--- /dev/null
+++ b/lib/file_hash.nom
@@ -0,0 +1,46 @@
+use "core"
+
+%hash_to_filename <- {}
+
+lua> ".."
+ local Hash = require("openssl.digest");
+ local function sha1(x)
+ local hash = Hash.new("sha1"):final(x);
+ local hex = hash:gsub('.', function(c) return string.format('%02x', string.byte(c)) end)
+ return hex
+ end
+ local lfs = require('lfs');
+ local function attrdir(path)
+ for filename in lfs.dir(path) do
+ if filename ~= "." and filename ~= ".." and filename:sub(1,1) ~= "." then
+ local filename = path..'/'..filename
+ local attr = lfs.attributes(filename);
+ if attr.mode == "directory" then
+ attrdir(filename);
+ elseif filename:match(".*%.nom") then
+ local file = io.open(filename);
+ local hash = sha1(file:read("*a"));
+ file:close();
+ \%hash_to_filename[hash] = filename
+ end
+ end
+ end
+ end
+ attrdir(".");
+
+action [sha1 %]:
+ lua> "return sha1(\%);"
+
+action [file with hash %hash]:
+ %file <- (%hash in %hash_to_filename)
+ assume %file or barf "File with SHA1 hash \%hash not found!"
+ return %file
+
+action [hash of file %filename]:
+ lua> ".."
+ local f = io.open(\%filename);
+ local hash = sha1(f:read("*a"));
+ f:close();
+ return hash;
+
+parse [use file with hash %hash] as: use (file with hash %hash)
diff --git a/lib/habit_breaker.nom b/lib/habit_breaker.nom
new file mode 100644
index 0000000..cc423e7
--- /dev/null
+++ b/lib/habit_breaker.nom
@@ -0,0 +1,54 @@
+use "core"
+
+immediately:
+ compile [correct %wrong to %right] to:
+ lua> ".."
+ local signature = {};
+ for i, action in ipairs(\%wrong.value) do signature[i] = action:get_src(); end
+ local stubs = nomsu:get_stubs_from_signature(signature);
+ local stub_args = nomsu:get_args_from_signature(signature);
+ local lua_fn_args = table.concat(stub_args[1], ", ");
+ local template;
+ if \%right.type == "Block" then
+ local lines = {};
+ for i, line in ipairs(\%right.value) do lines[i] = nomsu:dedent(line:get_src()); end
+ template = repr(table.concat(lines, "\\n"));
+ else
+ template = repr(nomsu:dedent(\%right:get_src()));
+ end
+ local replacements = {};
+ for i, a in ipairs(stub_args[1]) do replacements[i] = a.."="..a; end
+ replacements = "{"..table.concat(replacements, ", ").."}";
+ local def_tree = nomsu.compilestack[#nomsu.compilestack];
+ local code_location = ("%s:%s,%s"):format(def_tree.filename, def_tree.start, def_tree.stop);
+ return {statements=[[
+ nomsu:define_compile_action(]]..repr(signature)..[[, ]]..repr(code_location)..[[, function(]]..lua_fn_args..[[)
+ local template = nomsu:parse(]]..template..[[, ]]..repr(def_tree.filename)..[[);
+ local replacement = nomsu:tree_with_replaced_vars(template, ]]..replacements..[[);
+ error("Did you mean '"..nomsu:tree_to_nomsu(replacement).."'?");
+ end);
+ ]]};
+
+correct [%a == %b] to: %a = %b
+correct [%a = %b] to: %a <- %b
+correct [%a == %b] to: %a is %b
+correct [%a ~= %b, %a != %b, %a <> %b] to: %a is not %b
+correct [%a === %b] to: (%a's id) is (%b's id)
+correct [%a !== %b] to: (%a's id) is not (%b's id)
+correct [%a mod %b] to: %a wrapped around %b
+correct [function %names %body, def %names %body] to: action %names %body
+correct [switch %branch_value %body] to: when %branch_value = ? %body
+correct [None, Null] to: nil
+correct [True, true] to: yes
+correct [False, false] to: no
+correct [pass] to: do nothing
+correct [%a || %b] to: %a or %b
+correct [%a && %b] to: %a and %b
+correct [continue] to: do next
+correct [break] to: stop
+correct [let %thing = %value in %action] to: with [%thing <- %value] %action
+correct [print %] to: say %
+correct [error!, panic!, fail!, abort!] to: barf!
+correct [error %, panic %, fail %, abort %] to: barf %
+correct [assert %condition %message] to: assume %condition or barf %message
+correct [%cond ? %if_true %if_false] to: %if_true if %cond else %if_false
diff --git a/lib/math.nom b/lib/math.nom
deleted file mode 100644
index 70e7de4..0000000
--- a/lib/math.nom
+++ /dev/null
@@ -1,95 +0,0 @@
-#..
- This file defines some common math literals and functions
-
-use "lib/metaprogramming.nom"
-use "lib/text.nom"
-use "lib/operators.nom"
-use "lib/control_flow.nom"
-
-# Literals:
-compile [infinity, inf] to {expr:"math.huge"}
-compile [not a number, NaN, nan] to {expr:"(0/0)"}
-compile [pi, Pi, PI] to {expr:"math.pi"}
-compile [tau, Tau, TAU] to {expr:"(2*math.pi)"}
-compile [golden ratio] to {expr:"((1+math.sqrt(5))/2)"}
-compile [e] to {expr:"math.exp(1)"}
-
-# Functions:
-compile [% as a number] to {expr:"tonumber(\(% as lua expr))"}
-compile [absolute value %, | % |, abs %] to {expr:"math.abs(\(% as lua expr))"}
-compile [square root %, √%, sqrt %] to {expr:"math.sqrt(\(% as lua expr))"}
-compile [sine %, sin %] to {expr:"math.sin(\(% as lua expr))"}
-compile [cosine %, cos %] to {expr:"math.cos(\(% as lua expr))"}
-compile [tangent %, tan %] to {expr:"math.tan(\(% as lua expr))"}
-compile [arc sine %, asin %] to {expr:"math.asin(\(% as lua expr))"}
-compile [arc cosine %, acos %] to {expr:"math.acos(\(% as lua expr))"}
-compile [arc tangent %, atan %] to {expr:"math.atan(\(% as lua expr))"}
-compile [arc tangent %y/%x, atan2 %y %x] to {expr:"math.atan2(\(%y as lua expr), \(%x as lua expr))"}
-compile [hyperbolic sine %, sinh %] to {expr:"math.sinh(\(% as lua expr))"}
-compile [hyperbolic cosine %, cosh %] to {expr:"math.cosh(\(% as lua expr))"}
-compile [hyperbolic tangent %, tanh %] to {expr:"math.tanh(\(% as lua expr))"}
-compile [e^%, exp %] to {expr:"math.exp(\(% as lua expr))"}
-compile [natural log %, ln %, log %] to {expr:"math.log(\(% as lua expr))"}
-compile [log % base %base, log_%base %, log base %base %] to {expr:"math.log(\(% as lua expr), \(%base as lua expr))"}
-compile [floor %] to {expr:"math.floor(\(% as lua expr))"}
-compile [ceiling %, ceil %] to {expr:"math.ceil(\(% as lua expr))"}
-compile [round %, % rounded] to {expr:"math.floor(\(% as lua expr) + .5)"}
-action [%n to the nearest %rounder]:
- =lua "(\%rounder)*math.floor((\%n / \%rounder) + .5)"
-
-# Any/all/none
-compile [all of %items, all %items] to:
- unless: (%items' "type") is "List"
- return {expr:"utils.all(\(%items as lua expr))"}
- %clauses <- []
- for all (%items' "value"): lua> "table.insert(\%clauses, \(% as lua expr));"
- return {expr:"(\(%clauses joined with " and "))"}
-parse [not all of %items, not all %items] as: not (all of %items)
-compile [any of %items, any %items] to:
- unless: (%items' "type") is "List"
- return {expr:"utils.any(\(%items as lua expr))"}
- %clauses <- []
- for all (%items' "value"): lua> "table.insert(\%clauses, \(% as lua expr));"
- return {expr:"(\(%clauses joined with " or "))"}
-parse [none of %items, none %items] as: not (any of %items)
-compile [sum of %items, sum %items] to:
- unless: (%items' "type") is "List"
- return {expr:"utils.sum(\(%items as lua expr))"}
- %clauses <- []
- for all (%items' "value"): lua> "table.insert(\%clauses, \(% as lua expr));"
- return {expr:"(\(%clauses joined with " + "))"}
-compile [product of %items, product %items] to:
- unless: (%items' "type") is "List"
- return {expr:"utils.product(\(%items as lua expr))"}
- %clauses <- []
- for all (%items' "value"): lua> "table.insert(\%clauses, \(% as lua expr));"
- return {expr:"(\(%clauses joined with " * "))"}
-action [avg of %items, average of %items]:
- =lua "(utils.sum(\%items)/#\%items)"
-compile [min of %items, smallest of %items, lowest of %items] to {..}
- expr:"utils.min(\(%items as lua expr))"
-compile [max of %items, biggest of %items, largest of %items, highest of %items] to {..}
- expr:"utils.max(\(%items as lua expr))"
-compile [min of %items by %value_expr] to {..}
- expr: ".."
- utils.min(\(%items as lua expr), function(\(\% as lua expr))
- return \(%value_expr as lua expr)
- end)
-compile [max of %items by %value_expr] to {..}
- expr: ".."
- utils.max(\(%items as lua expr), function(\(\% as lua expr))
- return \(%value_expr as lua expr)
- end)
-
-# Random functions
-action [seed random with %]:
- lua> ".."
- math.randomseed(\%);
- for i=1,20 do math.random(); end
-parse [seed random] as: seed random with (=lua "os.time()")
-compile [random number, random, rand] to {expr:"math.random()"}
-compile [random int %n, random integer %n, randint %n] to {expr:"math.random(\(%n as lua expr))"}
-compile [random from %low to %high, random number from %low to %high, rand %low %high] to
- "math.random(\(%low as lua expr), \(%high as lua expr))"
-action [random choice from %elements, random choice %elements, random %elements]:
- =lua "\%elements[math.random(#\%elements)]"
diff --git a/lib/metaprogramming.nom b/lib/metaprogramming.nom
deleted file mode 100644
index 544ae59..0000000
--- a/lib/metaprogramming.nom
+++ /dev/null
@@ -1,200 +0,0 @@
-#..
- This File contains actions for making actions and compile-time actions and some helper
- functions to make that easier.
-
-# Compile-time action to make compile-time actions:
-immediately:
- lua> ".."
- nomsu:define_compile_action("compile %actions to %lua", \(!! code location !!), function(\%actions, \%lua)
- local signature = {};
- for i, action in ipairs(\%actions.value) do signature[i] = action:get_src(); end
- local stubs = nomsu:get_stubs_from_signature(signature);
- local stub_args = nomsu:get_args_from_signature(signature);
- local arg_set = {};
- for i, arg in ipairs(stub_args[1]) do arg_set[arg] = true; end
- if \%lua.type == "Text" then
- error("Invalid type for 'compile % to %', expected a dict with expr/statements, but got text.", 0);
- end
- local body_lua = nomsu:tree_to_lua(\%lua);
- local body_code = body_lua.statements or ("return "..body_lua.expr..";");
- local undeclared_locals = {};
- for i, body_local in ipairs(body_lua.locals or {}) do
- if not arg_set[body_local] then
- table.insert(undeclared_locals, body_local);
- end
- end
- if #undeclared_locals > 0 then
- body_code = "local "..table.concat(undeclared_locals, ", ")..";\\n"..body_code;
- end
- local lua_fn_args = table.concat(stub_args[1], ", ");
- local def_tree = nomsu.compilestack[#nomsu.compilestack];
- local code_location = ("%s:%s,%s"):format(def_tree.filename, def_tree.start, def_tree.stop);
- return {statements=([[
- nomsu:define_compile_action(]]..repr(signature)..[[, ]]..repr(code_location)..[[, function(]]..lua_fn_args..[[)
- ]]..body_code.."\\n"..[[
- end);
- ]])};
- end);
-
-# Compile-time action to make actions
-immediately:
- compile [action %actions %body] to:
- lua> ".."
- local signature = {};
- for i, action in ipairs(\%actions.value) do signature[i] = action:get_src(); end
- local stubs = nomsu:get_stubs_from_signature(signature);
- local stub_args = nomsu:get_args_from_signature(signature);
- local arg_set = {};
- for i, arg in ipairs(stub_args[1]) do arg_set[arg] = true; end
- local body_lua = nomsu:tree_to_lua(\%body);
- local body_code = body_lua.statements or ("return "..body_lua.expr..";");
- local undeclared_locals = {};
- for i, body_local in ipairs(body_lua.locals or {}) do
- if not arg_set[body_local] then
- table.insert(undeclared_locals, body_local);
- end
- end
- if #undeclared_locals > 0 then
- body_code = "local "..table.concat(undeclared_locals, ", ")..";\\n"..body_code;
- end
- local lua_fn_args = table.concat(stub_args[1], ", ");
- local def_tree = nomsu.compilestack[#nomsu.compilestack];
- local code_location = ("%s:%s,%s"):format(def_tree.filename, def_tree.start, def_tree.stop);
- return {statements=[[
- nomsu:define_action(]]..repr(signature)..[[, ]]..repr(code_location)..[[, function(]]..lua_fn_args..[[)
- ]]..body_code.."\\n"..[[
- end);
- ]]};
-
-# Macro to make nomsu macros:
-immediately:
- compile [parse %shorthand as %longhand] to:
- lua> ".."
- local signature = {};
- for i, action in ipairs(\%shorthand.value) do signature[i] = action:get_src(); end
- local stubs = nomsu:get_stubs_from_signature(signature);
- local stub_args = nomsu:get_args_from_signature(signature);
- local lua_fn_args = table.concat(stub_args[1], ", ");
- local template;
- if \%longhand.type == "Block" then
- local lines = {};
- for i, line in ipairs(\%longhand.value) do lines[i] = nomsu:dedent(line:get_src()); end
- template = repr(table.concat(lines, "\\n"));
- else
- template = repr(nomsu:dedent(\%longhand:get_src()));
- end
- local replacements = {};
- for i, a in ipairs(stub_args[1]) do replacements[i] = a.."="..a; end
- replacements = "{"..table.concat(replacements, ", ").."}";
- local def_tree = nomsu.compilestack[#nomsu.compilestack];
- local code_location = ("%s:%s,%s"):format(def_tree.filename, def_tree.start, def_tree.stop);
- return {statements=[[
- nomsu:define_compile_action(]]..repr(signature)..[[, ]]..repr(code_location)..[[, function(]]..lua_fn_args..[[)
- local template = nomsu:parse(]]..template..[[, ]]..repr(def_tree.filename)..[[);
- local replacement = nomsu:tree_with_replaced_vars(template, ]]..replacements..[[);
- return nomsu:tree_to_lua(replacement);
- end);
- ]]};
-
-action [remove action %stub]:
- lua> ".."
- local fn = ACTION[\%stub];
- local metadata = ACTION_METADATA[fn];
- for i=#metadata.aliases,1,-1 do
- metadata.arg_orders[metadata.aliases[i]] = nil;
- table.remove(metadata.aliases, i);
- end
- ACTION[\%stub] = nil;
-
-immediately:
- action [%tree as lua]:
- =lua "nomsu:tree_to_lua(\%tree)"
-
- action [%tree as lua expr]:
- lua> ".."
- local lua = nomsu:tree_to_lua(\%tree);
- if lua.locals or not lua.expr then
- error("Invalid thing to convert to lua expr: "..\%tree:get_src());
- end
- return lua.expr;
-
- action [%tree as lua statements]:
- lua> ".."
- local lua = nomsu:tree_to_lua(\%tree);
- local code = lua.statements or (lua.expr..";");
- if lua.locals then
- code = "local "..table.concat(lua.locals, ", ")..";\\n"..code;
- end
- return code;
-
- action [%tree as value]:
- =lua "nomsu:tree_to_value(\%tree)"
-
-immediately:
- compile [%tree's source code, %tree' source code] to {expr:"\(%tree as lua expr):get_src()"}
-
- compile [repr %obj] to {expr:"repr(\(%obj as lua expr))"}
- compile [type of %obj] to {expr:"type(\(%obj as lua expr))"}
-
-immediately:
- compile [nomsu] to {expr:"nomsu"}
- compile [%var as lua identifier] to {expr:"nomsu:var_to_lua_identifier(\(%var as lua expr))"}
-
-action [action %names metadata]:
- =lua "ACTION_METADATA[ACTION[\%names]]"
-
-# Get the source code for a function
-action [help %action]:
- lua> ".."
- local metadata = \(action %action metadata);
- if not metadata then
- print("Action not found: "..repr(\%action));
- else
- print(metadata.src or "<unknown source code>");
- end
-
-# Compiler tools
-immediately:
- compile [run %code] to {expr: "nomsu:run(\(%code as lua expr), '\(!! code location !!)')"}
- parse [enable debugging] as: lua> "nomsu.debug = true;"
- parse [disable debugging] as: lua> "nomsu.debug = false;"
-
-immediately:
- compile [say %message] to:
- lua> ".."
- if \%message.type == "Text" then
- return {statements="print("..\(%message as lua expr)..");"};
- else
- return {statements="print(stringify("..\(%message as lua expr).."));"};
- end
-
-# Return
-immediately:
- #.. Return statement is wrapped in a do..end block because Lua is unhappy if you
- put code after a return statement, unless you wrap it in a block.
- compile [return] to {statements:"do return; end"}
- compile [return %return_value] to {statements:"do return \(%return_value as lua expr); end"}
-
-# Error functions
-immediately:
- compile [barf] to {statements:"error(nil, 0);"}
- compile [barf %msg] to {statements:"error(\(%msg as lua expr), 0);"}
- compile [assume %condition] to:
- lua> "local \%assumption = 'Assumption failed: '..\%condition:get_src();"
- return {..}
- statements:".."
- if not \(%condition as lua expr) then
- error(\(repr %assumption), 0);
- end
- compile [assume %condition or barf %msg] to {..}
- statements:".."
- if not \(%condition as lua expr) then
- error(\(%msg as lua expr), 0);
- end
-
-# Literals
-immediately:
- compile [yes] to {expr:"true"}
- compile [no] to {expr:"false"}
- compile [nothing, nil, null] to {expr:"nil"}
-
diff --git a/lib/object.nom b/lib/object.nom
index 4d8932a..2f2f3ff 100644
--- a/lib/object.nom
+++ b/lib/object.nom
@@ -1,4 +1,4 @@
-use "lib/core.nom"
+use "core"
compile [@%var] to:
lua> ".."
diff --git a/lib/object2.nom b/lib/object2.nom
new file mode 100644
index 0000000..5e77a05
--- /dev/null
+++ b/lib/object2.nom
@@ -0,0 +1,135 @@
+use "core"
+
+compile [@] to {expr:"self"}
+
+compile [@%var] to:
+ lua> ".."
+ local key_lua = repr(\%var.value);
+ local key_attr = (key_lua:match("'([a-zA-Z][a-zA-Z0-9]*)'")
+ or key_lua:match('"([a-zA-Z][a-zA-Z0-9]*)"'));
+ if key_attr then
+ return {expr="self."..key_attr};
+ elseif key_lua:sub(1,1) == "[" then
+ key_lua = " "..key_lua.." ";
+ end
+ return {expr="self["..key_lua.."]"};
+
+compile [@%var <- %val] to:
+ lua> ".."
+ local val_lua = \(%val as lua expr);
+ local key_lua = repr(\%var.value);
+ local key_attr = (key_lua:match("'([a-zA-Z][a-zA-Z0-9]*)'")
+ or key_lua:match('"([a-zA-Z][a-zA-Z0-9]*)"'));
+ if key_attr then
+ return {statements="self."..key_attr.." = "..val_lua..";"};
+ elseif key_lua:sub(1,1) == "[" then
+ key_lua = " "..key_lua.." ";
+ end
+ return {statements="self["..key_lua.."] = "..val_lua..";"};
+
+compile [as %instance %body] to:
+ %body_lua <- (%body as lua)
+ return {..}
+ statements: ".."
+ do
+ local self = \(%instance as lua expr);
+ local global_actions = ACTION;
+ local ACTION = setmetatable({}, {__index=function(_,key)
+ local method = self[key];
+ if method then return (function(...) return method(self, ...); end); end
+ return global_actions[key];
+ end});
+ \((%body_lua's "statements") or "\(%body_lua's "expr");")
+ end
+ locals: %body_lua's "locals"
+
+compile [define object %classname %class_body] to:
+ %class_identifier <- (=lua "nomsu:var_to_lua_identifier(\(%classname as value)):sub(2,-1)")
+ if: %class_identifier is ""
+ %class_identifier <- "class"
+ %methods <- []
+ for %line in (%class_body's "value"):
+ if: (%line's "type") is "Comment"
+ do next %line
+ assume (((%line's "type") == "FunctionCall") and ((%line's "stub") == "action % %"))
+ ..or barf "Only action definitions are supported inside 'define object % %', not \(%line's "src")"
+ %actions <- (2nd in (%line's "value"))
+ %body <- (3rd in (%line's "value"))
+ lua> ".."
+ local signature = {};
+ for i, action in ipairs(\%actions.value) do signature[i] = action:get_src(); end
+ local stubs = nomsu:get_stubs_from_signature(signature);
+ local stub_args = nomsu:get_args_from_signature(signature);
+ local arg_set = {};
+ for i, arg in ipairs(stub_args[1]) do arg_set[arg] = true; end
+ local body_lua = nomsu:tree_to_lua(\%body);
+ local body_code = body_lua.statements or ("return "..body_lua.expr..";");
+ local undeclared_locals = {};
+ for i, body_local in ipairs(body_lua.locals or {}) do
+ if not arg_set[body_local] then
+ table.insert(undeclared_locals, body_local);
+ end
+ end
+ if #undeclared_locals > 0 then
+ body_code = "local "..table.concat(undeclared_locals, ", ")..";\\n"..body_code;
+ end
+ local lua_fn_args = table.concat({"self", unpack(stub_args[1])}, ", ");
+ local def_tree = nomsu.compilestack[#nomsu.compilestack];
+ local code_location = ("%s:%s,%s"):format(def_tree.filename, def_tree.start, def_tree.stop);
+
+ local compiled_args = {};
+ for i, arg in ipairs(stub_args[1]) do
+ compiled_args[i] = "nomsu:tree_to_lua("..arg..").expr";
+ end
+ compiled_args = table.concat(compiled_args, "..', '..");
+ table.insert(\%methods, ([==[
+ %s[ %s] = function(%s)
+ %s
+ end
+ ]==]):format(
+ \%class_identifier, repr(stubs[1]), lua_fn_args,
+ body_code));
+
+ return {..}
+ statements:".."
+ do -- \%class_identifier
+ -- Create the class object:
+ local \%class_identifier = setmetatable({
+ name=\(%classname as lua expr), instances=setmetatable({}, {__mode="k"}),
+ }, {
+ __tostring=function(c) return c.name; end,
+ __call=function(cls, inst)
+ inst = inst or {};
+ inst.id = tostring(inst):match('table: (.*)');
+ setmetatable(inst, cls.instance_metatable);
+ cls.instances[inst] = true;
+ if inst['set % up'] then
+ inst['set % up'](inst);
+ end
+ return inst;
+ end,
+ });
+ \%class_identifier.class = \%class_identifier;
+
+ -- Define the methods:
+ \(%methods joined with "\n")
+
+ -- Define class methods for instantiating and accessing instances:
+ \%class_identifier.instance_metatable = {
+ __index=\%class_identifier,
+ __tostring=\%class_identifier['% as text'] or function(inst)
+ return "<"..inst.class.name..": "..inst.id..">";
+ end,
+ };
+ nomsu:define_action("instances of "..\%class_identifier.name, "lib/class.nom", function()
+ return utils.keys(\%class_identifier.instances);
+ end, "");
+ nomsu:define_action("new "..\%class_identifier.name.." %instance", "lib/class.nom", function(_instance)
+ return \%class_identifier(_instance);
+ end, "");
+ nomsu:define_action("new "..\%class_identifier.name, "lib/class.nom", function()
+ return \%class_identifier({});
+ end, "");
+ end -- End of definition of \%class_identifier
+ \("\n\n")
+
diff --git a/lib/operators.nom b/lib/operators.nom
deleted file mode 100644
index 5d5a7a8..0000000
--- a/lib/operators.nom
+++ /dev/null
@@ -1,209 +0,0 @@
-#..
- This file contains definitions of operators like "+" and "and".
-
-use "lib/metaprogramming.nom"
-
-# Indexing:
-immediately:
- #.. NOTE!!! It's critical that there are spaces around %key if it's a string,
- otherwise, Lua will get confused and interpret %obj[[[foo]]] as %obj("[foo]")
- instead of %obj[ "foo" ].
- It's also critical to have parens around %obj, otherwise Lua is too dumb to
- realize that {x=1}["x"] is the same as ({x=1})["x"] or that
- {x=1}.x is the same as ({x=1}).x
- compile [..]
- %obj' %key, %obj's %key, %key in %obj, %key'th in %obj, %key of %obj,
- %key st in %obj, %key nd in %obj, %key rd in %obj, %key th in %obj,
- ..to:
- lua> ".."
- local obj_lua = \(%obj as lua expr);
- if not obj_lua:sub(-1,-1):match("[a-zA-Z)]") then
- obj_lua = "("..obj_lua..")";
- end
- local key_lua = \(%key as lua expr);
- local key_attr = (key_lua:match("'([a-zA-Z][a-zA-Z0-9]*)'")
- or key_lua:match('"([a-zA-Z][a-zA-Z0-9]*)"'));
- if key_attr then
- return {expr=obj_lua.."."..key_attr};
- elseif key_lua:sub(1,1) == "[" then
- key_lua = " "..key_lua.." ";
- end
- return {expr=obj_lua.."["..key_lua.."]"};
-
-# Comparison Operators
-immediately:
- compile [%x < %y] to {expr:"(\(%x as lua expr) < \(%y as lua expr))"}
- compile [%x > %y] to {expr:"(\(%x as lua expr) > \(%y as lua expr))"}
- compile [%x <= %y] to {expr:"(\(%x as lua expr) <= \(%y as lua expr))"}
- compile [%x >= %y] to {expr:"(\(%x as lua expr) >= \(%y as lua expr))"}
- # TODO: optimize case of [%x,%y] = [1,2]
- compile [%a is %b, %a = %b, %a == %b] to:
- lua> ".."
- local safe = {Text=true, Number=true};
- local a_lua, b_lua = nomsu:tree_to_lua(\%a).expr, nomsu:tree_to_lua(\%b).expr;
- if safe[\%a.type] or safe[\%b.type] then
- return {expr="("..a_lua.." == "..b_lua..")"};
- else
- return {expr="utils.equivalent("..a_lua..", "..b_lua..")"};
- end
- compile [%a isn't %b, %a is not %b, %a not= %b, %a != %b] to:
- lua> ".."
- local safe = {Text=true, Number=true};
- local a_lua, b_lua = nomsu:tree_to_lua(\%a).expr, nomsu:tree_to_lua(\%b).expr;
- if safe[\%a.type] or safe[\%b.type] then
- return {expr="("..a_lua.." ~= "..b_lua..")"};
- else
- return {expr="(not utils.equivalent("..a_lua..", "..b_lua.."))"};
- end
- # For strict identity checking, use (%x's id) is (%y's id)
- compile [%'s id, id of %] to {expr:"nomsu.ids[\(% as lua expr)]"}
-
-# Variable assignment operator
-immediately:
- compile [%var <- %value] to:
- lua> "local \%var_lua = nomsu:tree_to_lua(\%var);"
- assume (%var_lua's "expr") or barf "Invalid target for assignment: \(%var's source code)"
- lua> "local \%value_lua = nomsu:tree_to_lua(\%value);"
- assume (%value_lua's "expr") or barf "Invalid value for assignment: \(%value's source code)"
- return {..}
- statements:"\(%var_lua's "expr") = \(%value_lua's "expr");"
- locals: =lua "(\%var.type == 'Var' and {\%var_lua.expr} or nil)"
-
-immediately:
- # Simultaneous mutli-assignments like: x,y,z = 1,x,3;
- compile [<- %assignments] to:
- %locals <- []
- %targets <- []
- %values <- []
- assume ((%assignments' "type") is "Dict") or barf ".."
- Expected a Dict for the assignments part of '<- %' statement, not \(%assignments' source code)
- lua> ".."
- for i, item in ipairs(\%assignments.value) do
- local target, value = item.dict_key, item.dict_value;
- local target_lua = nomsu:tree_to_lua(target);
- if not target_lua.expr then error("Invalid target for assignment: "..target:get_src()); end
- local value_lua = nomsu:tree_to_lua(value);
- if not value_lua.expr then error("Invalid value for assignment: "..value:get_src()); end
- if target.type == "Var" then
- table.insert(\%locals, target_lua.expr);
- end
- table.insert(\%targets, target_lua.expr);
- table.insert(\%values, value_lua.expr);
- end
- utils.deduplicate(\%locals);
- return {locals=\%locals, statements=(table.concat(\%targets, ", ").." = "..table.concat(\%values, ", ")..";")};
-
-immediately:
- compile [export %var <- %value] to:
- %var_lua <- (%var as lua)
- assume (%var_lua's "expr") or barf "Invalid target for assignment: \(%var's source code)"
- %value_lua <- (%value as lua)
- assume (%value_lua's "expr") or barf "Invalid value for assignment: \(%value's source code)"
- return {statements:"\(%var_lua's "expr") = \(%value_lua's "expr");"}
-
- compile [exporting %exported %body] to:
- %body_lua <- (%body as lua)
- %leftover_locals <- (=lua "{unpack(\%body_lua.locals or {})}")
- assume ((%exported's "type") = "List") or barf ".."
- Expected a List for the export part of 'exporting' statement, not \(%exported's source code)
- lua> ".."
- for i, item in ipairs(\%exported.value) do
- if item.type ~= "Var" then
- error("'exporting' statement expects Vars, not: "..item:get_src());
- end
- local var = nomsu:tree_to_lua(item).expr;
- utils.remove_from_list(\%leftover_locals, var);
- end
- return {locals:%leftover_locals, statements:=lua "\%body_lua.statements or (\%body_lua.expr..';')"}
-
- compile [with %assignments %body] to:
- %body_lua <- (%body as lua)
- %locals <- []
- %declarations <- []
- %leftover_locals <- (=lua "{unpack(\%body_lua.locals or {})}")
- assume ((%assignments' "type") is "List") or barf ".."
- Expected a List for the assignments part of 'with' statement, not \(%assignments' source code)
- lua> ".."
- for i, item in ipairs(\%assignments.value) do
- if item.type == "Var" then
- local var = nomsu:tree_to_lua(item).expr;
- utils.remove_from_list(\%leftover_locals, var);
- table.insert(\%locals, var);
- else
- if not (item.type == "FunctionCall" and #item.value == 3 and item.value[2].value == "<-") then
- error("'with' statement expects entries of the form: '%var <- %value', not: "..item:get_src());
- end
- local target, value = item.value[1], item.value[3];
- local target_lua = nomsu:tree_to_lua(target);
- if not target_lua.expr then error("Invalid target for assignment: "..target:get_src()); end
- local value_lua = nomsu:tree_to_lua(value);
- if not value_lua.expr then error("Invalid value for assignment: "..value:get_src()); end
- if target.type == "Var" then
- utils.remove_from_list(\%leftover_locals, target_lua.expr);
- end
- table.insert(\%declarations, (("local %s = %s;\\n "):format(
- target_lua.expr, value_lua.expr)));
- end
- end
- local locals_code = "";
- if #\%locals > 0 then
- locals_code = "\\nlocal "..table.concat(\%locals, ", ")..";";
- end
- local declaration_code = "";
- if #\%declarations > 0 then
- declaration_code = "\\n"..table.concat(\%declarations, "\\n");
- end
- return {locals=\%leftover_locals, statements=([[
- do%s%s
- %s
- end]]):format(locals_code, declaration_code, \%body_lua.statements or (\%body_lua.expr..";"))};
-
-immediately:
- # Math Operators
- compile [%x + %y] to {expr:"(\(%x as lua expr) + \(%y as lua expr))"}
- compile [%x - %y] to {expr:"(\(%x as lua expr) - \(%y as lua expr))"}
- compile [%x * %y] to {expr:"(\(%x as lua expr) * \(%y as lua expr))"}
- compile [%x / %y] to {expr:"(\(%x as lua expr) / \(%y as lua expr))"}
- compile [%x ^ %y] to {expr:"(\(%x as lua expr) ^ \(%y as lua expr))"}
- compile [%x wrapped around %y, %x mod %y] to {expr:"(\(%x as lua expr) % \(%y as lua expr))"}
-
- # 3-part chained comparisons
- # (uses a lambda to avoid re-evaluating middle value, while still being an expression)
- parse [%x < %y < %z] as: =lua "(function(x,y,z) return x < y and y < z; end)(\%x,\%y,\%z)"
- parse [%x <= %y < %z] as: =lua "(function(x,y,z) return x <= y and y < z; end)(\%x,\%y,\%z)"
- parse [%x < %y <= %z] as: =lua "(function(x,y,z) return x < y and y <= z; end)(\%x,\%y,\%z)"
- parse [%x <= %y <= %z] as: =lua "(function(x,y,z) return x <= y and y <= z; end)(\%x,\%y,\%z)"
- parse [%x > %y > %z] as: =lua "(function(x,y,z) return x > y and y > z; end)(\%x,\%y,\%z)"
- parse [%x >= %y > %z] as: =lua "(function(x,y,z) return x >= y and y > z; end)(\%x,\%y,\%z)"
- parse [%x > %y >= %z] as: =lua "(function(x,y,z) return x > y and y >= z; end)(\%x,\%y,\%z)"
- parse [%x >= %y >= %z] as: =lua "(function(x,y,z) return x >= y and y >= z; end)(\%x,\%y,\%z)"
- # TODO: optimize for common case where x,y,z are all either variables or number literals
-
- # Boolean Operators
- compile [%x and %y] to {expr:"(\(%x as lua expr) and \(%y as lua expr))"}
- compile [%x or %y] to {expr:"(\(%x as lua expr) or \(%y as lua expr))"}
-
- # Bitwise Operators
- compile [%a OR %b, %a | %b] to {expr:"bit32.bor(\(%a as lua expr), \(%b as lua expr))"}
- compile [%a XOR %b] to {expr:"bit32.bxor(\(%a as lua expr), \(%b as lua expr))"}
- compile [%a AND %b, %a & %b] to {expr:"bit32.band(\(%a as lua expr), \(%b as lua expr))"}
- compile [NOT %, ~ %] to {expr:"bit32.bnot(\(% as lua expr))"}
- compile [%x LSHIFT %shift, %x << %shift] to {expr:"bit32.lshift(\(%x as lua expr), \(%shift as lua expr))"}
- compile [%x RSHIFT %shift, %x >>> %shift] to {expr:"bit32.rshift(\(%x as lua expr), \(%shift as lua expr))"}
- compile [%x ARSHIFT %shift, %x >> %shift] to {expr:"bit32.arshift(\(%x as lua expr), \(%shift as lua expr))"}
- # TODO: implement OR, XOR, AND for multiple operands?
-
- # Unary operators
- compile [- %] to {expr:"(- \(% as lua expr))"}
- compile [not %] to {expr:"(not \(% as lua expr))"}
-
-# Update operators
-immediately:
- parse [%var + <- %, %var +<- %] as: %var <- (%var + %)
- parse [%var - <- %, %var -<- %] as: %var <- (%var - %)
- parse [%var * <- %, %var *<- %] as: %var <- (%var * %)
- parse [%var / <- %, %var /<- %] as: %var <- (%var / %)
- parse [%var ^ <- %, %var ^<- %] as: %var <- (%var ^ %)
- parse [%var and <- %] as: %var <- (%var and %)
- parse [%var or <- %] as: %var <- (%var or %)
- parse [wrap %var around %] as: %var <- (%var wrapped around %)
diff --git a/lib/text.nom b/lib/text.nom
deleted file mode 100644
index 2a111fe..0000000
--- a/lib/text.nom
+++ /dev/null
@@ -1,56 +0,0 @@
-#..
- This file contains some definitions of text escape sequences, including ANSI console
- color codes.
-
-use "lib/metaprogramming.nom"
-
-# Text functions
-action [%texts joined with %glue]:
- lua> ".."
- local text_bits = {}
- for i,bit in ipairs(\%texts) do text_bits[i] = stringify(bit) end
- return table.concat(text_bits, \%glue)
-parse [joined %texts, %texts joined] as: %texts joined with ""
-
-compile [capitalized %text, %text capitalized] to {..}
- expr:"((\(%text as lua expr)):gsub('%l', string.upper, 1))"
-
-compile [%text with %sub instead of %patt, %text s/%patt/%sub] to {..}
- expr:"((\(%text as lua expr)):gsub(\(%patt as lua expr), \(%sub as lua expr)))"
-
-# TODO: figure out whether indent/dedent should affect first line
-compile [indented %text, %text indented] to {expr:"((\%text):gsub('\\n','\\n'..(' ')))"}
-compile [dedented %obj, %obj dedented] to {expr:"nomsu:dedent(\(%obj as lua expr))"}
-compile [%text indented %n times] to {expr:"((\%text):gsub('\\n','\\n'..(' '):rep(\%n)))"}
-
-# Text literals
-lua> ".."
- do
- local escapes = {
- nl="\\\\n", newline="\\\\n", tab="\\\\t", bell="\\\\a", cr="\\\\r", ["carriage return"]="\\\\r",
- backspace="\\\\b", ["form feed"]="\\\\f", formfeed="\\\\f", ["vertical tab"]="\\\\v",
- };
- for name, e in pairs(escapes) do
- local lua = "'"..e.."'";
- nomsu:define_compile_action(name, \(!! code location !!), function() return {expr=lua}; end);
- end
- local colors = {
- ["reset color"]="\\\\27[0m", bright="\\\\27[1m", dim="\\\\27[2m", underscore="\\\\27[4m",
- blink="\\\\27[5m", inverse="\\\\27[7m", hidden="\\\\27[8m",
-
- black="\\\\27[30m", red="\\\\27[31m", green="\\\\27[32m", yellow="\\\\27[33m", blue="\\\\27[34m",
- magenta="\\\\27[35m", cyan="\\\\27[36m", white="\\\\27[37m",
-
- ["on black"]="\\\\27[40m", ["on red"]="\\\\27[41m", ["on green"]="\\\\27[42m", ["on yellow"]="\\\\27[43m",
- ["on blue"]="\\\\27[44m", ["on magenta"]="\\\\27[45m", ["on cyan"]="\\\\27[46m", ["on white"]="\\\\27[47m",
- };
- for name, c in pairs(colors) do
- local color = "'"..c.."'";
- local reset = "'"..colors["reset color"].."'";
- nomsu:define_compile_action(name, \(!! code location !!), function() return {expr=color}; end);
- nomsu:define_compile_action(name.." %", \(!! code location !!), function(\%)
- return {expr=color..".."..nomsu:tree_to_lua(\%).expr..".."..reset};
- end);
- end
- end
-
diff --git a/lib/training_wheels.nom b/lib/training_wheels.nom
index 30c20fc..e5c02fb 100644
--- a/lib/training_wheels.nom
+++ b/lib/training_wheels.nom
@@ -2,7 +2,7 @@
This file contains a set of definitions that bring some familiar language features
from other languages into nomsu (e.g. "==" and "continue")
-use "lib/core.nom"
+use "core"
parse [%a = %b] as: %a <- %b
parse [%a == %b] as: %a is %b