#!/usr/bin/env nomsu -V7.0.0 ### 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 (#$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 #$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 = -9, .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} ### Set compliments: assume (~{.x}).y assume ((~{.x}).x == (nil)) $sc = ~{.x, .y} $sc.y = 99 ### For now, whether $sc.y == 99 or $sc.y == (yes) is unspecified, (but the actual behavior is (yes)) assume ($sc.y and (not $sc.x)) assume ($sc == (~{.x, .y} | {.y = 99})) ### Both sets: assume (({.x, .y} & {.y, .z}) == {.y}) assume (({.x, .y} | {.y, .z}) == {.x, .y, .z}) assume (({.x, .y} ~ {.y, .z}) == {.z, .x}) ### Mixed: assume (({.x, .y} & ~{.y, .z}) == {.x}) assume (({.x, .y} | ~{.y, .z}) == ~{.z}) assume (({.x, .y} ~ ~{.y, .z}) == ~{.x}) ### Mixed reversed: assume ((~{.y, .z} & {.x, .y}) == {.x}) assume ((~{.y, .z} | {.x, .y}) == ~{.z}) assume ((~{.y, .z} ~ {.x, .y}) == ~{.x}) ### Both set compliments: assume ((~{.x, .y} & ~{.y, .z}) == ~{.x, .y, .z}) assume ((~{.x, .y} | ~{.y, .z}) == ~{.y}) assume ((~{.x, .y} ~ ~{.y, .z}) == {.x, .z}) 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)" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ external: test: assume ((sorted [3, 1, 2]) == [1, 2, 3]) [$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]) (unique $items) means: $unique = [] $seen = {} for $ in $items: unless $seen.$: $unique, add $ $seen.$ = (yes) return $unique ### Ranges: test: $r = (3 to 5) assume ($r.2 == 4) assume ($r is "a Range") assume ((1 to 10, backwards) == (10 to 1 by -1)) assume (#(1 to 10 by 2) == 5) $visited = [] for $ in (1 to 10 by 2): $visited, add $ assume ($visited == [1, 3, 5, 7, 9]) $r = (1 to 10 by 2) $visited = [] for $ in $r: $visited, add $ assume ($visited == [1, 3, 5, 7, 9]) $range_mt = { .__type = "a Range" .__index = for ($self's $key): if ($key is "a Number"): if (($key % 1) != 0): return (nil) $i = ($self.first + ($key - 1) * $self.step) if ($self.step > 0): if ($i > $self.last): return (nil) ..else: if ($i < $self.last): return (nil) return $i return $range_mt.$key .__len = for $self: $len = (($self.last - $self.first) / $self.step + 1) if ($len < 0): $len = 0 return ($len, rounded down) .__eq = for ($self == $other) (($self's metatable) == ($other's metatable)) and ($self.first == $other.first) and ($self.last == $other.last) and ($self.step == $other.step) .backwards = (for $self ($self.last to $self.first by -$self.step)) .__inext = $(inext), .__next = $(inext) .as_text = for $self: if ($self.step == 1): return "(\($self.first) to \($self.last))" ..else: return "(\($self.first) to \($self.last) by \($self.step))" } $range_mt.reversed = $range_mt.backwards $range_mt.__unm = $range_mt.backwards $range_mt.__tostring = $range_mt.as_text $range_mt.as_nomsu = $range_mt.as_text set $range_mt's metatable to (nil) external: ($first to $last by $step) means setmetatable {.first = $first, .last = $last, .step = $step} $range_mt ($first to $last) means setmetatable {.first = $first, .last = $last, .step = 1} $range_mt