Skip to content

Latest commit

 

History

History
1065 lines (905 loc) · 21.1 KB

File metadata and controls

1065 lines (905 loc) · 21.1 KB

implish tests

#+tanco-format: 0.2

ok : ok: niladic word that returns nil

> ok

The ‘ok’ word is a niladic function (takes no arguments) that evaluates to NIL and produces no output.

get-set-basic : get/set: basic set and get

> set[`x; 42]
42
> get[`x]
42

Basic variable binding using set and retrieval using get. Variables hold mutable state that persists across expressions.

setword-getword-basic : set-word and get-word: syntactic sugar for set and get

> y: 99
99
> y
99
> :y
99

The colon notation provides shorthand: x: value is syntactic sugar for set[`x; value], and :x gets the value without further evaluation. Throughout the rest of these tests, we’ll use this shorthand notation freely.

sequence : sequence: semicolon separates expressions

> (x: 12; y: 34; z: 56)
56

A sequence of expressions separated by semicolons evaluates each in order and returns the value of the last expression. This allows building multi-step programs.

sequence-sugar : sequence: syntactic sugar (whitespace-separated)

> x: 12  y: 34  z: 56
56

Whitespace-separated expressions are syntactic sugar for semicolon-separated sequences. The parentheses and semicolons can be omitted at the top level.

branching-ite : branching: if-then-else with ite

> ite[1; 42; 99]
42
> ite[0; 42; 99]
99
> x: 5
5
> ite[x > 3; `big; `small]
`big

The ite[cond; then; else] function provides conditional branching. The condition is evaluated, and if truthy (non-zero, non-nil), the then branch is evaluated and returned; otherwise the else branch is evaluated and returned. Only one branch is evaluated (lazy evaluation).

repetition-while : repetition: while loop

> (n: 0; while[n < 5; n: n + 1]; n)
5

The while[cond; body] function provides repetition. The condition is evaluated, and while truthy, the body is repeatedly evaluated. The loop returns NIL. This, combined with mutable state (assignment), sequence, and branching, provides the fundamental building blocks for any computable program (Turing-complete).

nil : repl behavior: print everything but nil

> [nil]
[nil]
> nil
> 1 `x
`x

hello.applicative : echo “hello”

> echo "hello"
hello

add : echo[2 + 2]

> echo[2 + 2]
4

outputing “2 2” would be reasonable here if you don’t look ahead for operators. So this test forces you to define operators, and look ahead multiple tokens.

  • next -> echo (verb)
  • have verb, so look ahead for adverb/conjunction/
  • peek -> 2 (noun)
  • okay. we will apply verb to noun
  • fetch full noun phrase, by calling “next”
  • next -> 2 (noun)
  • have noun, so look ahead for .method or infix op

arithmetic : arithmetic (left to right eval)

> echo[1 + 2 * 3 + 5]
14

comma-sequencing : comma sequencing operator

> 2 + 3 * 5 + 7
32
> 2, + 3 * 5, + 7
24
> ! 10, * 2, + 1
1 3 5 7 9 11 13 15 17 19
> ! 10 * 2 + 1
1 3 5 7 9 11 13 15 17 19
> 1 + 2 * ! 10
0 3 6 9 12 15 18 21 24 27
> 1, + 2 * ! 10
1 3 5 7 9 11 13 15 17 19

echo-comment : ignore comments

> echo .: this is a comment :. "hi"
hi

echo-show : echo show “quoted”

> echo show "quoted"
"quoted"

hello.projection : echo[“hello”]

> echo["hello"]
hello

projection.op : +[2;3]

> +[2;3]
5

projection.explicit : +[2] (projection)

> +[2]
+[2]

“verb[noun]”, applies the noun as the verb’s first argument.

projection.pre : 2 + (also projection)

> 2 +
+[2]

“verb[noun]” also applies the noun as the verb’s first argument.

projection.nested : echo[+[2;3]]

> echo[+[2;3]]
5

xmls : echo xmls ‘[1 2; “three”; four]

> echo xmls [1 2; "three"; 'four]
<imp:lst open="[" close="]">
  <imp:ints v="1 2"/>
  <imp:str v="three"/>
  <imp:sym k="lit" v="four"/>
</imp:lst>

word-types-basic : word type round-tripping: basic types

> echo xmls '['foo :bar baz:]
<imp:lst open="[" close="]">
  <imp:sym k="lit" v="foo"/>
  <imp:sym k="get" v="bar"/>
  <imp:sym k="set" v="baz"/>
</imp:lst>

word-types-refn-path-file : word type round-tripping: refinement, path, and file

> echo xmls [/refn foo/bar %file/path]
<imp:lst open="[" close="]">
  <imp:sym k="refn" v="refn"/>
  <imp:sym k="path" v="foo/bar"/>
  <imp:sym k="file" v="file/path"/>
</imp:lst>

word-types-ish : word type round-tripping: issue

# > xmls [#abc]
# [#abc]

word-types-url : word type round-tripping: URL

> echo xmls [http://example.com]
<imp:lst open="[" close="]">
  <imp:sym k="url" v="http://example.com"/>
</imp:lst>

word-types-new1 : new symbol types: backtick and type

> echo xmls [`foo bar!]
<imp:lst open="[" close="]">
  <imp:sym k="bqt" v="foo"/>
  <imp:sym k="typ" v="bar"/>
</imp:lst>

word-types-new2 : new symbol types: annotation and messages

> echo xmls '[@note .msg .kw:]
<imp:lst open="[" close="]">
  <imp:sym k="ann" v="note"/>
  <imp:sym k="msg" v="msg"/>
  <imp:sym k="kw" v="kw"/>
</imp:lst>

word-types-new3 : new symbol types: message2 and keyword2

> echo xmls '[!msg2 !kw2:]
<imp:lst open="[" close="]">
  <imp:sym k="msg2" v="msg2"/>
  <imp:sym k="kw2" v="kw2"/>
</imp:lst>

word-types-err : new symbol type: error

> echo xmls [?error]
<imp:lst open="[" close="]">
  <imp:sym k="err" v="error"/>
</imp:lst>

backtick-tokenization : backtick tokenization: no spaces needed

> echo xmls '[`foo`bar`baz]
<imp:lst open="[" close="]">
  <imp:sym k="bqt" v="foo"/>
  <imp:sym k="bqt" v="bar"/>
  <imp:sym k="bqt" v="baz"/>
</imp:lst>

quoted-list-backtick : quoted list with backtick (quasiquote)

> `[1 2 3]
[1 2 3]

quoted-list-quote : quoted list with quote (strips one layer)

> '[a b c]
[a b c]

quoted-list-nested : quoted list with nested quotes

> '[a 'b '[c]]
[a 'b '[c]]

get-undefined : get/set: get undefined returns fault

> get[`undefined_var]
?undefined_var

When getting an undefined variable, get returns a fault symbol (prefixed with ?).

get-set-vector : get/set: parallel assignment with vector

> set[`a `b `c; 1 2 3]
1 2 3
> get[`a `b `c]
[1, 2, 3]

Parallel assignment distributes vector values to multiple variables.

get-set-broadcast : get/set: scalar broadcast

> set[`p `q `r; 99]
99
> get[`p `q `r]
[99, 99, 99]

When setting multiple variables to a scalar, all get the same value.

get-set-mixed : get/set: mixed defined and undefined

> set[`alpha; 10]
10
> get[`alpha `beta `gamma]
[10 ?beta ?gamma]

Getting a mix of defined and undefined variables returns values and faults.

assignment-basic : assignment: basic

> x: 42
42
> x
42

assignment-expr : assignment: with expression

> y: 2 + 3
5
> y
5

assignment-use : assignment: use assigned value

> a: 10
10
> b: a + 5
15
> b
15

assignment-chained : assignment: chained

> x: y: 42
42
> x
42
> y
42

assignment-in-operator : assignment: in operator context

> 1 + a: 2 3 4
3 4 5
> a
2 3 4
> 5 * b: 3
15
> b
3

load-string : load: parse string

> load "2 + 3"
2 + 3

file-ops : file operations: e?, wr, rd, rm

> f: %hello.txt
%hello.txt
> e? f
0
> wr f "test content"
> e? f
1
> rd f
"test content"
> rm f

quasiquote-basic : quasiquotation: basic unquote

> x: 42
42
> `[1 2 ,x]
[1 2, 42]

quasiquote-expr : quasiquotation: unquote with expression

> a: 10
10
> b: 20
20
> `[sum is ,a plus ,b]
[sum is 10 plus 20]

quasiquote-nested : quasiquotation: nested lists

> y: 99
99
> `[outer [inner ,y]]
[outer [inner 99]]

quasiquote-unquote-lit : quasiquotation: unquote quoted symbol

> x: 'two
'two
> `[one ,x three]
[one two three]

strand-ints : strands: integer vector

> 1 2 3
1 2 3
> echo 1 2 3 4 5
1 2 3 4 5

strand-single : strands: single integer is not a strand

> 42
42

strand-syms : strands: backtick symbol vector

> `a `b `c
`a `b `c
> echo `foo `bar `baz
`foo `bar `baz

strand-scalar-vector : strands: scalar plus vector

> 1 + 0 1 2
1 2 3

strand-ops : strands: separated by operators

> 1 2 + 3 4
4 6

strand-projection : strands: in projection

> echo[1 2 3]
1 2 3

strand-syntactic : strands: purely syntactic (not semantic)

> 1 2 3
1 2 3
> (0) 1
1
> (1 2) 3
3
> a: 2
2
> b: 3
3
> a b
3
> + a b
5

Strands are formed only from adjacent literal tokens (INT, NUM, backtick symbols), not from evaluated expressions or variables. So `+ a b` passes two separate arguments (variables aren’t literals), while `+ 2 3` would form a strand `2 3` as one argument (resulting in partial application).

strand-func-args-comma : strands: as function arguments with comma separator

> + 1 2 3, 4 5 6
5 7 9

When collecting function arguments, strands form within each comma-separated argument. The comma acts as an argument separator.

strand-func-args : strands: passed to functions

> show 1 2 3
"1 2 3"

When a function collects arguments, adjacent literals form a strand that is passed as a single argument. (Note: using `show` instead of `echo` since `echo` writes to stdout which isn’t captured by the test harness)

type-int : type? introspection

> echo show type? 3
int!
> echo show type? "hello"
str!
> echo show type? `foo
sym!
> echo show type? [1 2 3]
lst!
> echo show type? 1 2 3
ints!
> echo show type? nil
nil!

arithmetic-ops : arithmetic operators

> 10 + 5
15
> 10 - 5
5
> 10 * 5
50
> 10 % 3
3
> 2 ^ 8
256

min-max : min and max operators

> min[3; 7]
3
> max[3; 7]
7
> 1 2 3 min 2 4 1
1 2 1

take-nums : tk (take) operator: scalars and vectors

> 5 tk 10
10 10 10 10 10
> 3 tk 1 2
1 2 1
> 4 tk 7 8 9
7 8 9 7

take-str-lst : tk (take) operator: strings and lists

> 7 tk "abc"
"abcabca"
> 5 tk '[x y]
[x y x y x]

rev : rev (reverse)

> rev 1 2 3 4
4 3 2 1
> rev '[a b c]
[c b a]
> rev `x `y `z
`z `y `x

len : len (length)

> len 1 2 3
3
> len "hello"
5
> len '[a b c]
3
> len 42
1

char-num-conversions : chr, ord, hex, oct conversions

> echo chr 65
A
> echo chr 65 66 67
ABC
> ord "A"
65
> ord "AB"
65 66
> echo hex 255
ff
> echo hex -31
-1f
> echo show hex 15 16
["f" "10"]
> echo oct 64
100
> echo oct -9
-11
> echo show oct 8 9
["10" "11"]

range : ! (range/iota)

> ! 5
0 1 2 3 4
> ! 0

> ! 3
0 1 2

part : part (get part of speech)

> part 42
"N"
> part 'echo
"V"
> part '+
"V"

fold-sum-product : fold (/) suffix: sum and product

> +/ 1 2 3 4
10
> */ 1 2 3 4
24
> +/ 5
5

fold-min-max : fold (/) suffix: min and max

> min/ 5 2 8 1 9
1
> max/ 5 2 8 1 9
9

scan-sum : scan (\) suffix: running sum

> +\ 1 2 3 4
1 3 6 10
> *\ 2 3 4
2 6 24

user-fn-basic : user-defined functions: basic

> f: {x + 1}
{x + 1}
> f 5
6
> f 10 20 30
11 21 31

user-fn-dyadic : user-defined functions: dyadic

> add: {x + y}
{x + y}
> add[3; 5]
8

user-fn-composition : user-defined functions: composition

> double: {x * 2}
{x * 2}
> inc: {x + 1}
{x + 1}
> double inc 5
12

user-fn-partial : user-defined functions: partial application

> add: {x + y}
{x + y}
> add3: add[3]
{x + y}[3]
> add3[5]
8

user-fn-first-class : user-defined functions: as first-class values

> {x + 2}
{x + 2}
> {x * 2} 10
20

Functions are first-class values. A standalone function definition `{x + 2}` returns the function itself. Functions can be immediately applied by providing arguments.

user-fn-assign-curly : user-defined functions: assignment with curly braces

> f: {x + 2}
{x + 2}
> g: {x * 3}
{x * 3}
> f 5
7
> g 4
12

Assigning curly brace functions to variables works correctly. The function is stored as a value and can be called later.

user-fn-infix-body : user-defined functions: with infix operators

> calc: {x + y * 2}
{x + y * 2}
> calc[3; 5]
16

Function bodies can contain infix operators. The body is preserved unevaluated until the function is called with arguments. In this case, `3 + 5 * 2` evaluates left-to-right as `(3 + 5) * 2 = 16`.

get-word-function : get-word: return function without evaluation

> f: {x * 2}
{x * 2}
> :f
{x * 2}
> g: :f
{x * 2}
> g 5
10

get-word-partial : get-word: with partial application

> add: {x + y}
{x + y}
> add3: add[3]
{x + y}[3]
> :add3
{x + y}[3]

imparse-equivalence : imparse: equivalence of load and quoted blocks

> imparse load "2 + 3"
+[2 ; 3]
> imparse '[2 + 3]
+[2 ; 3]

Both `load “code”` (creates TOP node) and `’[code]` (regular list) get transformed into TOP nodes with M-expressions.

imparse-ast-postfix : imparse: AST for postfix transformation

> imparse '[2 !]
![2]
> imparse '[5 !]
![5]
> imparse '[10 !]
![10]

The parser transforms postfix unary (a F → F[a]) into M-expression form, returning a TOP node.

imparse-ast-infix : imparse: AST for infix transformation

> imparse '[2 + 3]
+[2 ; 3]
> imparse '[10 - 3]
-[10 ; 3]
> imparse '[4 * 5]
*[4 ; 5]

The parser transforms infix operators (a op b → op[a; b]) into M-expression form, returning a TOP node.

imparse-ast-chains : imparse: AST for infix chains (left-to-right)

> imparse '[2 + 3 * 5]
*[+[2 ; 3] ; 5]
> imparse '[10 - 2 + 5]
+[-[10 ; 2] ; 5]
> imparse '[2 * 3 + 4 * 5]
*[+[*[2 ; 3] ; 4] ; 5]

The parser transforms infix chains left-to-right, creating nested M-expressions in a TOP node.

imparse-ast-skip-prefix : imparse: AST handles prefix cases

> imparse '[! 10]
![10]
> imparse '[! 10 * 2]
*[![10] ; 2]
> imparse '[! 10 * 2 + 1]
+[*[![10] ; 2] ; 1]

The parser now handles prefix verbs! Two-pass transformation: first prefix (! 10 → ![10]), then infix/postfix.

imparse-postfix-left-noun : imparse: postfix verbs with noun on the left

> imparse '[ord "hello" - 32 chr]
chr[-[ord["hello"] ; 32]]
> imparse '["hello" ord - 32 chr]
chr[-[ord["hello"] ; 32]]

Monadic verbs should stay postfix even when a noun appears to their left; both forms parse identically.

imparse-ast-skip-comma : imparse: AST handles comma threading

> imparse '[2, + 3]
+[2 ; 3]
> imparse '[2, + 3 * 5]
+[2 ; *[3 ; 5]]

The parser now handles comma threading! When a comma is followed by a verb, it threads the previous value as the first argument.

imparse-get : imparse: GET symbol transformation

> imparse load ":x"
get[`x]
> imparse load ":foo"
get[`foo]

The parser transforms GET symbols (:word) into M-expression form: get[`word].

