ngs-lang / ngs

Next Generation Shell (NGS)
https://ngs-lang.org/
GNU General Public License v3.0
1.4k stars 43 forks source link

Build errors in vm.c on FreeBSD 13.2 #642

Closed yonas closed 1 year ago

yonas commented 1 year ago
$ gmake clean install
...
[ 57%] Building C object CMakeFiles/ngs.dir/vm.c.o
/memfs/git/ngs/vm.c:1425:25: warning: implicitly declaring library function 'strcasecmp' with type 'int (const char *, const char *)' [-Wimplicit-function-declaration]
        METHOD_RETURN(MAKE_INT(strcasecmp(obj_to_cstring(argv[0]), obj_to_cstring(argv[1]))));
                               ^
/memfs/git/ngs/vm.c:1425:25: note: include the header <strings.h> or explicitly provide a declaration for 'strcasecmp'
/memfs/git/ngs/vm.c:3716:4: error: use of undeclared identifier 'PF_UNIX'; did you mean 'P_UID'?
        E(PF_UNIX);
          ^~~~~~~
          P_UID
/memfs/git/ngs/vm.c:3675:54: note: expanded from macro 'E'
        #define E(name) set_global(vm, "C_" #name, MAKE_INT(name))
                                                            ^
/memfs/git/ngs/obj.h:318:53: note: expanded from macro 'MAKE_INT'
#define MAKE_INT(n)     ((VALUE){.num=(((intptr_t) (n)) << TAG_BITS) | TAG_INT})
                                                    ^
/usr/include/sys/wait.h:114:2: note: 'P_UID' declared here
        P_UID,                  /* A user identifier. */
        ^
/memfs/git/ngs/vm.c:3717:4: error: use of undeclared identifier 'PF_INET'; did you mean 'T_INT'?
        E(PF_INET);
          ^~~~~~~
          T_INT
/memfs/git/ngs/vm.c:3675:54: note: expanded from macro 'E'
        #define E(name) set_global(vm, "C_" #name, MAKE_INT(name))
                                                            ^
/memfs/git/ngs/obj.h:318:53: note: expanded from macro 'MAKE_INT'
#define MAKE_INT(n)     ((VALUE){.num=(((intptr_t) (n)) << TAG_BITS) | TAG_INT})
                                                    ^
/memfs/git/ngs/obj.h:265:2: note: 'T_INT' declared here
        T_INT           = 26,
        ^
/memfs/git/ngs/vm.c:3747:18: error: use of undeclared identifier 'SIGWINCH'
        S(SIGVTALRM); S(SIGWINCH); S(SIGXCPU); S(SIGXFSZ);
                        ^
/memfs/git/ngs/vm.c:3747:18: error: use of undeclared identifier 'SIGWINCH'
1 warning and 4 errors generated.
*** Error code 1

git commit 4dbdd65

ilyash-b commented 1 year ago

Thanks for the report, @yonas . I'll take a look. Not sure how much effort (time) that would be because NGS was never tested on FreeBSD. Any hints are welcome. It looks like some include is missing.

ilyash-b commented 1 year ago

While I didn't set up test environment, would you like to try the first pass at it - https://github.com/ngs-lang/ngs/commit/0383932674092ab7f3551da59951e989a880fb35 ?

yonas commented 1 year ago

@ilyash-b Thanks for the quick response. That resolved the string error:

        METHOD_RETURN(MAKE_INT(strcasecmp(obj_to_cstring(argv[0]), obj_to_cstring(argv[1]))));
                               ^
/memfs/git/ngs/vm.c:1425:25: note: include the header <strings.h> or explicitly provide a declaration for 'strcasecmp'

but the other errors remain.

ilyash-b commented 1 year ago

OK. Thanks. I'll look into this.

ilyash-b commented 1 year ago

For now, I'm unable to reproduce. I suspect make version or alike.

[root@freebsd ~/ngs]# gmake clean install
rm -rf build
( mkdir -p build && cd build && cmake .. && make )
-- The C compiler identification is Clang 14.0.5
-- The CXX compiler identification is Clang 14.0.5
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found PkgConfig: /usr/local/bin/pkg-config (found version "1.8.1")
-- Checking for module 'libffi'
--   Found libffi, version 3.4.4
-- leg program not found, will download and build it
CMake Warning (dev) at /usr/local/share/cmake/Modules/ExternalProject.cmake:3075 (message):
  The DOWNLOAD_EXTRACT_TIMESTAMP option was not given and policy CMP0135 is
  not set.  The policy's OLD behavior will be used.  When using a URL
  download, the timestamps of extracted files should preferably be that of
  the time of extraction, otherwise code that depends on the extracted
  contents might not be rebuilt if the URL changes.  The OLD behavior
  preserves the timestamps from the archive instead, but this is usually not
  what you want.  Update your project to the NEW behavior or specify the
  DOWNLOAD_EXTRACT_TIMESTAMP option with a value of true to avoid this
  robustness issue.
Call Stack (most recent call first):
  /usr/local/share/cmake/Modules/ExternalProject.cmake:4185 (_ep_add_download_command)
  CMakeLists.txt:42 (ExternalProject_Add)
This warning is for project developers.  Use -Wno-dev to suppress it.

-- Looking for fmemopen
-- Looking for fmemopen - found
-- Looking for execinfo.h
-- Looking for execinfo.h - found
-- Looking for sys/poll.h
-- Looking for sys/poll.h - found
-- Configuring done
-- Generating done
-- Build files have been written to: /root/ngs/build
[  4%] Creating directories for 'leg'
[  9%] Performing download step (download, verify and extract) for 'leg'
-- Downloading...
   dst='/root/ngs/build/leg-prefix/src/peg-0.1.18.tar.gz'
   timeout='none'
   inactivity timeout='none'
-- Using src='https://www.piumarta.com/software/peg/peg-0.1.18.tar.gz'
-- [download 0% complete]
-- [download 28% complete]
-- [download 57% complete]
-- [download 85% complete]
-- [download 100% complete]
-- verifying file...
       file='/root/ngs/build/leg-prefix/src/peg-0.1.18.tar.gz'
-- Downloading... done
-- extracting...
     src='/root/ngs/build/leg-prefix/src/peg-0.1.18.tar.gz'
     dst='/root/ngs/build/leg-prefix/src/leg'
-- extracting... [tar xfz]
-- extracting... [analysis]
-- extracting... [rename]
-- extracting... [clean up]
-- extracting... done
[ 13%] No update step for 'leg'
[ 18%] No patch step for 'leg'
[ 22%] No configure step for 'leg'
[ 27%] Performing build step for 'leg'
In file included from src/peg.c:49:
src/peg.peg-c:380:3: warning: unused label 'l77' [-Wunused-label]
  l77:;   yy->__pos= yypos0; yy->__thunkpos= yythunkpos0;
  ^~~~
src/peg.peg-c:257:16: warning: unused function 'yyPush' [-Wunused-function]
YY_LOCAL(void) yyPush(yycontext *yy, char *text, int count)
               ^
src/peg.peg-c:268:16: warning: unused function 'yyPop' [-Wunused-function]
YY_LOCAL(void) yyPop(yycontext *yy, char *text, int count)   { yy->__val -= count; }
               ^
src/peg.peg-c:269:16: warning: unused function 'yySet' [-Wunused-function]
YY_LOCAL(void) yySet(yycontext *yy, char *text, int count)   { yy->__val[count]= yy->__; }
               ^
4 warnings generated.
make[4]: don't know how to make tree.o. Stop

make[4]: stopped in /root/ngs/build/leg-prefix/src/leg
*** Error code 2

Stop.
make[3]: stopped in /root/ngs/build
*** Error code 1

Stop.
make[2]: stopped in /root/ngs/build
*** Error code 1

Stop.
make[1]: stopped in /root/ngs/build
gmake: *** [Makefile:22: build] Error 1
ilyash-b commented 1 year ago

Reproduced.

Temporarily, I did this crime because other things that I tried to convince cmake to use gmake failed:

[root@freebsd ~/ngs]# ls -l `which make`
lrwxr-xr-x  1 root  wheel  20 May 29 10:04 /usr/bin/make -> /usr/local/bin/gmake
yonas commented 1 year ago

@ilyash-b After installing https://github.com/ivmai/bdwgc I was able to successfully compile. Here are the changes:

