diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2019-01-14 15:42:48 -0800 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2019-01-14 15:43:24 -0800 |
| commit | c1c32688a4afc43f6addb99b8b5fa878944a70e3 (patch) | |
| tree | c886f21b5b08a9053aa74fcba4b241dae5ede76d /core | |
| parent | 2309b696fc34b24f05f6658b94f9105ca8ee76e4 (diff) | |
Overhaul in progress, mostly working. Moved all the nomsu packages into
lib/, including core/*. Changes to how nomsu environments and importing
work.
Diffstat (limited to 'core')
| -rw-r--r-- | core/collections.nom | 145 | ||||
| -rw-r--r-- | core/control_flow.nom | 489 | ||||
| -rw-r--r-- | core/coroutines.nom | 40 | ||||
| -rw-r--r-- | core/errors.nom | 130 | ||||
| -rw-r--r-- | core/id.nom | 66 | ||||
| -rw-r--r-- | core/init.nom | 11 | ||||
| -rw-r--r-- | core/io.nom | 29 | ||||
| -rw-r--r-- | core/math.nom | 212 | ||||
| -rw-r--r-- | core/metaprogramming.nom | 454 | ||||
| -rw-r--r-- | core/operators.nom | 263 | ||||
| -rw-r--r-- | core/text.nom | 78 |
11 files changed, 0 insertions, 1917 deletions
diff --git a/core/collections.nom b/core/collections.nom deleted file mode 100644 index 4cf54cd..0000000 --- a/core/collections.nom +++ /dev/null @@ -1,145 +0,0 @@ -#!/usr/bin/env nomsu -V6.14 -# - This file contains code that supports manipulating and using collections like lists - and dictionaries. - -use "core/metaprogramming" -use "core/control_flow" -use "core/operators" - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -# List functionality: -test: - $list = [1, 2, 3, 4, 5] - $visited = {} - for $i = $x in $list: - $visited.$i = (yes) - assume ($visited == {.1, .2, .3, .4, .5}) - $visited = {} - for $x in $list: - $visited.$x = (yes) - assume ($visited == {.1, .2, .3, .4, .5}) - assume (($list, 2 nd to last) == 4) - assume (($list, first) == 1) - assume ($list, has 3) - assume (($list, index of 3) == 3) - assume ((size of $list) == 5) - $list, add 6 - assume (($list, last) == 6) - $list, pop - assume (($list, last) == 5) - $list, remove index 1 - assume (($list, first) == 2) - assume (([1, 2] + [3, 4]) == [1, 2, 3, 4]) - -# Dict functionality -test: - $dict = {.x = 1, .y = 2, .z = 3} - assume (size of $dict) == 3 - assume [: for $k = $v in {.x = 1}: add {.key = $k, .value = $v}] == - [{.key = "x", .value = 1}] - assume ({.x = 1, .y = 1} + {.y = 10, .z = 10}) == {.x = 1, .y = 11, .z = 10} - assume ({.x = 1, .y = 1} | {.y = 10, .z = 10}) == {.x = 1, .y = 1, .z = 10} - assume ({.x = 1, .y = 1} & {.y = 10, .z = 10}) == {.y = 1} - assume ({.x = 1, .y = 1} ~ {.y = 10, .z = 10}) == {.x = 1, .z = 10} - -test: - assume (([[1, 2], [3, 4]] flattened) == [1, 2, 3, 4]) - -externally ($lists flattened) means: - $flat = [] - for $item in recursive $lists: - if ($item is a "List"): - for $ in $item: - recurse $item on $ - ..else: - $flat, add $item - return $flat - -test: - assume ((entries in {.x = 1}) == [{.key = "x", .value = 1}]) - -(entries in $dict) parses as [ - : for $k = $v in $dict: - add {.key = $k, .value = $v} -] - -test: - assume ((keys in {.x = 1}) == ["x"]) - -[keys in $dict, keys of $dict] all parse as [: for $k = $v in $dict: add $k] -test: - assume ((values in {.x = 1}) == [1]) -[values in $dict, values of $dict] all parse as [: for $k = $v in $dict: add $v] - -# Metatable stuff -test: - $t = {} - set $t's metatable to {.__tostring = ($ -> "XXX")} - assume ("\$t" == "XXX") - -(set $dict's metatable to $metatable) compiles to - "setmetatable(\($dict as lua expr), \($metatable as lua expr));" - -[$'s metatable, $'metatable] all compile to "getmetatable(\($ as lua expr))" -test: - assume (({} with fallback $ -> ($ + 1)).10 == 11) - -($dict with fallback $key -> $value) compiles to (" - (function(d) - local mt = {} - for k,v in pairs(getmetatable(d) or {}) do mt[k] = v end - mt.__index = function(self, \($key as lua expr)) - local value = \($value as lua expr) - self[\($key as lua expr)] = value - return value - end - return setmetatable(d, mt) - end)(\($dict as lua expr)) -") - -# Sorting -test: - $x = [3, 1, 2] - sort $x - assume ($x == [1, 2, 3]) - sort $x by $ = (- $) - assume ($x == [3, 2, 1]) - $keys = {.1 = 999, .2 = 0, .3 = 50} - sort $x by $ = $keys.$ - assume ($x == [2, 3, 1]) -(sort $items) compiles to "table.sort(\($items as lua expr));" -[sort $items by $item = $key_expr, sort $items by $item -> $key_expr] -..all parse as - do: - $keys = ({} with fallback $item -> $key_expr) - lua> "table.sort(\$items, function(x,y) return \$keys[x] < \$keys[y] end)" - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -test: - assume ((sorted [3, 1, 2]) == [1, 2, 3]) - -externally [$items sorted, sorted $items] all mean: - $copy = [: for $ in $items: add $] - sort $copy - return $copy - -[$items sorted by $item = $key, $items sorted by $item -> $key] all parse as - result of: - $copy = [: for $ in $items: add $] - sort $copy by $item = $key - return $copy - -test: - assume ((unique [1, 2, 1, 3, 2, 3]) == [1, 2, 3]) - -externally (unique $items) means: - $unique = [] - $seen = {} - for $ in $items: - unless $seen.$: - $unique, add $ - $seen.$ = (yes) - return $unique diff --git a/core/control_flow.nom b/core/control_flow.nom deleted file mode 100644 index b0c4f27..0000000 --- a/core/control_flow.nom +++ /dev/null @@ -1,489 +0,0 @@ -#!/usr/bin/env nomsu -V6.14 -# - This file contains compile-time actions that define basic control flow structures - like "if" statements and loops. - -use "core/metaprogramming" -use "core/operators" - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -# No-Op -test: - do nothing -(do nothing) compiles to "" - -# Conditionals -test: - if (no): - fail "conditional fail" - -(if $condition $if_body) compiles to (" - if \($condition as lua expr) then - \($if_body as lua) - end -") - -test: - unless (yes): - fail "conditional fail" - -(unless $condition $unless_body) parses as (if (not $condition) $unless_body) -[ - if $condition $if_body else $else_body, unless $condition $else_body else $if_body -] all compile to (" - if \($condition as lua expr) then - \($if_body as lua) - else - \($else_body as lua) - 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" -test: - assume ((1 if (yes) else 2) == 1) - assume ((1 if (no) else 2) == 2) - -[ - $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 -] all compile 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 {.Text, .List, .Dict, .Number}.($when_true_expr.type): - return - Lua (" - (\($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 - Lua (" - ((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 -test: - $i = 0 - --- $loop --- - $i += 1 - unless ($i == 10): - go to $loop - assume ($i == 10) - --- (Loop) --- - $i -= 1 - unless ($i == 0): - go to (Loop) - assume ($i == 0) - -(--- $label ---) compiles to (" - ::label_\( - ($label.stub, as lua id) if ($label.type == "Action") else - $label as lua identifier - ):: -") - -(go to $label) compiles to (" - goto label_\( - ($label.stub, as lua id) if ($label.type == "Action") else - $label as lua identifier - ) -") - -# Basic loop control -(stop $var) compiles to: - if $var: - return (Lua "goto stop_\($var as lua identifier)") - ..else: - return (Lua "break") - -(do next $var) compiles to: - if $var: - return (Lua "goto continue_\($var as lua identifier)") - ..else: - return (Lua "goto continue") - -(---stop $var ---) compiles to "::stop_\($var as lua identifier)::" -(---next $var ---) compiles to "::continue_\($var as lua identifier)::" - -# While loops -test: - $x = 0 - repeat while ($x < 10): $x += 1 - assume ($x == 10) - repeat while ($x < 20): stop - assume ($x == 10) - repeat while ($x < 20): - $x += 1 - if (yes): - do next - fail "Failed to 'do next'" - assume ($x == 20) - -(repeat while $condition $body) compiles to: - $lua = - Lua (" - while \($condition as lua expr) do - \($body as lua) - ") - - if ($body has subtree \(do next)): - $lua, add "\n ::continue::" - - $lua, add "\nend --while-loop" - return $lua - -(repeat $body) parses as (repeat while (yes) $body) -(repeat until $condition $body) parses as (repeat while (not $condition) $body) - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -test: - $nums = [] - for $x in 1 to 5: - $nums, add $x - assume ($nums == [1, 2, 3, 4, 5]) - $nums = [] - for $x in 1 to 5 via 2: - $nums, add $x - assume ($nums == [1, 3, 5]) - $nums = [] - for $outer in 1 to 100: - for $inner in $outer to ($outer + 2): - if ($inner == 2): - $nums, add -2 - do next $inner - $nums, add $inner - if ($inner == 5): - stop $outer - assume ($nums == [1, -2, 3, -2, 3, 4, 3, 4, 5]) - -# Numeric range for loops -[ - for $var in $start to $stop by $step $body - for $var in $start to $stop via $step $body -] all compile to: - # This uses Lua's approach of only allowing loop-scoped variables in a loop - $lua = - Lua (" - for \($var as lua identifier)=\($start as lua expr),\($stop as lua expr),\ - ..\($step as lua expr) do - ") - $lua, add "\n " ($body as lua) - if ($body has subtree \(do next)): - $lua, add "\n ::continue::" - - if ($body has subtree \(do next $var)): - $lua, add "\n " (\(---next $var ---) as lua) - - $lua, add "\nend -- numeric for " ($var as lua identifier) " loop" - if ($body has subtree \(stop $var)): - $lua = - Lua (" - do -- scope for (stop \($var as lua identifier)) - \$lua - \(\(---stop $var ---) as lua) - end -- scope for (stop \($var as lua identifier)) - ") - return $lua - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -(for $var in $start to $stop $body) parses as - for $var in $start to $stop via 1 $body - -test: - $x = 0 - repeat 5 times: - $x += 1 - assume $x == 5 - -(repeat $n times $body) parses as (for (=lua "_XXX_") in 1 to $n $body) -test: - $a = [10, 20, 30, 40, 50] - $b = [] - for $x in $a: - $b, add $x - assume ($a == $b) - $b = [] - for $x in $a: - if ($x == 10): - do next $x - - if ($x == 50): - stop $x - - $b, add $x - assume ($b == [20, 30, 40]) - -# For-each loop (lua's "ipairs()") -(for $var in $iterable at $i $body) compiles to: - # This uses Lua's approach of only allowing loop-scoped variables in a loop - $lua = - Lua (" - for \($i as lua identifier),\($var as lua identifier) in ipairs(\($iterable as lua expr)) do - \; - ") - $lua, add ($body as lua) - if ($body has subtree \(do next)): - $lua, add "\n ::continue::" - - if ($body has subtree \(do next $var)): - $lua, add "\n " (\(---next $var ---) as lua) - - $lua, add "\nend --for \($var as lua identifier) loop" - if ($body has subtree \(stop $var)): - $inner_lua = $lua - $lua = (Lua "do -- scope for stopping for-loop\n ") - $lua, add $inner_lua "\n " - $lua, add (\(---stop $var ---) as lua) - $lua, add "\nend -- end of scope for stopping for-loop" - return $lua - -(for $var in $iterable $body) parses as - for $var in $iterable at (=lua "__") $body - -test: - $d = {.a = 10, .b = 20, .c = 30, .d = 40, .e = 50} - $result = [] - for $k = $v in $d: - if ($k == "a"): - do next $k - - if ($v == 20): - do next $v - - $result, add "\$k = \$v" - assume (($result sorted) == ["c = 30", "d = 40", "e = 50"]) - -# Dict iteration (lua's "pairs()") -[for $key = $value in $iterable $body, for $key $value in $iterable $body] -..all compile to: - $lua = - Lua (" - for \($key as lua identifier),\($value as lua identifier) in pairs(\ - ..\($iterable as lua expr)) do - ") - $lua, add "\n " ($body as lua) - if ($body has subtree \(do next)): - $lua, add "\n ::continue::" - - if ($body has subtree \(do next $key)): - $lua, add "\n " (\(---next $key ---) as lua) - - if ($body has subtree \(do next $value)): - $lua, add "\n " (\(---next $value ---) as lua) - - $lua, add "\nend --foreach-loop" - $stop_labels = (Lua "") - if ($body has subtree \(stop $key)): - $stop_labels, add "\n" (\(---stop $key ---) as lua) - - if ($body has subtree \(stop $value)): - $stop_labels, add "\n" (\(---stop $value ---) as lua) - - if ((size of "\$stop_labels") > 0): - $inner_lua = $lua - $lua = (Lua "do -- scope for stopping for $ = $ loop\n ") - $lua, add $inner_lua $stop_labels "\nend" - - return $lua - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -test: - when: - (1 == 2) (100 < 0): - fail "bad conditional" - (1 == 0) (1 == 1) $not_a_variable.x: do nothing - (1 == 1): - fail "bad conditional" - - (1 == 2): - fail "bad conditional" - - else: - fail "bad conditional" - -# Multi-branch conditional (if..elseif..else) -(when $body) compiles to: - $code = (Lua "") - $clause = "if" - $else_allowed = (yes) - unless ($body.type is "Block"): - compile error at $body "'if' expected a Block, but got a \($body.type)." - "Perhaps you forgot to put a ':' after 'if'?" - - for $line in $body: - unless - (($line.type is "Action") and ((size of $line) >= 2)) and - $line.(size of $line) is "Block" syntax tree - ..: - compile error at $line "Invalid line for the body of an 'if' block." (" - Each line should contain one or more conditional expressions followed by a block, or "else"\ - .. followed by a block. - ") - $action = $line.(size of $line) - if (($line.1 is "else") and ((size of $line) == 2)): - unless $else_allowed: - compile error at $line "You can't have two 'else' blocks." - "Merge all of the 'else' blocks together." - - unless ((size of "\$code") > 0): - compile error at $line - .."You can't have an 'else' block without a preceding condition" (" - If you want the code in this block to always execute, you don't need a conditional block \ - ..around it. Otherwise, make sure the 'else' block comes last. - ") - - $code, add "\nelse\n " ($action as lua) - $else_allowed = (no) - ..else: - $code, add $clause " " - for $i in 1 to ((size of $line) - 1): - if ($i > 1): - $code, add " or " - $code, add ($line.$i as lua expr) - $code, add " then\n " ($action as lua) - $clause = "\nelseif" - - if ((size of "\$code") == 0): - compile error at $body "'if' block has an empty body." - "This means nothing would happen, so the 'if' block should be deleted." - - $code, add "\nend --when" - return $code - -test: - if 5 is: - 1 2 3: - fail "bad switch statement" - - 4 5: - do nothing - - 5 6: - fail "bad switch statement" - - else: - fail "bad switch statement" - -# Switch statement -[if $branch_value is $body, when $branch_value is $body] all compile to: - $code = (Lua "") - $clause = "if" - $else_allowed = (yes) - define mangler - unless ($body.type is "Block"): - compile error at $body "'if' expected a Block, but got a \($body.type)" - "Perhaps you forgot to put a ':' after the 'is'?" - - for $line in $body: - unless - (($line.type is "Action") and ((size of $line) >= 2)) and - $line.(size of $line) is "Block" syntax tree - ..: - compile error at $line "Invalid line for 'if' block." (" - Each line should contain expressions followed by a block, or "else" followed by a block - ") - $action = $line.(size of $line) - if (($line.1 is "else") and ((size of $line) == 2)): - unless $else_allowed: - compile error at $line "You can't have two 'else' blocks." - "Merge all of the 'else' blocks together." - - unless ((size of "\$code") > 0): - compile error at $line - .."You can't have an 'else' block without a preceding condition" (" - If you want the code in this block to always execute, you don't need a conditional block \ - ..around it. Otherwise, make sure the 'else' block comes last. - ") - - $code, add "\nelse\n " ($action as lua) - $else_allowed = (no) - ..else: - $code, add $clause " " - for $i in 1 to ((size of $line) - 1): - if ($i > 1): - $code, add " or " - $code, add "\(mangle "branch value") == " ($line.$i as lua expr) - $code, add " then\n " ($action as lua) - $clause = "\nelseif" - - if ((size of "\$code") == 0): - compile error at $body "'if' block has an empty body." - "This means nothing would happen, so the 'if' block should be deleted." - - $code, add "\nend --when" - return - Lua (" - do --if $ is... - local \(mangle "branch value") = \($branch_value as lua expr) - \$code - end -- if $ is... - ") - -# Do/finally -(do $action) compiles to (" - do - \($action as lua) - end -- do -") - -test: - assume ((result of: return 99) == 99) - -# Inline thunk: -(result of $body) compiles to "\(\(-> $body) as lua)()" -test: - $t = [1, [2, [[3], 4], 5, [[[6]]]]] - $flat = [] - for $ in recursive $t: - if ((lua type of $) is "table"): - for $2 in $: - recurse $ on $2 - ..else: - $flat, add $ - assume (sorted $flat) == [1, 2, 3, 4, 5, 6] - -# Recurion control flow -(recurse $v on $x) compiles to - Lua "table.insert(_stack_\($v as lua expr), \($x as lua expr))" -(for $var in recursive $structure $body) compiles to: - $lua = - Lua (" - do - local _stack_\($var as lua expr) = List{\($structure as lua expr)} - while #_stack_\($var as lua expr) > 0 do - \($var as lua expr) = table.remove(_stack_\($var as lua expr), 1) - \($body as lua) - ") - - if ($body has subtree \(do next)): - $lua, add "\n ::continue::" - - if ($body has subtree \(do next $var)): - $lua, add "\n \(\(---next $var ---) as lua)" - - $lua, add "\n end -- Recursive loop" - if ($body has subtree \(stop $var)): - $lua, add "\n \(\(---stop $var ---) as lua)" - $lua, add "\nend -- Recursive scope" - return $lua diff --git a/core/coroutines.nom b/core/coroutines.nom deleted file mode 100644 index 6a99f7e..0000000 --- a/core/coroutines.nom +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env nomsu -V6.14 -# - This file defines the code that creates and manipulates coroutines - -use "core/metaprogramming" -use "core/operators" -use "core/control_flow" - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -test: - $co = - ->: - yield 4 - yield 5 - repeat 3 times: - yield 6 - $nums = [] - for $ in coroutine $co: - $nums, add $ - - unless ($nums == [4, 5, 6, 6, 6]): - fail "Coroutine iteration failed" - - $d = {.x = 0} - $co2 = - coroutine: - $d.x += 1 - yield 1 - $d.x += 1 - yield - $d.x += 1 - repeat while ((coroutine status of $co2) != "dead"): resume $co2 - assume $d.x == 3 -(coroutine $body) parses as (coroutine from (-> $body)) -(for $ in coroutine $co $body) compiles to (" - for \($ as lua expr) in coroutine_wrap(\($co as lua expr)) do - \($body as lua) - end -") diff --git a/core/errors.nom b/core/errors.nom deleted file mode 100644 index 45fc8c5..0000000 --- a/core/errors.nom +++ /dev/null @@ -1,130 +0,0 @@ -#!/usr/bin/env nomsu -V6.14 -# - This file contains basic error reporting code - -use "core/metaprogramming" -use "core/operators" - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -(fail $msg) compiles to "error(\(($msg as lua expr) if $msg else "nil"), 0);" -(assume $condition) compiles to: - lua> (" - local \$assumption = 'Assumption failed: '..tostring((\$condition):get_source_code()) - ") - - return - Lua (" - if not \($condition as lua expr) then - error(\(quote "\$assumption"), 0) - end - ") - -(assume $a == $b) compiles to: - lua> "local \$assumption = 'Assumption failed: '..tostring(\(\($a == $b) as nomsu))" - - define mangler - - return - Lua (" - do - local \(mangle "a"), \(mangle "b") = \($a as lua expr), \($b as lua expr) - if \(mangle "a") ~= \(mangle "b") then - error(\(quote "\$assumption").."\\n"..tostring(\(mangle "a")).." != "..tostring(\ - ..\(mangle "b")), 0) - end - end - ") - -test: - try: fail - $worked = (no) - try: - fail "xx" - ..if it fails with $failure: - $worked = (yes) - ..if it succeeds: - fail "'try' incorrectly ran success case." - assume $failure == "xx" - unless $worked: - fail "'try' failed to recover from failure" - -# Try/except -[ - try $action if it succeeds $success if it fails with $msg $fallback - try $action if it fails with $msg $fallback if it succeeds $success -] all compile to: - $success_lua = ($success as lua) - if ((#"\$success_lua") > 0): - $success_lua, add "\n" - $success_lua, prepend "-- Success:\n" - $success_lua, - add "if not _fell_through then return table.unpack(_result, 2) end" - $fallback_lua = ($fallback as lua) - if ((#"\$fallback_lua") > 0): - $msg_lua = ($msg as lua expr) - if ((#"\$msg_lua") > 0): - $fallback_lua, prepend "\n\$msg_lua = _result[2]\n" - if ($msg_lua, text, is lua id): - $fallback_lua, add free vars [($msg_lua, text)] - $fallback_lua, prepend "-- Failure:" - return - Lua (" - do - local _fell_through = false - local _result = {pcall(function() - \($action as lua) - _fell_through = true - end)} - if _result[1] then - \$success_lua - else - \$fallback_lua - end - end - ") - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -(try $action) parses as - try $action if it succeeds (do nothing) if it fails (do nothing) - -(try $action if it fails $fallback) parses as - try $action if it succeeds (do nothing) if it fails $fallback - -(try $action if it fails with $msg $fallback) parses as - try $action if it succeeds (do nothing) if it fails with $msg $fallback - -(try $action if it succeeds $success) parses as - try $action if it succeeds $success if it fails (do nothing) - -(try $action if it fails $fallback if it succeeds $success) parses as - try $action if it fails with (=lua "") $fallback if it succeeds $success - -(try $action if it succeeds $success if it fails $fallback) parses as - try $action if it succeeds $success if it fails with (=lua "") $fallback - -test: - $success = (no) - try: - do: fail - ..then always: - $success = (yes) - ..if it succeeds: - fail "'try ... then always ...' didn't propagate failure" - - unless $success: - fail "'try ... then always ...' didn't execute the 'always' code" - -(do $action then always $final_action) compiles to (" - do -- do/then always - local _fell_through = false - local _results = {pcall(function() - \($action as lua) - _fell_through = true - end)} - \($final_action as lua) - if not _results[1] then error(_results[2], 0) end - if not _fell_through then return table.unpack(_results, 2) end - end -") diff --git a/core/id.nom b/core/id.nom deleted file mode 100644 index d2427b5..0000000 --- a/core/id.nom +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env nomsu -V6.14 -# - A simple UUID function based on RFC 4122: http://www.ietf.org/rfc/rfc4122.txt - -use "core/metaprogramming" -use "core/operators" -use "core/math" -use "core/collections" -use "core/control_flow" - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -$NaN_surrogate = {} -$nil_surrogate = {} -$obj_by_id = {} -set $obj_by_id's metatable to {.__mode = "v"} -$id_by_obj = {} -set $id_by_obj's metatable to { - .__mode = "k" - .__index = - for ($self $key): - if ($key == (nil)): - return $self.$nil_surrogate - - if ($key != $key): - return $self.$NaN_surrogate - - --- (retry) --- - $id = (uuid) - if ($obj_by_id.$id != (nil)): go to (retry) - $self.$key = $id - $obj_by_id.$id = $key - return $id -} - -externally (uuid) means: - # Set all the other bits to randomly (or pseudo-randomly) chosen values. - $bytes = [ - # time-low, time-mid, time-high-and-version - randint (2 ^ (4 * 8)), randint (2 ^ (2 * 8)), randint (2 ^ (2 * 8 - 4)) - # clock-seq-and-reserved, clock-seq-low - randint (2 ^ (1 * 8 - 2)), randint (2 ^ (1 * 8)), randint (2 ^ (3 * 8)) - # node - randint (2 ^ (3 * 8)) - ] - - # Set the four most significant bits (bits 12 through 15) of the - # time_hi_and_version field to the 4-bit version number from - # Section 4.1.3. - $bytes.3 += 0x4000 - - # Set the two most significant bits (bits 6 and 7) of the - # clock_seq_hi_and_reserved to zero and one, respectively. - $bytes.4 += 0xC0 - return (=lua "('%08x-%04x-%04x-%02x%02x-%6x%6x'):format(unpack(\$bytes))") - -# For strict identity checking, use ($x's id) == ($y's id) -test: - assume (([] == []) and ((id of []) != (id of []))) - seed random with 0 - $x = [] - assume ((id of $x) == (id of $x)) - seed random with 0 - assume ((id of $x) != (id of [])) - seed random -externally [id of $, $'s id, $'id] all mean $id_by_obj.$ diff --git a/core/init.nom b/core/init.nom deleted file mode 100644 index 0c8051d..0000000 --- a/core/init.nom +++ /dev/null @@ -1,11 +0,0 @@ -# Export everything -export "core/metaprogramming" -export "core/operators" -export "core/control_flow" -export "core/errors" -export "core/collections" -export "core/coroutines" -export "core/math" -export "core/id" -export "core/io" -export "core/text" diff --git a/core/io.nom b/core/io.nom deleted file mode 100644 index 7afe889..0000000 --- a/core/io.nom +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env nomsu -V6.14 -# - This file contains basic input/output code - -use "core/metaprogramming" - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -(say $message) compiles to: - lua> (" - if \$message.type == "Text" then - return LuaCode("say(", \($message as lua expr), ");"); - else - return LuaCode("say(tostring(", \($message as lua expr), "));"); - end - ") - -(say $message inline) compiles to: - lua> (" - if \$message.type == "Text" then - return LuaCode("io.write(", \($message as lua expr), ")"); - else - return LuaCode("io.write(tostring(", \($message as lua expr), "))"); - end - ") - -externally (ask $prompt) means: - $io.write $prompt - return ($io.read()) diff --git a/core/math.nom b/core/math.nom deleted file mode 100644 index 685ab1e..0000000 --- a/core/math.nom +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/env nomsu -V6.14 -# - This file defines some common math literals and functions - -use "core/metaprogramming" -use "core/text" -use "core/operators" -use "core/control_flow" -use "core/collections" - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -# Literals: -test: - unless (all of [inf, NaN, pi, tau, golden ratio, e]): - fail "math constants failed" - $nan = (NaN) - unless ($nan != $nan): - fail "NaN failed" -[infinity, inf] all compile to "math.huge" -[not a number, NaN, nan] all compile to "(0/0)" -[pi, Pi, PI] all compile to "math.pi" -[tau, Tau, TAU] all compile to "(2*math.pi)" -(golden ratio) compiles to "((1+math.sqrt(5))/2)" -(e) compiles to "math.exp(1)" - -# Functions: -test: - assume (("5" as a number) == 5) -external $($ as a number) = $(tonumber $) -external $($ as number) = $(tonumber $) -test: - unless - all of [ - abs 5, | 5 |, sqrt 5, √ 5, sine 5, cosine 5, tangent 5, arc sine 5, arc cosine 5 - arc tangent 5, arc tangent 5 / 10, hyperbolic sine 5, hyperbolic cosine 5 - hyperbolic tangent 5, e^ 5, ln 5, log 5 base 2, floor 5, ceiling 5, round 5 - ] - ..: - fail "math functions failed" -external [$(absolute value $), $(absolute value of $), $(| $ |), $(abs $)] = - [$math.abs, $math.abs, $math.abs, $math.abs] -external [$(square root $), $(square root of $), $(√ $), $(sqrt $)] = - [$math.sqrt, $math.sqrt, $math.sqrt, $math.sqrt] -external [$(sine $), $(sin $)] = [$math.sin, $math.sin] -external [$(cosine $), $(cos $)] = [$math.cos, $math.cos] -external [$(tangent $), $(tan $)] = [$math.tan, $math.tan] -external [$(arc sine $), $(asin $)] = [$math.asin, $math.asin] -external [$(arc cosine $), $(acos $)] = [$math.acos, $math.acos] -external [$(arc tangent $), $(atan $)] = [$math.atan, $math.atan] -external [$(arc tangent $y / $x), $(atan2 $y $x)] = [$math.atan2, $math.atan2] -external [$(hyperbolic sine $), $(sinh $)] = [$math.sinh, $math.sinh] -external [$(hyperbolic cosine $), $(cosh $)] = [$math.cosh, $math.cosh] -external [$(hyperbolic tangent $), $(tanh $)] = [$math.tanh, $math.tanh] -external [$(e^ $), $(exp $)] = [$math.exp, $math.exp] -external [$(natural log $), $(ln $), $(log $), $(log $ base $)] = [$math.log, $math.log, $math.log, $math.log] -external $(floor $) = $math.floor -external [$(ceiling $), $(ceil $)] = [$math.ceil, $math.ceil] -externally [round $, $ rounded] all mean - floor ($ + 0.5) -test: - unless ((463 to the nearest 100) == 500): fail "rounding failed" - unless ((2.6 to the nearest 0.25) == 2.5): fail "rounding failed" - -externally ($n to the nearest $rounder) means - $rounder * (floor ($n / $rounder + 0.5)) - -# Any/all -externally [all of $items, all $items] all mean: - for $ in $items: - unless $: - return (no) - return (yes) -[not all of $items, not all $items] all parse as (not (all of $items)) -externally [any of $items, any $items] all mean: - for $ in $items: - if $: - return (yes) - return (no) -[none of $items, none $items] all parse as (not (any of $items)) - -# Sum/product -externally [sum of $items, sum $items] all mean: - $total = 0 - for $ in $items: - $total += $ - return $total - -externally [product of $items, product $items] all mean: - $prod = 1 - for $ in $items: - $prod *= $ - return $prod - -externally [avg of $items, average of $items] all mean - (sum of $items) / (size of $items) - -# Min/max -externally [min of $items, smallest of $items, lowest of $items] all mean: - $best = (nil) - for $ in $items: - if (($best == (nil)) or ($ < $best)): $best = $ - return $best - -externally [ - max of $items, biggest of $items, largest of $items, highest of $items -] all mean: - $best = (nil) - for $ in $items: - if (($best == (nil)) or ($ > $best)): $best = $ - return $best - -test: - assume ((min of [3, -4, 1, 2] by $ = ($ * $)) == 1) - assume ((max of [3, -4, 1, 2] by $ = ($ * $)) == -4) - -(min of $items by $item = $value_expr) parses as - result of: - $best = (nil) - $best_key = (nil) - for $item in $items: - $key = $value_expr - if (($best == (nil)) or ($key < $best_key)): - $best = $item - $best_key = $key - return $best - -(max of $items by $item = $value_expr) parses as - result of: - $best = (nil) - $best_key = (nil) - for $item in $items: - $key = $value_expr - if (($best == (nil)) or ($key > $best_key)): - $best = $item - $best_key = $key - return $best - -test: - assume (100 clamped between 0 and 10) == 10 - -externally ($ clamped between $min and $max) means: - when: - ($ < $min): - return $min - - ($ > $max): - return $max - - else: - return $ - -test: - assume (-0.1 smoothed by 2.7) == 0 - assume (0 smoothed by 2.7) == 0 - assume (0.5 smoothed by 2.7) == 0.5 - assume (1 smoothed by 2.7) == 1 - assume (1.1 smoothed by 2.7) == 1 - -externally ($ smoothed by $smoothness) means: - $ = ($ clamped between 0 and 1) - if ($smoothness == 0): return $ - $k = (2 ^ $smoothness) - if ($ < 0.5): - return (0.5 * (2 * $) ^ $k) - ..else: - return (1 - 0.5 * (2 - 2 * $) ^ $k) - -test: - assume (5 to 7 mixed by -1.0) == 5 - assume (5 to 7 mixed by 0.0) == 5 - assume (5 to 7 mixed by 0.5) == 6 - assume (5 to 7 mixed by 1.0) == 7 - assume (5 to 7 mixed by 2.0) == 7 - -externally ($lo to $hi mixed by $amount) means: - $ = ($amount clamped between 0 and 1) - return ((1 - $) * $lo + $ * $hi) - -test: - assume ([0, 1, 11] mixed by 0.0) == 0 - assume ([0, 1, 11] mixed by 0.25) == 0.5 - assume ([0, 1, 11] mixed by 0.5) == 1 - assume ([0, 1, 11] mixed by 0.75) == 6 - assume ([0, 1, 11] mixed by 1.0) == 11 - assume ([99] mixed by 0.5) == 99 - -externally ($nums mixed by $amount) means: - $ = ($amount clamped between 0 and 1) - $i = (1 + ($ * ((#$nums) - 1))) - if ((floor $i) == (#$nums)): - return $nums.(floor $i) - [$lo, $hi] = [$nums.(floor $i), $nums.(floor ($i + 1))] - return ($lo to $hi mixed by ($i mod 1)) - -# Random functions -externally (seed random with $) means: - lua> (" - math.randomseed(\$); - for i=1,20 do math.random(); end - ") -(seed random) parses as (seed random with (=lua "os.time()")) -[random number, random, rand] all compile to "math.random()" -[random int $n, random integer $n, randint $n] all compile to - "math.random(\($n as lua expr))" - -[random from $low to $high, random number from $low to $high, rand $low $high] -..all compile to "math.random(\($low as lua expr), \($high as lua expr))" - -externally [ - random choice from $elements, random choice $elements, random $elements -] all mean (=lua "\$elements[math.random(#\$elements)]") diff --git a/core/metaprogramming.nom b/core/metaprogramming.nom deleted file mode 100644 index fa29a8a..0000000 --- a/core/metaprogramming.nom +++ /dev/null @@ -1,454 +0,0 @@ -#!/usr/bin/env nomsu -V6.14 -# - This File contains actions for making actions and compile-time actions and some helper - functions to make that easier. - -lua> "NOMSU_CORE_VERSION = 14" -lua> "NOMSU_LIB_VERSION = 8" -lua> (" - do - local mangle_index = 0 - function mangler() - local my_mangle_index = mangle_index - mangle_index = mangle_index + 1 - return function(varname) - return (varname..(("\\3%X"):format(my_mangle_index))):as_lua_id() - end - end - end - COMPILE_RULES["define mangler"] = function(\(nomsu environment)) - return LuaCode("local mangle = mangler()") - end -") - -lua> (" - COMPILE_RULES["1 ->"] = function(\(nomsu environment), \$args, \$body) - if \$args and not \$body then \$args, \$body = {}, \$args end - local body_lua = SyntaxTree:is_instance(\$body) and \(nomsu environment):compile(\$body) or \$body - if SyntaxTree:is_instance(\$body) and \$body.type ~= "Block" then body_lua:prepend("\ - ..return ") end - local lua = LuaCode("(function(") - if SyntaxTree:is_instance(\$args) and (\$args.type == "Action" or \$args.type == "MethodCall") then - \$args = \$args:get_args() - elseif SyntaxTree:is_instance(\$args) and \$args.type == "Var" then \$args = {\$args} end - for i, arg in ipairs(\$args) do - local arg_lua = SyntaxTree:is_instance(arg) and \(nomsu environment):compile(arg):text() or arg - if arg_lua == "..." then - if i < #\$args then - compile_error_at(SyntaxTree:is_instance(arg) and arg or nil, - "Extra arguments must come last.", "Try removing any arguments after \ - ..(*extra arguments*)") - end - elseif not arg_lua:is_lua_id() then - compile_error_at(SyntaxTree:is_instance(arg) and arg or nil, - "This does not compile to a Lua identifier, so it can't be used as a function \ - ..argument.", - "This should probably be a Nomsu variable instead (like $x).") - end - lua:add(i > 1 and ", " or "", arg_lua) - body_lua:remove_free_vars({arg_lua}) - end - body_lua:declare_locals() - lua:add(")\\n ", body_lua, "\\nend)") - return lua - end - COMPILE_RULES["->"] = COMPILE_RULES["1 ->"] - COMPILE_RULES["for"] = COMPILE_RULES["1 ->"] -") - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -test: - (five) compiles to "5" - -test: - unless ((five) == 5): - fail "Compile to expression failed." - (loc x) compiles to "local x = 99;" - -test: - lua> "do" - loc x - unless ($x is 99): - fail "Compile to statements with locals failed." - lua> "end" - unless ($x is (nil)): - fail "Failed to properly localize a variable." - - (asdf) compiles to: - $tmp = "" - return (Lua $tmp) - -test: - asdf - unless ($tmp is (nil)): - fail "compile to is leaking variables" - -lua> (" - COMPILE_RULES["1 compiles to"] = function(env, \$action, \$body) - local \$args = List{"\(nomsu environment)", unpack(\$action:get_args())} - if \$body.type == "Text" then - \$body = SyntaxTree{source=\$body.source, type="Action", "Lua", \$body} - end - return LuaCode("COMPILE_RULES[", \$action:get_stub():as_lua(), - "] = ", \(\($args -> $body) as lua)) - end -") - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -($actions all compile to $body) compiles to: - lua> (" - if \$actions.type ~= "List" then - compile_error(\$actions, "This should be a list of actions.") - end - local lua = \(\($actions.1 compiles to $body) as lua) - local \$args = List{"\(nomsu environment)", unpack(\$actions[1]:get_args())} - local \$compiled_args = List{"\(nomsu environment)"}; - for i=2,#\$args do \$compiled_args[i] = \(nomsu environment):compile(\$args[i]) end - for i=2,#\$actions do - local alias = \$actions[i] - local \$alias_args = List{"\(nomsu environment)", unpack(alias:get_args())} - lua:add("\\nCOMPILE_RULES[", alias:get_stub():as_lua(), "] = ") - if \$alias_args == \$args then - lua:add("COMPILE_RULES[", \$actions[1]:get_stub():as_lua(), "]") - else - lua:add("function(") - local \$compiled_alias_args = List{"\(nomsu environment)"}; - for i=2,#\$alias_args do \$compiled_alias_args[i] = \(nomsu environment):compile(\$alias_args[i]) end - lua:concat_add(\$compiled_alias_args, ", ") - lua:add(") return COMPILE_RULES[", \$actions[1]:get_stub():as_lua(), "](") - lua:concat_add(\$compiled_args, ", ") - lua:add(") end") - end - end - return lua - ") - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -test: - (foo $x) means "outer" - with [$(foo $)]: - (foo $x) means: - $y = ($x + 1) - return $y - - unless ((foo 10) == 11): - fail "Action didn't work." - - unless ($y is (nil)): - fail "Action leaked a local into globals." - - (baz $) parses as (foo $) - assume ((foo 1) == "outer") - -($action means $body) compiles to: - lua> (" - - local lua = LuaCode() - if \$action.type == "MethodCall" then - lua:add(\(nomsu environment):compile(\$action[1]), ".", \$action[2]:get_stub():as_lua_id()) - elseif \$action.type == "Action" then - lua:add(\$action:get_stub():as_lua_id()) - lua:add_free_vars({\$action:get_stub():as_lua_id()}) - else - compile_error_at(\$action, "Expected an action or method call here") - end - lua:add(" = ", \(\($action -> $body) as lua), ";") - return lua - ") - -($actions all mean $body) compiles to: - lua> (" - local lua = \(\($actions.1 means $body) as lua) - local first_def = (\$actions[1].type == "MethodCall" - and LuaCode(\(nomsu environment):compile(\$actions[1][1]), ".", \$actions[1]:get_stub():as_lua_id()) - or LuaCode(\$actions[1]:get_stub():as_lua_id())) - local \$args = List(\$actions[1]:get_args()) - for i=2,#\$actions do - local alias = \$actions[i] - local \$alias_args = List(alias:get_args()) - lua:add("\\n") - if alias.type == "MethodCall" then - lua:add(\(nomsu environment):compile(alias[1]), ".", alias:get_stub():as_lua_id()) - else - lua:add(alias:get_stub():as_lua_id()) - lua:add_free_vars({alias_name}) - end - if \$args == \$alias_args then - lua:add(" = ", first_def, ";") - else - lua:add(" = ", \(\($alias_args -> $actions.1) as lua), ";") - end - end - return lua - ") - -test: - externally (baz1) means: - return "baz1" - externally (baz2) means "baz2" - -test: - assume ((baz1) == "baz1") - assume ((baz2) == "baz2") - -(externally $action means $body) compiles to: - lua> (" - local lua = \(\($action means $body) as lua) - lua:remove_free_vars({\$action:get_stub():as_lua_id()}) - return lua - ") - -(externally $actions all mean $body) compiles to: - lua> (" - local lua = \(\($actions all mean $body) as lua) - lua:remove_free_vars(table.map(\$actions, function(a) return a:get_stub():as_lua_id() end)) - return lua - ") - -test: - (swap $x and $y) parses as - do: - $tmp = $x - $x = $y - $y = $tmp - -test: - [$1, $2] = [1, 2] - swap $1 and $2 - unless (($1 == 2) and ($2 == 1)): - fail "'parse $ as $' failed on 'swap $ and $'" - [$tmp, $tmp2] = [1, 2] - swap $tmp and $tmp2 - unless (($tmp == 2) and ($tmp2 == 1)): - fail "'parse $ as $' variable mangling failed." - -($actions all parse as $body) compiles to: - lua> (" - local replacements = {} - if \$actions.type ~= "List" then - compile_error(\$actions, "This should be a list.") - end - for i,arg in ipairs(\$actions[1]:get_args()) do - replacements[arg[1]] = \(nomsu environment):compile(arg):text() - end - local function make_tree(t) - if SyntaxTree:is_instance(t) and t.type == "Var" then - if replacements[t:as_var()] then - return replacements[t:as_var()] - else - return "SyntaxTree{mangle("..t:as_var():as_lua().."), type="..t.type:as_lua()..", \ - ..source="..tostring(t.source):as_lua().."}" - end - elseif SyntaxTree:is_instance(t) then - local ret = {} - local i = 1 - for k, v in pairs(t) do - if k == i then - ret[#ret+1] = make_tree(t[i]) - i = i + 1 - elseif k == "source" then - ret[#ret+1] = k.."= "..tostring(v):as_lua() - elseif lua_type_of(k) == 'string' and k:is_a_lua_id() then - ret[#ret+1] = k.."= "..make_tree(v) - else - ret[#ret+1] = "["..make_tree(k).."]= "..make_tree(v) - end - end - return "SyntaxTree{"..table.concat(ret, ", ").."}" - elseif lua_type_of(t) == 'number' then - return tostring(t) - else - return t:as_lua() - end - end - local \$new_body = LuaCode:from(\$body.source, - "local mangle = mangler()", - "\\nreturn ", make_tree(\$body)) - local ret = \(\($actions all compile to $new_body) as lua) - return ret - ") - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -[$action parses as $body] all parse as ([$action] all parse as $body) -externally ($tree as lua expr) means: - lua> (" - local tree_lua = \(nomsu environment):compile(\$tree) - if \$tree.type == 'Block' then - tree_lua = LuaCode:from(\$tree.source, '(function()\\n ', tree_lua, '\\nend)()') - elseif \$tree.type == 'MethodCall' and #\$tree > 2 then - compile_error_at(\$tree, "This must be a single value instead of "..(#\$tree - 1).."\ - .. method calls.", - "Replace this with a single method call.") - end - return tree_lua - ") - -externally [$var as lua identifier, $var as lua id] all mean: - lua> (" - local lua = \($var as lua) - if not lua:text():is_a_lua_id() then - compile_error(\$var, - "This is supposed to be something that compiles to a valid Lua identifier.", - "This should probably be a variable.") - end - return lua - ") - -test: - (num args (*extra arguments*)) means (select "#" (*extra arguments*)) - assume (num args 1 2 3) == 3 - (extra args (*extra arguments*)) means [*extra arguments*] - assume (extra args 1 2 3) == [1, 2, 3] - (third arg (*extra arguments*)) means (select 3 (*extra arguments*)) - assume (third arg 5 6 7 8) == 7 - -(*extra arguments*) compiles to "..." - -($ is syntax tree) compiles to "SyntaxTree:is_instance(\($ as lua expr))" - -externally ($ is $kind syntax tree) means - =lua "SyntaxTree:is_instance(\$) and \$.type == \$kind" - -($tree with $t -> $replacement) compiles to (" - \($tree as lua expr):map(function(\($t as lua expr)) - \( - =lua (" - \$replacement.type == 'Block' and \($replacement as lua) or 'return '..\ - ..\($replacement as lua expr):text() - ") - ) - end) -") - -externally ($tree with vars $replacements) means - =lua (" - \$tree:map(function(\$t) - if \$t.type == "Var" then - return \$replacements[\$t:as_var()] - end - end) - ") - -(tree $tree with vars $replacements) compiles to (" - \(=lua "(\$tree):as_lua()"):map(function(t) - if t.type == "Var" then - return \($replacements as lua expr)[t:as_var()] - end - end) -") - -($tree has subtree $match_tree) compiles to (" - (function() - local match_tree = \($match_tree as lua expr) - for subtree in coroutine_wrap(function() \($tree as lua expr):map(yield) end) do - if subtree == match_tree then return true end - end - end)() -") - -externally (match $tree with $patt) means: - lua> (" - if \$patt.type == "Var" then return Dict{[\$patt:as_var()]=\$tree} end - if \$patt.type == "Action" and \$patt:get_stub() ~= \$tree:get_stub() then return nil end - if #\$patt ~= #\$tree then return nil end - local matches = Dict{} - for \($i)=1,#\$patt do - if SyntaxTree:is_instance(\$tree[\$i]) then - local submatch = \(match $tree.$i with $patt.$i) - if not submatch then return nil end - for k,v in pairs(submatch) do - if matches[k] and matches[k] ~= v then return nil end - matches[k] = v - end - end - end - return matches - ") - -test: - assume - ( - quote (" - one - "two" - ") - ) == "\"one\\n\\\"two\\\"\"" - -(quote $s) compiles to "tostring(\($s as lua expr)):as_lua()" -test: - assume (lua type of {}) == "table" - assume (type of {}) == "Dict" - assume ({} is a "Dict") - assume ("" is text) - assume ("" isn't a "Dict") -externally ($ is text) means (=lua "\(lua type of $) == 'string'") -externally [$ is not text, $ isn't text] all mean - =lua "\(lua type of $) ~= 'string'" - -externally (type of $) means: - lua> (" - local lua_type = \(lua type of $) - if lua_type == 'string' then return 'Text' - elseif lua_type == 'table' or lua_type == 'userdata' then - local mt = getmetatable(\$) - if mt and mt.__type then return mt.__type end - end - return lua_type - ") - -[$ is a $type, $ is an $type] all parse as ((type of $) == $type) -[$ isn't a $type, $ isn't an $type, $ is not a $type, $ is not an $type] -..all parse as ((type of $) != $type) - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -test: - (foo) means: - return 100 200 300 - assume (select 2 (foo)) == 200 - -# 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. -(return (*extra arguments*)) compiles to: - lua> (" - local lua = \(Lua "do return ") - for i=1,select('#',...) do - if i > 1 then lua:add(", ") end - lua:add(\(nomsu environment):compile((select(i, ...)))) - end - lua:add(" end") - return lua - ") - -# Literals -(yes) compiles to "true" -(no) compiles to "false" -[nothing, nil, null] all compile to "nil" -(Nomsu syntax version) compiles to "NOMSU_SYNTAX_VERSION" -(Nomsu compiler version) compiles to "NOMSU_COMPILER_VERSION" -(core version) compiles to "NOMSU_CORE_VERSION" -(lib version) compiles to "NOMSU_LIB_VERSION" -(command line args) compiles to "COMMAND_LINE_ARGS" - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -# - (with local compile actions $body) compiles to (" - do - local OLD_RULES = COMPILE_RULES - local OLD_ENV = \(nomsu environment) - local \(nomsu environment) = setmetatable({ - COMPILE_RULES=setmetatable({}, {__index=OLD_RULES}) - }, {__index=OLD_ENV}) - \($body as lua) - end - ") - -externally (Nomsu version) means: - return (" - \(Nomsu syntax version).\(core version).\(Nomsu compiler version).\(lib version) - ") diff --git a/core/operators.nom b/core/operators.nom deleted file mode 100644 index dee76b6..0000000 --- a/core/operators.nom +++ /dev/null @@ -1,263 +0,0 @@ -#!/usr/bin/env nomsu -V6.14 -# - This file contains definitions of operators like "+" and "and". - -use "core/metaprogramming" - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -test: - assume (all [1 < 2, 2 > 1, 1 <= 2, 2 >= 1, 1 == 1, 1 != 2]) - -# Comparison Operators -($x < $y) compiles to "(\($x as lua expr) < \($y as lua expr))" -($x > $y) compiles to "(\($x as lua expr) > \($y as lua expr))" -($x <= $y) compiles to "(\($x as lua expr) <= \($y as lua expr))" -($x >= $y) compiles to "(\($x as lua expr) >= \($y as lua expr))" -[$a is $b, $a == $b] all compile to "(\($a as lua expr) == \($b as lua expr))" -[$a isn't $b, $a is not $b, $a not= $b, $a != $b] all compile to - "(\($a as lua expr) ~= \($b as lua expr))" - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -test: - $x = 10 - assume ($x == 10) - [$x, $y] = [10, 20] - unless (($x == 10) and ($y == 20)): - fail "mutli-assignment failed." - [$x, $y] = [$y, $x] - unless (($y == 10) and ($x == 20)): - fail "swapping vars failed." - $vals = [4, 5] - [$x, $y] = (unpack $vals) - unless (($x == 4) and ($y == 5)): - fail "unpacking failed" - -# Variable assignment operator -($var = $value) compiles to: - lua> (" - local lua = LuaCode() - if \$var.type == "List" then - for i, \$assignment in ipairs(\$var) do - if i > 1 then lua:add(", ") end - local assignment_lua = \($assignment as lua expr) - lua:add(assignment_lua) - if \$assignment.type == 'Var' then - lua:add_free_vars({assignment_lua:text()}) - end - end - lua:add(' = ') - if \$value.type == "List" then - if #\$value ~= #\$var then - compile_error_at(\$value, - "This assignment has too "..(#\$value > #\$var and "many" or "few").." values.", - "Make sure it has the same number of values on the left and right hand side \ - ..of the '=' operator.") - end - for i, \$val in ipairs(\$value) do - if i > 1 then lua:add(", ") end - local val_lua = \($val as lua expr) - lua:add(val_lua) - end - lua:add(";") - else - lua:add(\($value as lua expr), ';') - end - else - local var_lua = \($var as lua expr) - lua:add(var_lua) - if \$var.type == 'Var' then - lua:add_free_vars({var_lua:text()}) - end - lua:add(' = ', \($value as lua expr)) - end - return lua - ") - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -test: - [$foozle, $y] = ["outer", "outer"] - externally (set global x local y) means: - external $foozle = "inner" - $y = "inner" - set global x local y - unless (($foozle == "inner") and ($y == "outer")): fail "external failed." -(external $var = $value) compiles to: - $lua = ((SyntaxTree {.type = "Action", .source = $var.source, .1 = $var, .2 = "=", .3 = $value}) as lua) - $lua, remove free vars - return $lua -test: - [$foozle, $y] = ["outer", "outer"] - externally (set global x local y) means: - with external [$foozle]: - $foozle = "inner" - $y = "inner" - set global x local y - unless (($foozle == "inner") and ($y == "outer")): - fail "'with external' failed." - -(with external $externs $body) compiles to: - $body_lua = ($body as lua) - lua> (" - \$body_lua:remove_free_vars(table.map(\$externs, function(v) return \(nomsu environment):compile(v):text() end)) - ") - return $body_lua - -test: - [$x, $y] = [1, 2] - with [$z, $x = 999]: - assume $z == (nil) - $z = 999 - unless ($z == 999): - fail "'with' failed." - - unless ($x == 999): - fail "'with' assignment failed." - - unless ($x == 1): - fail "'with' scoping failed" - - unless ($z == (nil)): - fail "'with' scoping failed" - -(with $assignments $body) compiles to: - lua> (" - local \$defs = LuaCode() - for i, \$item in ipairs(\$assignments) do - if i > 1 then \$defs:add("\\n") end - local item_lua = \($item as lua) - if \$item.type == 'Action' and \$item.stub == '1 =' then - item_lua:remove_free_vars(item_lua.free_vars) - end - \$defs:add("local ", item_lua, ";") - end - ") - - return - Lua (" - do - \$defs - \($body as lua) - end -- 'with' block - ") - -# Math Operators -test: - unless ((5 wrapped around 2) == 1): - fail "mod not working" - -[$x wrapped around $y, $x mod $y] all compile to - "((\($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) -test: - $calls = 0 - (one) means: - external $calls = ($calls + 1) - return 1 - - unless (0 <= (one) <= 2): - fail "Three-way chained comparison failed." - - unless ($calls == 1): - fail "Three-way comparison evaluated middle value multiple times" -($x < $y < $z) parses as ((($a $b $c) -> (($a < $b) and ($b < $c))) $x $y $z) -($x <= $y < $z) parses as ((($a $b $c) -> (($a <= $b) and ($b < $c))) $x $y $z) -($x < $y <= $z) parses as ((($a $b $c) -> (($a < $b) and ($b <= $c))) $x $y $z) -($x <= $y <= $z) parses as ((($a $b $c) -> (($a <= $b) and ($b <= $c))) $x $y $z) -($x > $y > $z) parses as ((($a $b $c) -> (($a > $b) and ($b > $c))) $x $y $z) -($x >= $y > $z) parses as ((($a $b $c) -> (($a >= $b) and ($b > $c))) $x $y $z) -($x > $y >= $z) parses as ((($a $b $c) -> (($a > $b) and ($b >= $c))) $x $y $z) -($x >= $y >= $z) parses as ((($a $b $c) -> (($a >= $b) and ($b >= $c))) $x $y $z) - -# TODO: optimize for common case where x,y,z are all either variables or number literals -# Boolean Operators -test: - (barfer) means (fail "short circuiting failed") - assume (((no) and (barfer)) == (no)) - assume ((no) or (yes)) - assume ((yes) or (barfer)) -($x and $y) compiles to "(\($x as lua expr) and \($y as lua expr))" -($x or $y) compiles to "(\($x as lua expr) or \($y as lua expr))" - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -# Bitwise Operators -# TODO: implement OR, XOR, AND for multiple operands? -test: - assume ((~ (~ 5)) == 5) - assume ((1 | 4) == 5) - assume ((1 ~ 3) == 2) - assume ((1 & 3) == 1) - assume ((1 << 2) == 4) - assume ((4 >> 2) == 1) - -# Lua 5.3 introduced bit operators like | and &. Use them when possible, otherwise - fall back to bit.bor(), bit.band(), etc. -lua> "if \((is jit) or ((Lua version) == "Lua 5.2")) then" -[NOT $, ~ $] all compile to "bit.bnot(\($ as lua expr))" -[$x OR $y, $x | $y] all compile to - "bit.bor(\($x as lua expr), \($y as lua expr))" - -[$x XOR $y, $x ~ $y] all compile to - "bit.bxor(\($x as lua expr), \($y as lua expr))" - -[$x AND $y, $x & $y] all compile to - "bit.band(\($x as lua expr), \($y as lua expr))" - -[$x LSHIFT $shift, $x << $shift] all compile to - "bit.lshift(\($x as lua expr), \($shift as lua expr))" - -[$x RSHIFT $shift, $x >> $shift] all compile to - "bit.rshift(\($x as lua expr), \($shift as lua expr))" - -lua> "else" -[NOT $, ~ $] all compile to "~(\($ as lua expr))" -[$x OR $y, $x | $y] all compile to "(\($x as lua expr) | \($y as lua expr))" -[$x XOR $y, $x ~ $y] all compile to "(\($x as lua expr) ~ \($y as lua expr))" -[$x AND $y, $x & $y] all compile to "(\($x as lua expr) & \($y as lua expr))" -[$x LSHIFT $shift, $x << $shift] all compile to - "(\($x as lua expr) << \($shift as lua expr))" - -[$x RSHIFT $shift, $x >> $shift] all compile to - "(\($x as lua expr) >> \($shift as lua expr))" - -lua> "end" - -# Unary operators -test: - assume ((- 5) == -5) - assume ((not (yes)) == (no)) -(- $) compiles to "(- \($ as lua expr))" -(not $) compiles to "(not \($ as lua expr))" -test: - assume ((size of [1, 2, 3]) == 3) - assume ((#[1, 2, 3]) == 3) -[#$list, size of $list] all compile to "(#\($list as lua expr))" -($list is empty) compiles to "(#\($list as lua expr) == 0)" - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -# Update operators -test: - $x = 1 - $x += 1 - unless ($x == 2): - fail "+= failed" - $x *= 2 - unless ($x == 4): - fail "*= failed" - wrap $x around 3 - unless ($x == 1): - fail "wrap around failed" -($var += $) parses as ($var = (($var or 0) + $)) -($var -= $) parses as ($var = (($var or 0) - $)) -($var *= $) parses as ($var = (($var or 1) * $)) -($var /= $) parses as ($var = ($var / $)) -($var ^= $) parses as ($var = ($var ^ $)) -($var and= $) parses as ($var = ($var and $)) -($var or= $) parses as ($var = ($var or $)) -(wrap $var around $) parses as ($var = ($var wrapped around $)) diff --git a/core/text.nom b/core/text.nom deleted file mode 100644 index 1351af6..0000000 --- a/core/text.nom +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env nomsu -V6.14 -# - This file contains some definitions of text escape sequences, including ANSI console - color codes. - -use "core/metaprogramming" -use "core/operators" -use "core/control_flow" - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -test: - assume "\[1, 2, 3]" == "[1, 2, 3]" - assume "foo = \(1 + 2)!" == "foo = 3!" - assume (" - one - two - ") == (" - one - two - ") - assume "nogap" == "nogap" - assume (["x", "y"], joined with ",") == "x,y" - assume (["x", "y"], joined) == "xy" - assume ("BAR", byte 2) == 65 - assume ("BAR", bytes 1 to 2) == [66, 65] - assume ("asdf", capitalized) == "Asdf" - assume ("asdf", uppercase) == "ASDF" - assume ("asdf", with "s" -> "X") == "aXdf" - assume - (" - one - two - - "), lines - ..== ["one", "two", ""] - - ($spec とは $body) parses as ($spec means $body) - -test: - $こんにちは = "こんにちは" - ($ と言う) とは "\($)世界" - assume ($こんにちは と言う) == "こんにちは世界" - -($expr for $match in $text matching $patt) compiles to: - define mangler - return - Lua (" - (function() - local \(mangle "comprehension") = List{} - for \($match as lua expr) in (\($text as lua expr)):gmatch(\($patt as lua expr)) do - \(mangle "comprehension")[#\(mangle "comprehension")+1] = \($expr as lua) - end - return \(mangle "comprehension") - end)() - ") - -test: - assume "\n" == (newline) - -test: - assume (0xDEADBEEF as hex) == "0xDEADBEEF" - -externally ($num as hex) means: - if ($num < 0): - return ("-0x%X", formatted with (- $num)) - ..else: - return ("0x%X", formatted with $num) - -# Text literals -$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 = $str in $escapes: - with [$lua = (Lua (quote $str))]: - $(COMPILE RULES).$name = (-> $lua) |