imparse-set : imparse: SET symbol transformation

> imparse load "x: 42"
set[`x ; 42]
> imparse load "foo: 99"
set[`foo ; 99]

The parser transforms SET symbols (word: value) into M-expression form: set[`word; value].

imparse-set-expr : imparse: SET with expression RHS

> imparse load "x: 2 + 3"
set[`x ; +[2 ; 3]]
> imparse load "y: 5 * 10 + 1"
set[`y ; +[*[5 ; 10] ; 1]]

The parser transforms the RHS of SET through the full transformation pipeline, converting infix to M-expressions.

imparse-set-chained : imparse: chained SET

> imparse load "a: b: 99"
set[`a ; set[`b ; 99]]
> imparse load "x: y: z: 42"
set[`x ; set[`y ; set[`z ; 42]]]

Chained assignments are right-associative: a: b: 99 becomes set[`a; set[`b; 99]].

imparse-postfix-valence : imparse: postfix with multiple args (valence check)

> imparse load "2 ! 10"
![2 ; 10]
> imparse load "5 ! 20 30"
![5 ; 20 30]

The parser collects trailing nouns after postfix verbs. The evaluator will check arity and throw valence errors if needed.

valence-error : valence errors: too many arguments

> 2 ! 10
Error: [project] !: valence error: expected 1 args, got 2
> ![2; 10]
Error: [project] !: valence error: expected 1 args, got 2