diff --git a/CMakeLists.txt b/CMakeLists.txt                                                                                                                                                                         
index 8c01a22..2d53a4f 100644                                                                                                                                                                                        
--- a/CMakeLists.txt                                                                                                                                                                                                 
+++ b/CMakeLists.txt                                                                                                                                                                                                 
@@ -8,12 +8,13 @@ include(CheckFunctionExists)                                                                                                                                                                       
 include(CheckIncludeFile)                                                                                                                                                                                           
 include(FindPkgConfig)                                                                                                                                                                                              
 include(ExternalProject)                                                                                                                                                                                            
-                                                                                                                                                                                                                    
+include(FindBacktrace)                                                                                                                                                                                              

 # -D_DARWIN_C_SOURCE - SIGWINCH and friends on MacOS                                                                                                                                                                
 # -D_XOPEN_SOURCE - strptime on Linux                                                                                                                                                                               
 # -D_DEFAULT_SOURCE - MAP_ANONYMOUS on Linux                                                                                                                                                                        
-add_definitions(-D_XOPEN_SOURCE=700 -D_DARWIN_C_SOURCE=1 -D_DEFAULT_SOURCE=1 -D_BSD_SOURCE)                                                                                                                         
+# -D__BSD_VISIBLE - AF_UNIX on FreeBSD                                                                                                                                                                              
+add_definitions(-D_XOPEN_SOURCE=700 -D_DARWIN_C_SOURCE=1 -D_DEFAULT_SOURCE=1 -D_BSD_SOURCE -D__BSD_VISIBLE)                                                                                                         

 # This is workaround for boehm GC library bug or NGS usage of it                                                                                                                                                    
 # which cases sporadic SIGSEGV and other issues after fork() in child process.                                                                                                                                      
@@ -70,7 +71,7 @@ IF(POLL_H)                                                                                                                                                                                         
 ENDIF()

