236 lines
7.0 KiB
Plaintext
236 lines
7.0 KiB
Plaintext
#!/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 |