When a function receives more arguments than its arity, the evaluator throws a valence error.

get-set-integration : GET/SET: full integration test

> x: 42
42
> :x
42
> y: x + 10
52
> a: b: c: 99
99
> a + b + c
297

GET (:x) and SET (x: value) transformations integrate seamlessly with the rest of the language.

dict-basic : dictionaries: basic creation and access

> d: :[`a 1; `b 2 + 2]
:[`a 1; `b 4]
> d[`a]
1
> d[`b]
4

Dictionaries are key-value maps. Created with `:[key val; …]` syntax where keys must be backtick symbols.

dict-backtick-access : dictionaries: backtick infix access

> d: :[`a 1; `b 4]
:[`a 1; `b 4]
> d`a
1
> d`b
4

Dictionary values can be accessed using backtick infix syntax: `dict`key`.

dict-multi-key : dictionaries: multi-key access

> d: :[`a 1; `b 2 + 2]
:[`a 1; `b 4]
> d[`a `b]
[1, 4]

Multiple keys can be accessed at once using space-separated strands. Returns a list of values.

dict-empty : dictionaries: empty dictionary

> e: :[]
:[]
> e
:[]

Empty dictionaries are created with `:[]` syntax.

dict-nested : dictionaries: nested values

> d: :[`x [1 2 3]; `y "hello"]
:[`x [1 2 3]; `y "hello"]
> d`x
[1 2 3]
> d`y
"hello"

Dictionary values can be any implish type, including lists and strings.

dict-type : dictionaries: type check

> d: :[`a 1]
:[`a 1]
> echo show type? d
dct!

The type? function returns dct! for dictionaries.

dict-symbol-values : dictionaries: symbol values with comma

> d: :[`c `d; `e `f `g]
:[`c, `d; `e, `f `g]
> d`c
`d
> d`e
`f `g

When a dictionary value is a backtick symbol, it’s printed with a comma to distinguish it from the key. If the value is a strand of symbols, only the first gets the comma.

[0/7] upcoming tests

ambivalent operators

ex: - x is negate, x - y is subtraction x + y is addition, + x is transpose or complex conjugate

grammar rules / definitions / macros

binary expressions

eq =  ne ~: gt >  lt <  ge >: le <:
xr ~: an *. or +. nt -.
lid / rid ? (li/ri?)

proofs

  • unification for rewrite rules
  • hehner has two levels (one for expr, one for proofs)
  • really just same op with two precedence levels
  • quoting might fill the gap?

tokenizer -> xml test?

rln : rln: read line from input

> rln
> hello
"hello"

This test verifies that rln reads a line from the input stream. The input provider can be customized for different environments (Node.js, browser, testing, etc.)

rln-assign : rln: store in variable

> name: rln
> Alice
"Alice"
> echo name
Alice

rln-multiple : rln: multiple reads

> a: rln
> first
"first"
> b: rln
> second
"second"
> a
"first"
> b
"second"

imparse-prefix-precedence : imparse: prefix verbs bind tighter than infix

> imparse '[1 + 2 * ! 10]
*[+[1 ; 2] ; ![10]]

Prefix verbs should bind to their right argument even when an infix verb sits to their left.

paren-infix-prefix : parentheses with prefix inside

> (1 + 2 * ! 10)
0 3 6 9 12 15 18 21 24 27

imparse-infix-monadic-right : infix with monadic verb on the right

> imparse '[1 + 2 * ! 10]
*[+[1 ; 2] ; ![10]]

An infix verb with a monadic verb on the right should apply that verb to its arguments before forming the infix M-expression.