-find_program(SED NAMES gsed sed) # gsed - MacOS, sed - all the rest
+find_program(SED NAMES gsed sed) # gsed - MacOS and FreeBSD, sed - all the rest
 find_file(PCRE_H pcre.h)
 add_custom_command(
        OUTPUT
@@ -114,7 +115,7 @@ add_custom_command(
 )

-target_link_libraries(ngs m pthread gc ffi dl json-c pcre)
+target_link_libraries(ngs m pthread gc ffi dl json-c pcre ${Backtrace_LIBRARY})

 add_custom_target(man ALL WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc COMMAND make man DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/doc/*.1.md)

diff --git a/build-scripts/docker_push.sh b/build-scripts/docker_push.sh
index 36b6b7c..13ab0f0 100755
--- a/build-scripts/docker_push.sh
+++ b/build-scripts/docker_push.sh
@@ -1,4 +1,4 @@
-#!/bin/bash 
+#!/usr/bin/env bash
 REPO="$1"
 TAG1="$2"
 TAG2="$3"
diff --git a/build-scripts/make-errno-include.sh b/build-scripts/make-errno-include.sh
index 7a0c9e5..7a84f43 100755
--- a/build-scripts/make-errno-include.sh
+++ b/build-scripts/make-errno-include.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash

 set -eu
 set -o pipefail
diff --git a/vm.c b/vm.c
index 9e1fa31..f7404d2 100644
--- a/vm.c
+++ b/vm.c
@@ -6,6 +6,7 @@
 #include <stdarg.h>
 #include <time.h>
 #include <string.h>
+#include <strings.h>

 #ifdef HAVE_POLL_H
 #include <sys/poll.h>

Running test.ngs resulted in a few fails:

 + Test: a=[{"x": 7}, {"x": 8}]; a .= x; a == [7, 8]
 + Test: a=[1,2]; a .= map(F(x) x*2); a[0]+=10; a == [12,4]
 + Test: o = {'a': 1, 'b': [null, false, true, 3.14], 'nothing': null}; decode_json(encode_json(o)) == o
 + Test: {decode_json("1xx")}.assert(JsonDecodeFail)
 + Test: h={'a': 1, 'b': 2, 'c': 3}; h.update({'b': 20, 'd': 40}); h == {'a': 1, 'b': 20, 'c': 3, 'd': 40}
 + Test: F opt(a, b=10, *rest) [b, rest]; opt(1) == [10, []]
 + Test: F opt(a, b=10, *rest) [b, rest]; opt(1, 2) == [2, []]
 + Test: F opt(a, b=10, *rest) [b, rest]; opt(1, 2, 3) == [2, [3]]
 + Test: F opt(a, b=10, *rest) [b, rest]; opt(1, b=10) == [10, []]
 + Test: F opt(a, b=10, *rest, **kw) [b, rest, kw]; opt(1, b=20) == [20, [], {"b": 20}]
 + Test: F opt(a, b=10, *rest, **kw) [b, rest, kw]; opt(1, c=20) == [10, [], {"c": 20}]
 + Test: F opt(a, b=10, *rest) [b, rest]; try opt(1, c=20) == [10, []] catch(e:MethodNotFound) true
 + Test: F opt(a, b=10, *rest, **kw) [b, rest, kw]; opt(1, b=30, **{"b": 20}) == [20, [], {"b": 20}]
 + Test: F opt(a, b=10, *rest, **kw) [b, rest, kw]; opt(1, b=30, **{"b": 20}, b=40) == [40, [], {"b": 40}]
 + Test: F opt(a, b=10, *rest, **kw) [a, b, rest, kw]; opt(1, 100, 200, **{"b": 20}, b=40) == [1, 100, [200], {"b": 40}]
 + Test: F f(a) a; f(**{"a": 10}) == 10
 + Test: F f(a=7) a; f(**{"a": 10}) == 10
 + Test: F f(a,b,*args,**kwargs) [a,b,args,kwargs]; f(kw1=100, 1, 2, 3, kw2=200) == [1, 2, [3], {"kw1":100, "kw2":200}]
 + Test: F construct(x:Int) F() x; f1=construct(1); f2=construct(2); (f1==f1) and (f1!=f2)
 + Test: 1.1 < 1.11
 + Test: 1.1 + 1.1 == 2.2
 + Test: F uniq_attrs_func() "some body"; uniq_attrs_func.Arr()[0].meta({"a": 1}); uniq_attrs_func.Arr()[0].meta() == {"a": 1}
 + Test: meta((+).Arr()[0])['name'] == '+'
 + Test: params((+).Arr()[0])[0]['name'] == 'a'
 + Test: params((+).Arr()[0])[0]['type'] === Real
 + Test: "config" in globals()
 + Test: args=['1', '+', '2']; $(expr $*args).Str()[0..1] == '3'
 + Test: time() > 1466662171
 + Test: a=[10,20,30]; f=a[X]; f(1) == 20
 + Test: a=[10,20,30]; f=X[1]; f(a) == 20
 + Test: %[${true}][0].Type() == Bool
 + Test: %{a ${true}}.values()[0].Type() == Bool
 + Test: [10,20,30,40][0..2] == [10,20]
 + Test: [10,20,30,40][1..2] == [20]
 + Test: (try [10,20,30,40][-1..2] catch(e:IndexNotFound) "OK") == "OK"
 + Test: [10,20,30,40][2..null] == [30, 40]
 + Test: (try [10,20,30,40][2..1] catch(e:InvalidArgument) "OK") == "OK"
 + Test: (try [10,20,30,40][2..10] catch(e:IndexNotFound) "OK") == "OK"
 + Test: [10,20,30,40][4..null] == []
 + Test: a=[1,2,3,4]; a[4..4]=["END"]; a[2..2]=["MIDDLE"]; a[0..0]=["START"]; a[0..1] = ["start"]; a == ["start", 1, 2, "MIDDLE", 3, 4, "END"]
 + Test: h={"a": 1}; h.del("a"); h == {}
 + Test: h={}; try h.del("a") catch(e:KeyNotFound) true
 + Test: h={"a": 1, "b": 2}; h.del("a"); h == {"b": 2}
 + Test: "abcd"[1..3] == "bc"
 + Test: s="abcd"; s[4..4]="END"; s[2..2]="MIDDLE"; s[0..0]="START"; s[0..1] = "s"; s == "sTARTabMIDDLEcdEND"
 + Test: a = Path("a"); b = Path("a"); (a == b) and (a !== b)
 + Test: Path("a") != Path("b")
 + Test: h={}; try h["a"] catch(e:KeyNotFound) true
 + Test: a=[]; try a[0] catch(e:IndexNotFound) true
 + Test: a=[]; try a[0]=1 catch(e:IndexNotFound) true
 + Test: type T; try T().xyz catch(e:FieldNotFound) true
 + Test: try NO_SUCH_GLOBAL catch(e:GlobalNotFound) true
 + Test: try c_lseek(0, 0, "bad arg") catch(e:InvalidArgument) true
 + Test: try compile("xyz(", "something") catch(e:CompileFail) true
 + Test: try 1(2, 3) catch(e:DontKnowHowToCall) true
 + Test: type T; try echo(T(), T()) catch(e:MethodNotFound) true
 + Test: try F f() f(); try f() catch(e:StackDepthFail) true
 + Test: try [].pop() catch(e:EmptyArrayFail) true
 + Test: try [].shift() catch(e:EmptyArrayFail) true
 + Test: try { {myvar=myvar}() } catch(e:UndefinedLocalVar) { e.name == 'myvar'}
 + Test: [{"val": 10, "flag": false}, {"val": 20, "flag": true}].filter(X.flag).val == [20]
 + Test: %[f1 f3].map({"f1": 1, "f2": 2, "f3": 3}.(X)) == [1, 3]
 + Test: [[1, 10], [2,20]].map(X[0]) == [1, 2]
 + Test: [1,2].map([10,20,30][X]) == [20, 30]
 + Test: [[1],[2,3]].filter(X.has(1)) == [[1]]
 + Test: {true}()
 + Test: f = F(x, y) x+y; f(X, 10)(20) == 30
 + Test: ns1 = ns { a = 1; _b = 2; a = 100 }; ns1 == {"a": 100}
 + Test: ns1 = ns { F f(x) x+1; }; ns1::f(1) == 2
 + Test: ns1 = ns { yes_one = 1; no_one = 11; no_two = 12; _exports.=reject(F(k, v) k ~ /^no/) }; ns1 == {"yes_one": 1}
 + Test: ns1 = ns { return 10 }; ns1 == 10
 + Test: ns1 = ns { _exports = 11 }; ns1 == 11
 + Test: global a=110; ns1 = ns(outer_a=a) { a=outer_a+1 }; ns1::a == a+1
 + Test: ns { section "x" { a = 1; _c = 10 }; b = 2; _d = 20 } == %{a 1 b 2}
 + Test: try $(cat NO_SUCH_FILE 2>/dev/null) catch(pf:ProcessFail) true
 + Test: x=1; `line: echo a$x b${x}c` == "a1 b1c"
 + Test: f='/tmp/ngs-redir-test'; $(echo -n abc >$f); $(echo -n def >>$f); txt=`cat $f`; $(rm $f); txt == 'abcdef'
 + Test: f='/tmp/ngs-redir-test'; $(ok: cat /NO-SUCH-FILE 2>$f); txt=`cat $f`; $(rm $f); txt ~ /No such file or directory/
 + Test: F guard_yes_use_value() { guard 10 }; guard_yes_use_value() == 10
 + Test: F guard_yes_discard_value() { guard 10; 20 }; guard_yes_discard_value() == 20
 + Test: F guard_no() { guard 0 }; guard_no.assert(MethodNotFound)
 + Test: 1 != 2
 + Test: (1 != 1) == false
 + Test: 1 !== 2
 + Test: (1 !== 1) == false
 + Test: (1 not in [1,2,3]) == false
 + Test: 10 not in [1,2,3]
 + Test: 1 is not Null
 + Test: (null is not Null) == false
 + Test: [1,2,3].has(1)
 + Test: [1,2,3].has(10).not()
 + Test: [1,2,3].has_no(1).not()
 + Test: [1,2,3].has_no(10)
 + Test: type T1; type T2(T1); T2() is T1
 + Test: block b { b.return(1); 2 } == 1
 + Test: block b { "blah"; 2 } == 2
 + Test: block b1 { [block b2 { b2.return(2); 0 }, 1] } == [2, 1]
 + Test: assert({1/0}, DivisionByZero) is DivisionByZero
 + Test: identity(10) == 10
 + Test: p = partial((-), 10); p(7) == 3
 + Test: p = partial_tail((-), 10); p(7) == -3
 + Test: type T; t=T().set(a=1, b=2); t.a == 1 and t.b == 2
 + Test: type T; t=T(); Str(t) == '<T>'
 + Test: type T; t=T(); t.a=1; t.b=2; Str(t) == '<T a=1 b=2>'
 + Test: type T; t=T(); t.dflt("a", 1); t.a == 1
 + Test: type T; t=T(); t.a = 2; t.dflt("a", 1); t.a == 2
 + Test: type T; t=T(); t.x=1; t2=t.copy(); t2.x==1
 + Test: ({"a": 1}) =~ ({"a": Any})
 + Test: ({"a": 1}) !~ ({"a": Any, "b": Any})
 + Test: ({"a": 1, "b": 2}) !~ ({"a": Any, "b": {false}})
 + Test: ("zz") !~ ({"a": Any, "b": {false}})
 + Test: ({"a": 1}) =~ ({"a": Any, "b": IfExists(Int)})
 + Test: ({"a": 1, "b": 2}) =~ ({"a": Any, "b": IfExists(Int)})
 + Test: ({"a": 1, "b": "x"}) !~ ({"a": Any, "b": IfExists(Int)})
 + Test: [1,2] =~ [Int, Any]
 + Test: [1] !~ [Int, Any]
 + Test: 1 !~ Lit(Int)
 + Test: Int =~ Lit(Int)
 + Test: [1,2,3] =~ Repeat(Int, 3)
 + Test: [1,2,3] !~ Repeat(Int, 2)
 + Test: [1,2,3] =~ Repeat(Int)
 + Test: [1,2,3] !~ Repeat(Str)
 + Test: 1 =~ Not(3)
 + Test: not(1 =~ Not(1))
 + Test: 1 =~ AtPath(Any, Int)
 + Test: [1] =~ AtPath(Any, Int)
 + Test: [1] =~ AtPath([Any], Int)
 + Test: [1] =~ AtPath([0], Int)
 + Test: [1] !~ AtPath([Any, Any], Int)
 + Test: [1] =~ [ResetPath(AtPath([], Int))]
 + Test: [1] !~ [ResetPath(AtPath([Any], Int))]
 + Test: Set([1,2]).mapo(X*2) == Set([2,4])
 + Test: ["abc", 1, "def", 2].map(only(Int, X*2)) == ["abc", 2, "def", 4]
 + Test: [1,null,2,0].filter() == [1, 2]
 + Test: partition([1,10,2,20], X>=10) == [[10, 20], [1, 2]]
 + Test: partition(["abc", "", "def", null]) == [["abc", "def"], ["", null]]
 + Test: [1,2,3].first(X>1, 10) == 2
 + Test: [1,2,3].first(X>5, 10) == 10
 + Test: [1,2,3].first(X>1) == 2
 + Test: {[1,2,3].first(X>5)}.assert(ElementNotFound)
 + Test: [1,2,3].last(X>1, 10) == 3
 + Test: [1,2,3].last(X>5, 10) == 10
 + Test: [1,2,3].last(X>1) == 3
 + Test: {[1,2,3].last(X>5)}.assert(ElementNotFound)
 + Test: ok = false; [1,2,11,3,4].the_one(X>10, F(the_value) { ok = the_value == 11 }); ok
 + Test: ok = false; [1,2,11,3,4].the_one(X>20, nop, found_none = { ok = true }); ok
 + Test: ok = false; [1,2,11,3,4].the_one(Int, nop, found_more = { ok = true }); ok
 + Test: [1,2,11,3,4].the_one(X>10) == 11
 + Test: { [1,2,11,12,4].the_one(X>10) }.assert(ElementNotFound)
 + Test: { [1,2,3,4].the_one(X>10) }.assert(ElementNotFound)
 + Test: [1, "a", "b"].the_one(Int) == 1
 + Test: [1].the_one() == 1
 + Test: { [1, "a", "b"].the_one(Str) }.assert(ElementNotFound)
 + Test: [1,2,3,1,2].take(X<3) == [1,2]
 + Test: [1,2,3,1,2].drop(X<3) == [3,1,2]
 + Test: ["ssh", "IP", "w"].replace("IP", "10.0.0.100") == ['ssh','10.0.0.100','w']
 + Test: [1,2,3].reduce(0, (+)) == 6
 + Test: [10].reduce((+)) == 10
 + Test: [1,2,3].reduce((+)) == 6
 + Test: { [].reduce((+)) }.assert(EmptyEachableFail)
 + Test: ok=false; 7.tap({ok = A==7}) == 7 and ok
 + Test: ( 1 in [1,2,3].Iter()) == true
 + Test: (10 in [1,2,3].Iter()) == false
 + Test: a=[1,2]; a.push_all([3,4]); a==[1,2,3,4]
 + Test: Lines(["ab", "cd"]) + Lines(["ef"]) == Lines(["ab", "cd", "ef"])
 + Test: [].has_index(0) == false
 + Test: [].has_index(-1) == false
 + Test: [100].has_index(0) == true
 + Test: [100].has_index(1) == false
 + Test: [100].has_index(-1) == true
 + Test: [100].has_index(-2) == false
 + Test: type T(ArrLike); [1,2].ensure(T) =~ [1,2]
 + Test: type T(ArrLike); "abc".ensure(T) =~ ["abc"]
 + Test: ("abc" ~ AnyOf(/x/, /a/)) is MatchSuccess
 + Test: ("abc" ~ AnyOf(/x/, /y/)) is MatchFailure
 + Test: ("abc" ~ AnyOf([Int, Str])) is MatchSuccess
 + Test: ("abc" ~ AnyOf([Int, Num])) is MatchFailure
 + Test: {"k1": "v1", "k2": "v2", "k3": "v3"}.filterk(AnyOf("k1", "k3")) == {"k1": "v1", "k3": "v3"}
 + Test: AnyOf([1, false]).Bool()
 + Test: AnyOf([0, false]).Bool().not()
 + Test: {"k1": "v1", "k2": "v2", "kk3": "vv3"}.filterk(AllOf(/^k/, {len(A) <= 2})) == %{k1 v1 k2 v2}
 + Test: AllOf([1, 2]).Bool()
 + Test: AllOf([1, 2, false]).Bool().not()
 + Test: HashLike({"a": 1, "b": 2}).filter(F(k, v) v==1) == HashLike({"a": 1})
 + Test: HashLike({"a": 1}).filter(F(k, v) v==10) == HashLike()
 + Test: type MyHashLike(HashLike); MyHashLike({"a": 1, "b": 2}).filter(F(k, v) v==1) == MyHashLike({"a": 1})
 + Test: h=HashLike({"a": 1, "b": 2}); h.del("a"); h == HashLike({"b": 2})
 + Test: { h=HashLike({"a": 1, "b": 2}); h.del("c") }.assert(KeyNotFound)
 + Test: Set([1,2]) - Set([2]) == Set([1])
 + Test: Set([1,2]) + Set([3]) == Set([1,2,3])
 + Test: Set([1,2]) + Set([3]) <= Set([1,2,3])
 + Test: Set([1,2]) <= Set([1,2,3])
 + Test: not(Set([1,2,4]) <= Set([1,2,3]))
 + Test: s1 = Set([1,2]); s2 = s1.copy(); s1.push(3); s2.Arr() == [1,2]
 + Test: type T(NamedInstances); T.NamedInstances = %[RED GREEN BLUE]; i = [T::RED, T::GREEN, T::BLUE]; i.all(T) and T::RED.name == 'RED'
 + Test: global init; type Color(NamedInstances); F init(c:Color, numval:Int) c.numval = numval; Color.NamedInstances = ns { RED = Color(4); GREEN = Color(2); BLUE = Color(1) ; }; Color.NamedInstances is Namespace and 2.decode(Color) == Color::GREEN and "RED".decode(Color) == Color::RED
 + Test: Real('1.1') == 1.1
 + Test: decode("no", Bool) == false
 + Test: decode("0", Bool) == false
 + Test: decode("yes", Bool) == true
 + Test: { decode("ya", Bool) }.assert(DecodeFail)
 + Test: decode("1", Bool) == true
 + Test: decode("1", Int) == 1
 + Test: decode("1", Real) == 1.0
 + Test: Lines <= Eachable1
 + Test: Lines <= Eachable
 + Test: Int <= Any
 + Test: Eachable <= Lines == false
 + Test: [{}, {"x": 1}].dflt("x", 100) == [{"x": 100}, {"x": 1}]
 + Test: Lock().acquire(F() 1) == 1
 + Test: 0..null is NumRange
 + Test: null..null is PredRange
 + Test: null.."xyz" is PredRange
 + Test: (1..3).map(X*2) == [2, 4]
 + Test: (1...3).map(X*2) == [2, 4, 6]
 + Test: (10..20).first(F(x) x % 3 == 0) == 12
 + Test: not(1 in NumRange(1,2,false,true))
 + Test: 2 in NumRange(1,2,false,true)
 + Test: not(10 in NumRange(1,2,false,true))
 + Test: 20 in NumRange(1,30,false,true)
 + Test: 30 in NumRange(1,30,false,true)
 + Test: not(0 in NumRange(0,0,true,false))
 + Test: len(1..3) == 2
 + Test: len(1...3) == 3
 + Test: NumRange(1, 3, include_start=false, include_end=false).len() == 1
 + Test: 1 =~ 0..5
 + Test: 0 =~ 0..5
 + Test: 5 !~ 0..5
 + Test: ensure(1, 3..10)   == 3
 + Test: ensure(20, 3...10) == 10
 + Test: ensure(12, 3...null) == 12
 + Test: ensure(40, 3..35) == 34
 + Test: ensure(40, 3...35) == 35
 + Test: (collector/"a" collect("b")) == "ab"
 + Test: a=0; {1/1}.finally({a=10}) == 1 and a == 10
 + Test: a=0; (try finally({1/0}, {a=10}) catch(e:DivisionByZero) "OK") == "OK" and a == 10
 + Test: F f() block b { finally({ b.return(7) }, {8}) }; f() == 7
 + Test: [{"x": 1}, {"x": 2}].x == [1, 2]
 + Test: a=[{"x": 1}, {"x": 2}]; a.y = [10, 20]; a == [{"x": 1, "y": 10}, {"x": 2, "y": 20}]
 + Test: "ab" + "cd" == "abcd"
 + Test: [1].get(0) == 1
 + Test: [1].get(5) == null
 + Test: [1].get(0, 10) == 1
 + Test: [1].get(5, 10) == 10
 + Test: [10].get(-1) == 10
 + Test: [10].get(-1, "X") == 10
 + Test: [10,20].get(-5) == null
 + Test: [10,20].get(-5, "X") == "X"
 + Test: 1 in [1,2]
 + Test: not(10 in [1,2])
 + Test: [1, 2] == [1, 2]
 + Test: [1, 3] != [1, 2]
 + Test: [1] != [1, 2]
 + Test: [1, 2] <= [1, 3]
 + Test: not([1, 3] <= [1, 2])
 + Test: [1] <= [1, 2]
 + Test: not([1,2] <= [1])
 + Test: [1,2,10].any(F(elt) elt > 5)
 + Test: [1,2,3].any(F(elt) elt > 5).not()
 + Test: [0, 1, null].any()
 + Test: [0, null].any().not()
 + Test: [1,2,3].all(X<10)
 + Test: [1,2,10].all(X>5).not()
 + Test: [1, 1].all()
 + Test: [1, 1, false].all().not()
 + Test: [0,1,2].none(X>2)
 + Test: [0,1,2].none(X<2).not()
 + Test: [0, 0, false].none()
 + Test: [0, 0, true].none().not()
 + Test: [1,2,3].map(X*4) == [4,8,12]
 + Test: [1,2,3].subset([1,2,3,4])
 + Test: [1].subset([2]) == false
 + Test: r=[]; ["a", "b"].each_idx_val(F(idx, val) r.push([idx, val])); r == [[0, "a"], [1, "b"]]
 + Test: [1,2,3,2].without(2) == [1,3]
 + Test: [1,2,3] - [5,6,1] == [2,3]
 + Test: [1,2,3] - [5,6,1,1,1,1,1,1,1,1,1,1,1,2] == [3]
 + Test: [1,2,3,11,12].count(X>10) == 2
 + Test: [0,1,2,null,false].count() == 2
 + Test: [[1], [2,3]].flatten() == [1,2,3]
 + Test: [1,2,2,3,4,4].uniq() == [1,2,3,4]
 + Test: [{"a": 1, "z": 10}, {"a": 1, "z": 20}, {"a": 2, "z": 30}].uniq(X.a) == [{"a": 1, "z": 10}, {"a": 2, "z": 30}]
 + Test: [null, false, 10, 20].first() == 10
 + Test: [{"id": 100, "a": 1}, {"id": 200, "a": 2}, {"id": 300, "a": 2}].uniq("a") == [{"id": 100, "a": 1}, {"id": 200, "a": 2}]
 + Test: [null, null, 1].index() == 2
 + Test: [1,5,1,10].indexes(X>2) == [1,3]
 + Test: [1,5,null,10].indexes() == [0,1,3]
 + Test: [5,10,15].first(X>7) == 10
 + Test: { [5,10,15].first(X>20) }.assert(ElementNotFound)
 + Test: { [0, false, null].first() }.assert(ElementNotFound)
 + Test: [1,2,3].reverse() == [3,2,1]
 + Test: x=[1,2]; x.unshift(3); x == [3, 1, 2]
 + Test: Arr({'x': 7, 'y': 8}) == [['x', 7], ['y', 8]]
 + Test: zip([1,2,3], [4,5,6]) == [[1,4], [2,5], [3,6]]
 + Test: zip([1,2,3], [4,5,6], [7,8,9,10]) == [[1,4,7], [2,5,8], [3,6,9], [null, null, 10]]
 + Test: Arr(1..3) == [1,2]
 + Test: Arr(1...3) == [1,2,3]
 + Test: a = [1, 2, 3]; a[-1] = 99; a == [1, 2, 99]
 + Test: [10,20,30,40][1..-1] == [20,30]
 + Test: { indexes([1,2,3,4], ((X==1)..(X==14))) }.assert(IndexNotFound)
 + Test: indexes([1,2,3,4], ((X==1)..(X==14)), []) == []
 + Test: %[a1 a2 b1 b2][/^a/../^b/] == %[a2]
 + Test: %[a1 a2 b1 b2][null../^b/] == %[a1 a2]
 + Test: %[a1 a2 b1 b2][/^b/..null] == %[b2]
 + Test: %[a1 a2 b1 b2][/^a/.../^b/] == %[a1 a2 b1]
 + Test: { %[a1 a2 b1 b2][/^a/.../^x/] }.assert(IndexNotFound)
 + Test: { %[a1 a2 b1 b2][/^x/.../^b/] }.assert(IndexNotFound)
 + Test: a = %[a1 a2 b1 b2]; a[/^a/.../^b/] = [7]; a == [7, 'b2']
 + Test: a = %[a1 a2 b1 b2]; m = a ~ /^a/../^b/; m.matches == [['a2']] and m.before == ['a1'] and m.after == ['b1', 'b2']
 + Test: a = %[a1 a2 b1 b2]; m = a ~ /^a/.../^b/; m.matches == [['a1','a2','b1']] and m.before == [] and m.after == ['b2']
 + Test: [10,20,30,40][[0,3]] == [10, 40]
 + Test: [10,11,12].limit(10) == [10,11,12]
 + Test: [10,11,12].limit(2) == [10,11]
 + Test: [10,20] * 2 == [10,20,10,20]
 + Test: [10,20] * [30,40] == [[10, 30], [10, 40], [20, 30], [20, 40]]
 + Test: merge_sorted([1,3,10], [0, 7], (<=)) == [0, 1, 3, 7, 10]
 + Test: merge_sorted([1,3,10], [0, 7, 12, 13, 14], (<=)) == [0, 1, 3, 7, 10, 12, 13, 14]
 + Test: merge_sorted([0, 1], [2, 3], (<=)) == [0, 1, 2, 3]
 + Test: merge_sorted([2, 3], [0, 1], (<=)) == [0, 1, 2, 3]
 + Test: sort([0,5,3,-1], (<=)) == [-1, 0, 3, 5]
 + Test: [{'x': 1}, {'x': 5}, {'x': 3}].sort('x') == [{'x': 1}, {'x': 3}, {'x': 5}]
 + Test: [1, "a", 2, 3, "a", 4].split("a") == [[1], [2, 3], [4]]
 + Test: [1,2,3].intersperse(0) == [1,0,2,0,3]
 + Test: ensure_array([1,2]) == [1,2]
 + Test: ensure_array("aa") == ["aa"]
 + Test: subset({}, {"a": 1})
 + Test: subset({"a": 1}, {"a": 1})
 + Test: subset({"a": 1, "b":2}, {"b": 3, "d": 4}) == false
 + Test: subset({"a": 1}, {"a": 10}) == false
 + Test: {"x": 1, "y": 2}.any(F(k, v) v is Int)
 + Test: {"x": 1, "y": 2}.any(F(k, v) v is Str).not()
 + Test: {"x": 1, "y": 2}.all(F(k, v) v is Int)
 + Test: {"x": 1, "y": "zz"}.all(F(k, v) v is Int).not()
 + Test: ret = []; {"a": 1, "b": 2}.eachk(ret.push(X)); ret == %[a b]
 + Test: ret = []; {"a": 1, "b": 2}.eachv(ret.push(X)); ret == %[1 2]
 + Test: {'a': 1, 'b': 2}.map(F(k, v) "${k}-$v") == ['a-1', 'b-2']
 + Test: mapk({"a": 1}, X+"z") == {"az": 1}
 + Test: mapv({"a": 1}, X+1) == {"a": 2}
 + Test: mapkv({"a": 1}, {[A+"zz", B+10]}) == {"azz": 11}
 + Test: {'a': 1, 'b': 2}.filter(F(k, v) k == 'a') == {'a': 1}
 + Test: {"a1": 1, "a2": 2, "b1": 10}.filterk(/^b/) == {"b1": 10}
 + Test: {0: "zero", 1: "one"}.filterk() == {1: "one"}
 + Test: {"a1": 1, "a2": 2, "b1": 10}.rejectk(/^b/) == {"a1": 1, "a2": 2}
 + Test: {0: "zero", 1: "one"}.rejectk() == {0: "zero"}
 + Test: {"a1": 1, "a2": 2, "b1": 10}.filterv(X>5) == {"b1": 10}
 + Test: {"yes": true, "no": false}.filterv() == {"yes": true}
 + Test: {"a1": 1, "a2": 2, "b1": 10}.rejectv(X>5) == {"a1": 1, "a2": 2}
 + Test: {"yes": true, "no": false}.rejectv() == {"no": false}
 + Test: {'a': 1, 'b': 2, 'c': 11}.count(F(k, v) v>10) == 1
 + Test: {'b': 2, 'c': 11, 'a': 1}.sortk() == {'a': 1, 'b': 2, 'c': 11}
 + Test: {'a': 1, 'b': 2, 'c': 11}.sortk() == {'a': 1, 'b': 2, 'c': 11}
 + Test: {"a": 1, "b": 10, "c": 5}.sortv() == {"a": 1, "c": 5, "b": 10}
 + Test: {"a": 1, "b": 10, "c": 5}.sortv() != {"a": 1, "b": 10, "c": 5}
 + Test: Hash([['a', 1], ['c', 3]]) == {'a': 1, 'c': 3}
 + Test: Hash([{'x': 1}, {'x': 2}], 'x') == {1: {'x': 1}, 2: {'x': 2}}
 + Test: Hash([1,2], F(x) x*2) == {1: 2, 2: 4}
 + Test: Hash(["a", "b", "c"], [1,2,3]) == {"a": 1, "b": 2, "c": 3}
 + Test: Hash([{"Name": "n1", "Value": "v1"},{"Name": "n2", "Value": "v2"}], "Name", "Value") == {"n1": "v1", "n2": "v2"}
 + Test: Hash([{"Name": "n1", "Value": "v1"},{"Name": "n2", "Value": "v2"}]) == {"n1": "v1", "n2": "v2"}
 + Test: {'a': 1, 'b': 2, 'c': 3}.without('a') == {'b': 2, 'c': 3}
 + Test: {'a': 1, 'b': 2, 'c': 3}.without('a', 1).without('b', 22) == {'b': 2, 'c': 3}
 + Test: {'a': 1, 'b': 2, 'c': 3} + {'b': 20, 'd': 40} == {'a': 1, 'b': 20, 'c': 3, 'd': 40}
 + Test: section "order matters" { ns { a=1; c=3; } + ns { a=10; b=2; } == ns { a=10; c=3; b=2; } }
 + Test: { {"a": 1} + ns { b = 2 } }.assert(InvalidArgument)
 + Test: {'a': 1, 'b': 2}.Strs() == ['a=1', 'b=2']
 + Test: {"a": 1, "b": 2}.limit(3) == {"a": 1, "b": 2}
 + Test: {"a": 1, "b": 2}.limit(1) == {"a": 1}
 + Test: [["a", 1], ["b", 2], ["a", 3]].group(X[0], X[1]) == {"a": [1,3], "b": [2]}
 + Test: ["a1", "a2", "b1", "b2", "c1", "d1"].duplicates(F(x) x[0]) == [["a1", "a2"], ["b1", "b2"]]
 + Test: h={"a": 1}; g=copy(h); h.b = 2; ("b" in h) and ("b" not in g)
 + Test: {"b": 2, "c": 3, "a": 1}.sort() == {"a": 1, "b": 2, "c": 3}
 + Test: h={"a": 1}; s = h.shift("a"); h == {} and s == 1
 + Test: h={}; try h.shift("a") catch(e:KeyNotFound) true
 + Test: h={}; h.shift("a", 7) == 7
 + Test: h={"a": 1, "b": 2}; s = h.shift("a"); h == {"b": 2} and s == 1
 + Test: Box(5).any(Int)
 + Test: EmptyBox().any(Str) == false
 + Test: EmptyBox().any(Int) == false
 + Test: Box(5).none(Int) == false
 + Test: Box(5).none(Str)
 + Test: EmptyBox().none(Int)
 + Test: FullBox(null).filter() == EmptyBox()
 + Test: EmptyBox().filter() == EmptyBox()
 + Test: { EmptyBox().get() }.assert(BoxFail)
 + Test: EmptyBox().get(10) == 10
 + Test: Box().get(10) == 10
 + Test: Box() == EmptyBox()
 + Test: Box(null) == EmptyBox()
 + Test: EmptyBox() == EmptyBox()
 + Test: EmptyBox() != FullBox(1)
 + Test: FullBox(1) == FullBox(1)
 + Test: FullBox(1) != FullBox(2)
 + Test: Result({100}).filter() == Success(100)
 + Test: Result({1/0}).filter() is Failure
 + Test: {read("/etc/passwd")}.Result() is Success
 + Test: {read("NO-SUCH-FILE")}.Result() is Failure
 + Test: Success(10).map(X*2).get() == 20
 + Test: { { throw Error("xx") }.Result().map(X*2).get() }.assert(ResultFail)
 + Test: [1,2,3] =~ Present(3)
 + Test: [1,2,3] =~ Absent(0)
 + Test: Diff([1,2], [2,3]) =~ {'add': [3], 'remove': [1]}
 + Test: Diff(["a", "b"], [Present("a"), Present("c"), Absent("b"), Absent("d")], false) =~ {"add": ["c"], "remove": ["b"]}
 + Test: Diff(["a", "b"], [Present("a"), Present("c"), Absent("b"), Absent("d")], true) =~ {"add": ["c"], "remove": ["b"]}
 + Test: Diff({"a": 1, "b":2}, {"b": 3, "d": 4}) =~ {"add":{"d":4}, "change":{"b":3}, "remove":["a"]}
 + Test: 3.map(identity) == [0, 1, 2]
 + Test: 3.map(X*3) == [0, 3, 6]
 + Test: r=0; 5.times(F() r=r+2); r==10
 + Test: Int("100") == 100
 + Test: { Int("100x") }.assert(InvalidArgument)
 + Test: { Int(" XX") }.assert(InvalidArgument)
 + Test: Int(" XX", 36) == 1221
 + Test: { Int("9" * 1000) }.assert(InvalidArgument)
 + Test: true.Int() == 1
 + Test: false.Int() == 0
 + Test: pos("abc", "cd") == null
 + Test: pos("abcdef", "cd") == 2
 + Test: pos("a:b:c", ":") == 1
 + Test: pos("a:b:c", ":", 2) == 3
 + Test: "bc" in "abcd"
 + Test: "x" not in "abcd"
 + Test: ("abc"[0] == "a") and ("abc"[2] == "c")
 + Test: { "abc"[3] == "x" }.assert(IndexNotFound)
 + Test: ("abc"[-1] == "c") and ("abc"[-3] == "a")
 + Test: { "abc"[-4] == "x" }.assert(IndexNotFound)
 + Test: ":a:bc:d:".split(":") == ["", "a", "bc", "d", ""]
 + Test: "bucket_name/dir/file".split("/", 2) == ["bucket_name", "dir/file"]
 + Test: ":a:bc:d:".split("bc") == [":a:", ":d:"]
 + Test: "a:b".before_first(":") == "a"
 + Test: {"a:b".before_first("*")}.assert(InvalidArgument)
 + Test: "a:b".after_last(":") == "b"
 + Test: {"a:b".after_last("*")}.assert(InvalidArgument)
 + Test: Str(10,  4) == '  10'
 + Test: Str(10,  4, '0') == '0010'
 + Test: Str(10, -4) == '10  '
 + Test: Str('x', 3) == 'x  '
 + Test: Str('x',-3) == '  x'
 + Test: Str('x',-3, '.') == '..x'
 + Test: "abcd".starts_with("ab")
 + Test: "ab".starts_with("abcd") == false
 + Test: "abcd".ends_with("cd")
 + Test: "ab".ends_with("cdab") == false
 + Test: "xx\nyy".lines() == %[xx yy]
 + Test: "ab cd\nef".words() == %[ab cd ef]
 + Test: " x y  ".words() == %[x y]
 + Test: "a " + ["1", "2"] == ["a 1", "a 2"]
 + Test: ["1", "2"] + " a" == ["1 a", "2 a"]
 + Test: "abcd".mapo(F(x) if x == "b" then "X" else x) == "aXcd"
 + Test: "abc".limit(5, "...") == "abc"
 + Test: "abcdef".limit(5, "...") == "ab..."
 + Test: "abcdef".limit(2) == "ab"
 + Test: m = (~~)("abcbe", "b", true); m.len() == 5 and m[[0,2,4]] == %[a c e] and m[[1,3]].all(MatchSuccess)
 + Test: "x10ab20c30y".replace("0", "X") == "x1Xab2Xc3Xy"
 + Test: "a$*{[1,2]}b$*{10..12}c" == ["a1b10c", "a1b11c", "a2b10c", "a2b11c"]
 + Test: Stats().push('a').push('a').push('b').Hash() == {'a': 2, 'b': 1}
 + Test: Stats(['a', 'a', 'b']).Hash() == {'a': 2, 'b': 1}
 + Test: Stats(['a', 'a', 'b'])['a'] == 2
 + Test: Stats(['a', 'a', 'b']).a == 2
 + Test: (collector/Stats() { collect("a"); collect("a"); collect("b")}).Hash() == {"a": 2, "b": 1}
 + Test: Stats().collector(F(c) {c("a"); c("a"); c("b")}).Hash() == {"a": 2, "b": 1}
 + Test: try pmap(3, F(x) 100+x) == [100, 101, 102]
 + Test: try pmap(3, F(x) 100/x) catch(e:ResultsException) (e.results[0] is Failure)
 + Test: try pmap(3, F(x) 100/x) catch(e:ResultsException) (e.results[2] is Success) and (e.results[2].get() == 50)
 + Test: e=100; e.pmap(2, X*2) == e.map(X*2)
 + Test: (1..3).Arr().pmap(X*10) == [10, 20]
 + Test: 3.pmap(X*10) == [0, 10, 20]
 + Test: Set(1..10).pfilter(X>3) == Set(1..10).filter(X>3)
 + Test: Set(1..10).pfilter(2, X>3) == Set(1..10).filter(X>3)
 + Test: Path('/').Bool()
 + Test: Path('/no-such-file').Bool() == false
 + Test: Path('/', true) is Dir
 + Test: dir("/").path.any(AnyOf("/var", "/etc", "/bin"))
 + Test: dir("/").all({A.Type().name == 'Path'})
 + Test: dir("/", subtype=true).all({ (A is Path) and (A.Type().name != 'Path') })
 + Test: try assert(Program('no_such_prog')) catch(e) e is AssertFail
 + Test: assert(Program('ls')) == Program('ls')
 + Test: write(1, "W1") == 1
 W1+ Test: { write(10, "W10") }.assert(WriteFail)
 + Test: open(File("/etc/passwd"), "r").fd > 0
 + Test: basename("/abc") == "abc"
 + Test: basename("abc") == "abc"
 + Test: basename("abc/def") == "def"
 + Test: { basename("abc/def/") }.assert(BasenameArgumentFail)
 + Test: ok = false; File("/etc/passwd").lines({if ":" in A ok = true}); ok
 + Test: { $(ok:2 cat NO_SUCH_FILE 2>/dev/null) }.assert(ProcessFail)
 + Test: $(ok:1 cat NO_SUCH_FILE 2>/dev/null); true
 + Test: $(ok:[1,10] cat NO_SUCH_FILE 2>/dev/null); true
 + Test: $(ok:1..2 cat NO_SUCH_FILE 2>/dev/null); true
 + Test: p=$(sleep 10 &); p.kill(); try p.wait() catch(pf:ProcessFail) true
 + Test: p=$(ok_sig:SIGNALS.TERM sleep 10 &); p.kill(); p.wait(); true
 + Test: Argv({"--a": 1, "-b": null, ["-y1", "-n1"]: true, ["-y2", "-n2"]: false, ["-y3"]: true, ["-y4"]: false}) == ["--a", 1, "-y1", "-n2", "-y3"]
 + Test: Argv({"-a1": [], "-a2": [1,2,3]}) == ["-a2", 1, 2, 3]
 + Test: Argv({Repeat("--repeat"): ["a", "b"]}) == ["--repeat", "a", "--repeat", "b"]
 + Test: $(true).Bool()
 + Test: $(false).Bool() == false
 + Test: "$(/bin/echo -n abc)" == 'abc'
 + Test: $(cd:"/" pwd).lines()[0] == '/'
 + Test: t=TmpFile(); $(echo -n abc >$t); t.read() == "abc"
 + Test: counter = 0; p=$(sleep 5&); if p.processes[0].stdout == null counter +=1; p.kill(); try p.wait() catch(e:ProcessFail) true
 + Test: p=$(|cat); p.write("abc"); p.close(); p.Str() == "abc"
 + Test: p=$(|cat|); p.write("abc"); p.read(3) == "abc"
 + Test: $(echo abc | cat).Str().starts_with("abc")
 + Test: decode('a b\nc d', {'KVS': ' '}) == {'a': 'b', 'c': 'd'}
 + Test: decode('a b\nc d', {'FS': ' '}) == [['a', 'b'], ['c', 'd']]
 + Test: ok = false; $(cat /etc/passwd).lines({if ":" in A ok = true}); ok
 + Test: instances = [{"Tags": [{"Key": "k", "Value": "v"},{"Key": "k2", "Value": "v2"}]}]; stdlib_aws_straighten_tags(instances); instances.Tags == [{"k": "v", "k2": "v2"}]
 + Test: instances = ["something"]; stdlib_aws_straighten_tags(instances); instances == ["something"]
 + Test: "abc" - Pfx("a") == "bc"
 + Test: { "abc" - Pfx("wa") }.assert(InvalidArgument)
 + Test: "abc" - MaybePfx("wa") == "abc"
 + Test: "abc" - Sfx("c") == "ab"
 + Test: { "abc" - Sfx("cd") }.assert(InvalidArgument)
 + Test: "abc" - MaybeSfx("cd") == "abc"
 + Test: "abcde" - Ifx("bc") == "ade"
 + Test: { "abcde" - Ifx("xy") }.assert(InvalidArgument)
 + Test: "abcde" - MaybeIfx("xy") == "abcde"
 + Test: [1,2,3] =~ Pfx([1,2])
 + Test: [1,2] =~ Pfx([1,2])
 + Test: [1,2,3] !~ Pfx([2])
 + Test: [1,2] !~ Pfx([1,2,3])
 + Test: ("abc" ~ /a(.)c/)[1] == "b"
 + Test: 1 !~ Pfx("a")
 + Test: m = "xabcy" ~ /a(.)c/; m.matches == ["abc", "b"] and m.before == 'x' and m.after == 'y' and m.whole == 'abc'
 + Test: m = "xAbcy" ~ /a(.)c/; m is MatchFailure
 + Test: ("ab\r\ncd" ~~ /(*CRLF)|./m).whole.filter(len) == ['a', 'b', 'c', 'd']
 + Test: ("ab\r\ncd" ~~ /|./m).whole.filter(len) == ['a', 'b', '\r', 'c', 'd']
 + Test: try ("aяb" ~~ /(*UTF)|\\x8F|b/).whole.filter(len) == ['b'] catch(e:RegExpCompileFail) 'not recognized' in e.message
 + Test: Bool("A" ~ /a/i)
 + Test: "abc01def23" - /[0-9]+/ == "abcdef23"
 + Test: "ab01de12f".split(/[0-9]+/) == ['ab', 'de', 'f']
 + Test: "ab01de12f".without(/[0-9]+/) == 'abdef'
 + Test: "ab01de12f"[/[0-9]+/] == '01'
 + Test: %[w1 w2].filter(/1/) == %[w1]
 + Test: %[1 w2].filter(/1/) == []
 + Test: "x10ab20c30y".replace(/[0-9]+/, F(match_text) "[$match_text]") == "x[10]ab[20]c[30]y"
 + Test: "x10ab20c30y".replace(/[0-9]+/, "@") == "x@ab@c@y"
 + Test: a = Box * 2; a[0] is Box and a[1] is Box and a[0] !== a[1]
 + Test: s=''; for(i;20) s+=chr(ord('A')+i); out=s.rand_uniq(20).Stats(); len(out) == 20 and out.values().all(1)
 + Test: t = 0; retry(body={t+=1; false}, fail_cb={"OK"}, times=5, sleep=0) == "OK" and t == 5
 + Test: t = 0; retry(body={t+=1; "OK"}, fail_cb={"NOTOK"}, sleep=0) == "OK" and t == 1
 + Test: encode_hex('ab') == '6162'
 + Test: decode_hex("010203") == chr(1) + chr(2) + chr(3)
 + Test: { decode_hex("0102034") }.assert(InvalidArgument)
 + Test: encode_uri_component("ab+c%") == "ab%2Bc%25"
 + Test: decode_uri_component("ab%2Bc%25") == "ab+c%"
 + Test: "ab>+c%&'".encode_html() == "ab&gt;+c%&amp;'"
 + Test: "ab>+c%&'".encode_html_attr() == "ab&gt;+c%&amp;&#39;"
 + Test: access(File("/"), bor(ACCESS::R_OK, ACCESS::X_OK))
 + Test: pow(2, 10) == 1024
 + Test: { pow(0, -1) }.assert(InvalidArgument)
 + Test: t = decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"); t =~ AllOf(JWT, {"header": {"alg": "HS256", "typ": "JWT"}, "payload": {"sub": "1234567890", "name": "John Doe", "iat": 1516239022}})
 [ERROR 2023-05-29 11:08:57 EDT] +----------------------------------------------------------------------+
 [ERROR 2023-05-29 11:08:57 EDT] | The arguments did not match any of the methods' parameters.          |
 [ERROR 2023-05-29 11:08:57 EDT] | Method or MultiMethod name: decode                                   |
 [ERROR 2023-05-29 11:08:57 EDT] | Called with arguments' types: Str                                    |
 [ERROR 2023-05-29 11:08:57 EDT] | Please check that you pass the right number of arguments.            |
 [ERROR 2023-05-29 11:08:57 EDT] | Please check that arguments' types are matching methods' parameters. |
 [ERROR 2023-05-29 11:08:57 EDT] +----------------------------------------------------------------------+
 [ERROR 2023-05-29 11:08:57 EDT] Exception of type MethodNotFound occurred
 [ERROR 2023-05-29 11:08:57 EDT] Info: message = (root) = String of length 41: Failed to call method_not_found_handler()
 [ERROR 2023-05-29 11:08:57 EDT] Info: callable:
 [ERROR 2023-05-29 11:08:57 EDT]   (root) - MultiMethod
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr() - Arr of size 12
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[0] - <UserDefinedMethod decode(value:Any, t:Type) at <builtin-stdlib>:1857>
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[0].meta().name = String of length 6: decode
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[0].meta().doc<> = Resolve named instance of t by uniquely-identifying value of any field
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[0].meta().doc<[Example]> = echo("RED".decode(Color))   # <Color numval=4 name=RED>
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[1] - <UserDefinedMethod decode(s:Str, t:Type) at <builtin-stdlib>:1940>
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[1].meta().name = String of length 6: decode
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[1].meta().doc<> = Decode (parse) strings such as command line arguments or environment variables to result given type
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[1].meta().doc<> = TODO: Consider renaming to UnArgv or decode_arg
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[1].meta().doc<[Parameter %STATUS]> = experimental
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[2] - <UserDefinedMethod decode(s:Str, p:Any) at <builtin-stdlib>:5480>
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[2].meta().name = String of length 6: decode
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[2].meta().doc<> = Decode Path and its subtypes
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[2].meta().doc<[Returns]> = instance of Path or a subtype.
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[3] - <UserDefinedMethod decode(s:Str, hints:Hash=...) at <builtin-stdlib>:6593>
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[3].meta().name = String of length 6: decode
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[3].meta().doc<> = Attempt to decode JSON. Uses decode_json().
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[4] - <UserDefinedMethod decode(s:Str, hints:Hash) at <builtin-stdlib>:6602>
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[4].meta().name = String of length 6: decode
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[4].meta().doc<> = EXPERIMENTAL. KVS (key-value separator) hint for decode()
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[4].meta().doc<[Example]> = decode('a b\nc d', {'KVS': ' '}) == {'a': 'b', 'c': 'd'}
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[5] - <UserDefinedMethod decode(s:Str, hints:Hash) at <builtin-stdlib>:6611>
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[5].meta().name = String of length 6: decode
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[5].meta().doc<> = EXPERIMENTAL. FS (field separator) hint for decode()
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[5].meta().doc<[Example]> = decode('a b\nc d', {'FS': ' '})  # [['a', 'b'], ['c', 'd']]
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[6] - <UserDefinedMethod decode(s:Str, hints:Hash) at <builtin-stdlib>:6621>
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[6].meta().name = String of length 6: decode
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[6].meta().doc<> = EXPERIMENTAL! Do not use!
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[6].meta().doc<> = Handle fields_names hint - run decode() and make Arr[Hash] from Arr[Arr] using provided fields_names
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[6].meta().doc<[Example]> = backup_list = fetch('backup.list', {'FS': ' ', 'fields_names': %[env role]})
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[7] - <UserDefinedMethod decode(s:Str, hints:Hash) at <builtin-stdlib>:6631>
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[7].meta().name = String of length 6: decode
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[7].meta().doc<> = Decode "curl -i" to {"code": Int, "message": Str, "headers": Hash, "headers_arr": Hash, "body": Str}
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[7].meta().doc<> = .body is not recursively decoded
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[7].meta().doc<[Returns]> = Hash
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[8] - <UserDefinedMethod decode(s:Str, hints:Hash) at <builtin-stdlib>:6726>
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[8].meta().name = String of length 6: decode
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[8].meta().doc<> = Parse the output of "aws" command. Extracts the array (see "s" below"). For "describe-instances", fl...
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[8].meta().doc<[Parameter s]> = Str containing JSON with a Hash at top level with exactly one Arr as value
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[8].meta().doc<[Parameter hints]> = hints.process.command.argv[0] must be 'aws'
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[8].meta().doc<[Returns]> = data structure, typically an Arr at top level. If s is empty string - null.
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[9] - <UserDefinedMethod decode(s:Str, hints:Hash) at <builtin-stdlib>:6804>
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[9].meta().name = String of length 6: decode
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[9].meta().doc<> = Parse the output of "find" command which does not use "-printf". Handles "-print0".
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[9].meta().doc<[Parameter hints]> = hints.process.command.argv[0] must be 'find'
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[9].meta().doc<[Returns]> = Arr of Str
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[10] - <UserDefinedMethod decode(s:Str, hints:Hash) at <builtin-stdlib>:6815>
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[10].meta().name = String of length 6: decode
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[10].meta().doc<> = Parse the output of "locate" command.
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[10].meta().doc<[Parameter hints]> = hints.process.command.argv[0] must be 'locate'
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[10].meta().doc<[Returns]> = Arr of Str
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[11] - <UserDefinedMethod decode(s:Str, hints:Hash=...) at <builtin-stdlib>:8181>
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[11].meta().name = String of length 6: decode
 [ERROR 2023-05-29 11:08:57 EDT]   .Arr()[11].meta().doc<> = Decodes JSON Web Token (JWT)
 [ERROR 2023-05-29 11:08:57 EDT] Info: args:
 [ERROR 2023-05-29 11:08:57 EDT]   (root) - Arr of size 1
 [ERROR 2023-05-29 11:08:57 EDT]   [0] = String of length 155: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0Ijox...
 [ERROR 2023-05-29 11:08:57 EDT] Frame 0: <builtin-stdlib>:8452:2 - 8452:35
 [ERROR 2023-05-29 11:08:57 EDT] Frame 1: <builtin-stdlib>:8426:21 - 8426:30 [in bootstrap_exception_catch_wrapper]
 [ERROR 2023-05-29 11:08:57 EDT] Frame 2: <builtin-stdlib>:8334:12 - 8334:24 [in bootstrap]
 [ERROR 2023-05-29 11:08:57 EDT] Frame 3: test.ngs:47:7 - 47:11
 [ERROR 2023-05-29 11:08:57 EDT] Frame 4: <builtin-stdlib>:2601:11 - 2601:13 [in each]
 [ERROR 2023-05-29 11:08:57 EDT] Frame 5: test.ngs:31:13 - 31:18 [in perform_tests_in_file]
 [ERROR 2023-05-29 11:08:57 EDT] Frame 6: <builtin-stdlib>:5799:2 - 5799:9 [in lines]
 [ERROR 2023-05-29 11:08:57 EDT] Frame 7: <builtin-stdlib>:2375:8 - 2375:14 [in finally]
 [ERROR 2023-05-29 11:08:57 EDT] Frame 8: <builtin-stdlib>:3935:11 - 3935:14 [in Result]
 [ERROR 2023-05-29 11:08:57 EDT] Frame 9: <builtin-stdlib>:5801:16 - 5801:21
 [ERROR 2023-05-29 11:08:57 EDT] Frame 10: <builtin-stdlib>:4578:12 - 4578:16 [in lines]
 [ERROR 2023-05-29 11:08:57 EDT] Frame 11: <builtin-stdlib>:2601:11 - 2601:13 [in each]
 [ERROR 2023-05-29 11:08:57 EDT] Frame 12: test.ngs:35:4 - 35:16
 [ERROR 2023-05-29 11:08:57 EDT] Frame 13: test.ngs:24:11 - 24:23 [in perform_test]
 [ERROR 2023-05-29 11:08:57 EDT] Frame 14: <test>:1:3 - 1:3
 [ERROR 2023-05-29 11:08:57 EDT] Frame 15: <test>:1:13 - 1:19
 +----------------------------------------------------------------------+
 | The arguments did not match any of the methods' parameters.          |
 | Method or MultiMethod name: decode                                   |
 | Called with arguments' types: Str                                    |
 | Please check that you pass the right number of arguments.            |
 | Please check that arguments' types are matching methods' parameters. |
 +----------------------------------------------------------------------+
ilyash-b commented 1 year ago

@ilyash-b After installing https://github.com/ivmai/bdwgc I was able to successfully compile. Here are the changes:

How did you install it? I did pkg install boehm-gc-threaded and it doesn't seem to work. I'm also preparing instructions for FreeBSD installations and I prefer to have pkg install ... there as it seems to be the "standard" way. Meanwhile I have:

pkg install git gmake cmake pkg-provides pkgconf gsed boehm-gc-threaded json-c peg

Test: t = decode("eyJhbGciOiJIUzI....

Likely, missing base64 binary. Should probably display better error.

Edit: pkg-provides is not actually needed, it was for me

yonas commented 1 year ago

How did you install it?

@ilyash-b git clone https://github.com/ivmai/bdwgc ; cd bdwgc ; mkdir out; cd out ; cmake -G Ninja .. ; ninja ; doas ninja install

Likely, missing base64 binary. Should probably display better error.

Thanks, now all tests pass.

ilyash-b commented 1 year ago

@ilyash-b git clone https://github.com/ivmai/bdwgc ; cd bdwgc ; mkdir out; cd out ; cmake -G Ninja .. ; ninja ; doas ninja install

Just got it to work with the pkg installation.

Enjoy! Feel free to open issues and/or ask questions.