Merge pull request #2 from catseye/develop-0.4
Develop 0.4
Chris Pressey authored 5 years ago
GitHub committed 5 years ago
0 | 0 | History of Robin |
1 | 1 | ================ |
2 | 2 | |
3 | Robin 0.3 (ca 2019) | |
3 | Robin 0.4 (Sep 2019) | |
4 | --------- | |
5 | ||
6 | * Reworked entire specification document, making it properly modular. | |
7 | * Many tests are for the Robin Expression Language, and have been | |
8 | made explicitly so, instead of for the Robin Toplevel Language. | |
9 | * The `bound?` predicate was added to env lib in stdlib. | |
10 | * The `require` top-level form was added. | |
11 | * The `write` command was added to the definition of `line-terminal`. | |
12 | * The `random-u16-source` facility was added to the Reactors | |
13 | specification and to the reference implementation. | |
14 | * Fixed shortcomings in `<` and `>` where operating on large numbers | |
15 | would give incorrect results (thanks wob_jonas!) | |
16 | * Clarified what Robin borrows from PicoLisp (thanks arseniiv!) | |
17 | ||
18 | For the reference implementation, | |
19 | ||
20 | * Added `eval` command-line option, to evaluate a Robin expression | |
21 | given in a text file (mostly to support Robin Expression tests.) | |
22 | * Added a small, crude QuickCheck test suite. | |
23 | ||
24 | Robin 0.3 (Aug 2019) | |
4 | 25 | --------- |
5 | 26 | |
6 | 27 | * The "intrinsics wrappers" were removed. Their semantics have been |
52 | 73 | --------- |
53 | 74 | |
54 | 75 | Initial language. |
76 | ||
77 | Pre-history (ca 2010) | |
78 | ----------- | |
79 | ||
80 | Robin was originally a design for a [Pixley][]-based operating system (or something | |
81 | similar to an operating system) which was heavily resource-oriented; almost | |
82 | everything, including every concurrent process, was a virtual device | |
83 | which must be acquired from a central resource arbiter. This arbiter could | |
84 | satisfy the constraints specified when requesting a device any way it saw | |
85 | fit; so the operating environment potentially had a lot of influence over | |
86 | exactly what any given program does. | |
87 | ||
88 | Not a lot of that idea remains, but it did influence the fact that Robin should | |
89 | be a purely functional language which nevertheless interacts with the rest of the | |
90 | world through some kind of framework. | |
91 | ||
92 | [Pixley]: https://catseye.tc/node/Pixley |
0 | 0 | Robin |
1 | 1 | ===== |
2 | 2 | |
3 | _Version 0.3. Work-in-progress, subject to change._ | |
3 | _Version 0.4. Work-in-progress, subject to change._ | |
4 | 4 | |
5 | 5 | Overview |
6 | 6 | -------- |
56 | 56 | |
57 | 57 | APPLIANCES="appliances/robin.md" ./test.sh |
58 | 58 | |
59 | There are also some QuickCheck tests which you can run with | |
60 | ||
61 | ghc -isrc src/QuickCheckTests.hs -e testAll | |
62 | ||
59 | 63 | Extended Description |
60 | 64 | -------------------- |
61 | 65 | |
65 | 69 | ### Scheme ### |
66 | 70 | |
67 | 71 | Like [Scheme][], Robin is eagerly evaluated, latently typed, and homoiconic, |
72 | as well as properly tail-recursive and lexically scoped (at least by default), | |
68 | 73 | and tries hard to be well-defined and system-agnostic, but (as you can read |
69 | 74 | below) diverges significantly from Scheme in other ways. |
70 | 75 | |
78 | 83 | |
79 | 84 | ### PicoLisp ### |
80 | 85 | |
81 | [PicoLisp][] has both macros and functions, but in Robin, the macro, rather | |
82 | than the function, is the fundamental abstraction. There is a `function` | |
83 | form, but it's defined as a macro! | |
86 | [PicoLisp][] allows functions that do not evaluate their arguments. Robin | |
87 | takes this concept and calls it a `macro`, and builds everything else on | |
88 | top of it. There is a `function` form in Robin, but it's defined as a macro! | |
84 | 89 | |
85 | 90 | ### Haskell ### |
86 | 91 |
33 | 33 | |
34 | 34 | Rename "small" to "core" or "base" or something. |
35 | 35 | |
36 | `(bound? sym)` returns `#t` if the symbol is bound, `#f` if not. | |
37 | ||
38 | 36 | `(compose f1 f2)` composes two functions. |
39 | ||
40 | `macro?` should return `#t` on intrinsics, because they are | |
41 | intrinsic macros. Maybe a separate `intrinsic?` predicate. | |
42 | 37 | |
43 | 38 | Other libs |
44 | 39 | ---------- |
55 | 50 | Note that this relies on the assumption that all standard symbols |
56 | 51 | have their standard meanings. |
57 | 52 | |
58 | Toplevels | |
59 | --------- | |
60 | ||
61 | `(require foo)` is conceptually sugar for `(assert (bound? foo))` | |
62 | but doesn't need `bound?` to be defined, and is also declarative, | |
63 | so an implementation can read it and perhaps try to find `foo` | |
64 | and load it. | |
65 | ||
66 | The intrinsics defining files need not be entirely empty; they | |
67 | should `(requires thesym)` because, being an intrinsic, it should | |
68 | be implemented, and available, and if it's not, it should fail. | |
69 | ||
70 | 53 | Tests |
71 | 54 | ----- |
72 | 55 | |
73 | "Execute", not "Interpret", a Robin program (as these tests could also | |
74 | be used to test compilers.) | |
56 | Tests for Evaluate Robin Expression (with literal) | |
75 | 57 | |
76 | A way to evaluate a Robin expression and display it, mainly | |
77 | to make the tests more concise - don't need to say `(display ...)` always. | |
58 | Grep for FIXME and TODO in stdlib. | |
78 | 59 | |
79 | 60 | Reactors |
80 | 61 | -------- |
102 | 83 | Rename `fun` to `function`. This is because Robin prefers full words |
103 | 84 | over abbreviations, which are jargon-y. |
104 | 85 | |
86 | Rename `>` to `greater-than?`, and so forth. Figure out a place to | |
87 | put the aliases-which-are-punctuation-y. | |
88 | ||
105 | 89 | Rename `raise` to `throw`. This is because `throw` is the opposite |
106 | 90 | of `catch`. Also "raise" suggests an error, but it might be merely a |
107 | 91 | non-local exit. |
0 | -> Functionality "Interpret core Robin Program" is implemented by shell command | |
0 | The following functionalities are used to test Robin Expressions. | |
1 | ||
2 | -> Functionality "Evaluate core Robin Expression" is implemented by shell command | |
3 | -> "bin/robin --no-builtins eval %(test-body-file)" | |
4 | ||
5 | -> Functionality "Evaluate Robin Expression (with Small)" is implemented by shell command | |
6 | -> "bin/robin --no-builtins pkg/small.robin eval %(test-body-file)" | |
7 | ||
8 | -> Functionality "Evaluate Robin Expression (with List)" is implemented by shell command | |
9 | -> "bin/robin --no-builtins pkg/small.robin pkg/list.robin eval %(test-body-file)" | |
10 | ||
11 | -> Functionality "Evaluate Robin Expression (with Env)" is implemented by shell command | |
12 | -> "bin/robin --no-builtins pkg/small.robin pkg/list.robin pkg/env.robin eval %(test-body-file)" | |
13 | ||
14 | -> Functionality "Evaluate Robin Expression (with Boolean)" is implemented by shell command | |
15 | -> "bin/robin --no-builtins pkg/small.robin pkg/boolean.robin eval %(test-body-file)" | |
16 | ||
17 | -> Functionality "Evaluate Robin Expression (with Arith)" is implemented by shell command | |
18 | -> "bin/robin --no-builtins pkg/small.robin pkg/arith.robin eval %(test-body-file)" | |
19 | ||
20 | -> Functionality "Evaluate Robin Expression (with Stdlib)" is implemented by shell command | |
21 | -> "bin/robin --no-builtins pkg/stdlib-no-builtins.robin eval %(test-body-file)" | |
22 | ||
23 | The following functionalities are used to test Robin Toplevel programs. | |
24 | ||
25 | -> Functionality "Execute core Robin Toplevel Program" is implemented by shell command | |
1 | 26 | -> "bin/robin --no-builtins %(test-body-file)" |
2 | 27 | |
3 | -> Functionality "Interpret Robin Program" is implemented by shell command | |
28 | -> Functionality "Execute Robin Toplevel Program (with Small)" is implemented by shell command | |
4 | 29 | -> "bin/robin --no-builtins pkg/small.robin %(test-body-file)" |
5 | ||
6 | -> Functionality "Interpret Robin Program (with Small)" is implemented by shell command | |
7 | -> "bin/robin --no-builtins pkg/small.robin %(test-body-file)" | |
8 | ||
9 | -> Functionality "Interpret Robin Program (with List)" is implemented by shell command | |
10 | -> "bin/robin --no-builtins pkg/small.robin pkg/list.robin %(test-body-file)" | |
11 | ||
12 | -> Functionality "Interpret Robin Program (with Env)" is implemented by shell command | |
13 | -> "bin/robin --no-builtins pkg/small.robin pkg/list.robin pkg/env.robin %(test-body-file)" | |
14 | ||
15 | -> Functionality "Interpret Robin Program (with Boolean)" is implemented by shell command | |
16 | -> "bin/robin --no-builtins pkg/small.robin pkg/boolean.robin %(test-body-file)" | |
17 | ||
18 | -> Functionality "Interpret Robin Program (with Arith)" is implemented by shell command | |
19 | -> "bin/robin --no-builtins pkg/small.robin pkg/arith.robin %(test-body-file)" | |
20 | ||
21 | -> Functionality "Interpret Robin Program (with List-Arith)" is implemented by shell command | |
22 | -> "bin/robin --no-builtins pkg/small.robin pkg/list.robin pkg/arith.robin pkg/list-arith.robin %(test-body-file)" | |
23 | ||
24 | -> Functionality "Interpret Robin Program (with Stdlib)" is implemented by shell command | |
25 | -> "bin/robin --no-builtins pkg/stdlib-no-builtins.robin %(test-body-file)" |
0 | -> Functionality "Interpret core Robin Program" is implemented by shell command | |
0 | The following functionalities are used to test Robin Expressions. | |
1 | ||
2 | -> Functionality "Evaluate core Robin Expression" is implemented by shell command | |
3 | -> "bin/robin --no-builtins eval %(test-body-file)" | |
4 | ||
5 | -> Functionality "Evaluate Robin Expression (with Small)" is implemented by shell command | |
6 | -> "bin/robin eval %(test-body-file)" | |
7 | ||
8 | -> Functionality "Evaluate Robin Expression (with List)" is implemented by shell command | |
9 | -> "bin/robin pkg/list.robin eval %(test-body-file)" | |
10 | ||
11 | -> Functionality "Evaluate Robin Expression (with Env)" is implemented by shell command | |
12 | -> "bin/robin pkg/list.robin pkg/env.robin eval %(test-body-file)" | |
13 | ||
14 | -> Functionality "Evaluate Robin Expression (with Boolean)" is implemented by shell command | |
15 | -> "bin/robin pkg/boolean.robin eval %(test-body-file)" | |
16 | ||
17 | -> Functionality "Evaluate Robin Expression (with Arith)" is implemented by shell command | |
18 | -> "bin/robin pkg/arith.robin eval %(test-body-file)" | |
19 | ||
20 | -> Functionality "Evaluate Robin Expression (with Stdlib)" is implemented by shell command | |
21 | -> "bin/robin pkg/stdlib.robin eval %(test-body-file)" | |
22 | ||
23 | The following functionalities are used to test Robin Toplevel programs. | |
24 | ||
25 | -> Functionality "Execute core Robin Toplevel Program" is implemented by shell command | |
1 | 26 | -> "bin/robin --no-builtins %(test-body-file)" |
2 | 27 | |
3 | -> Functionality "Interpret Robin Program" is implemented by shell command | |
28 | -> Functionality "Execute Robin Toplevel Program (with Small)" is implemented by shell command | |
4 | 29 | -> "bin/robin %(test-body-file)" |
5 | ||
6 | -> Functionality "Interpret Robin Program (with Small)" is implemented by shell command | |
7 | -> "bin/robin %(test-body-file)" | |
8 | ||
9 | -> Functionality "Interpret Robin Program (with List)" is implemented by shell command | |
10 | -> "bin/robin pkg/list.robin %(test-body-file)" | |
11 | ||
12 | -> Functionality "Interpret Robin Program (with Boolean)" is implemented by shell command | |
13 | -> "bin/robin pkg/boolean.robin %(test-body-file)" | |
14 | ||
15 | -> Functionality "Interpret Robin Program (with Env)" is implemented by shell command | |
16 | -> "bin/robin pkg/list.robin pkg/env.robin %(test-body-file)" | |
17 | ||
18 | -> Functionality "Interpret Robin Program (with Arith)" is implemented by shell command | |
19 | -> "bin/robin pkg/arith.robin %(test-body-file)" | |
20 | ||
21 | -> Functionality "Interpret Robin Program (with List-Arith)" is implemented by shell command | |
22 | -> "bin/robin pkg/list.robin pkg/arith.robin pkg/list-arith.robin %(test-body-file)" | |
23 | ||
24 | -> Functionality "Interpret Robin Program (with Stdlib)" is implemented by shell command | |
25 | -> "bin/robin pkg/stdlib.robin %(test-body-file)" |
28 | 28 | stdlib/not.robin stdlib/and.robin stdlib/or.robin \ |
29 | 29 | stdlib/xor.robin > pkg/boolean.robin |
30 | 30 | |
31 | cat stdlib/env-p.robin stdlib/export.robin stdlib/sandbox.robin \ | |
31 | cat stdlib/env-p.robin stdlib/bound-p.robin stdlib/export.robin stdlib/sandbox.robin \ | |
32 | 32 | stdlib/unbind.robin stdlib/unshadow.robin > pkg/env.robin |
33 | 33 | |
34 | 34 | cat stdlib/itoa.robin > pkg/misc.robin |
8 | 8 | echo "ghc not found, not building $PROG.exe" |
9 | 9 | fi |
10 | 10 | |
11 | # For this to work, you need hastec installed. | |
12 | # You also need parsec installed in a way that haste can use it: | |
13 | # | |
14 | # haste-cabal install parsec-3.1.1 | |
15 | # | |
16 | # Later versions might not work. For example, 3.1.13.0 fails to build for me at: | |
17 | # Preprocessing library generic-deriving-1.12.3... | |
18 | # src/Generics/Deriving/TH/Pre4_9.hs:177:20: | |
19 | # parse error on input ‘->’ | |
20 | # | |
21 | # You also need random installed in a way that haste can use it: | |
22 | # | |
23 | # haste-cabal install random-1.1 | |
24 | # | |
25 | ||
11 | 26 | if command -v hastec >/dev/null 2>&1; then |
12 | 27 | echo "building $PROG.js with hastec" |
13 | 28 | (cd src && hastec --make HasteMain.hs -o ../demo/$PROG.js) || exit 1 |
14 | 29 | else |
15 | 30 | echo "hastec not found, not building $PROG.js" |
16 | 31 | fi |
32 | ||
33 | ./build-packages.sh |
180 | 180 | |
181 | 181 | <p><i>(alist)</i> lookup extend delete</p> |
182 | 182 | |
183 | <p><i>(env)</i> env? export sandbox unbind unshadow</p> | |
183 | <p><i>(env)</i> env? bound? export sandbox unbind unshadow</p> | |
184 | 184 | |
185 | 185 | <p><i>(arith)</i> abs add > >= < <= multiply divide remainder</p> |
186 | 186 |
0 | 0 | Robin |
1 | 1 | ===== |
2 | 2 | |
3 | This document defines version 0.3 of the Robin programming language. | |
3 | This document defines version 0.4 of the Robin programming language. | |
4 | 4 | |
5 | 5 | The Robin specification is modular in the sense that it consists |
6 | 6 | of several smaller specifications, some of which depend on others, |
7 | 7 | that can be composed or used in isolation. These specifications are: |
8 | 8 | |
9 | * Robin Syntax | |
10 | * Robin Expressions | |
11 | * Robin Reactors | |
12 | * Robin Toplevel | |
13 | ||
14 | Expressions, Reactors, and Toplevel are written in the Syntax. | |
15 | ||
16 | Data Types and Intrinsics and "Small" Library and Standard Library are | |
17 | concepts used in Expressions. | |
18 | ||
19 | Reactors are defined using Expressions. A Toplevel names Expressions | |
20 | and Reactors. | |
21 | ||
22 | For historical reasons, this document is not organized in sections | |
23 | corresponding to these sub-specifications. In a future version of Robin, | |
24 | we hope that it will be. | |
25 | ||
26 | -> Tests for functionality "Interpret core Robin Program" | |
27 | ||
28 | Top-level S-expressions | |
29 | ----------------------- | |
30 | ||
31 | A Robin program consists of a series of "top-level" S-expressions. | |
32 | Each top-level S-expression must have a particular form, but most of these | |
33 | top-level S-expressions may contain general, evaluatable S-expressions | |
34 | themselves. Allowable top-level forms are given in the subsections below. | |
35 | ||
36 | ### `display` ### | |
37 | ||
38 | `(display EXPR)` evaluates the EXPR and displays the result in a canonical | |
39 | S-expression rendering, followed by a newline. | |
40 | ||
41 | | (display #t) | |
42 | = #t | |
43 | ||
44 | Note that a Robin program may be split over several files in the filesystem. | |
45 | Also, more than one top-level S-expression may appear in a single file. | |
46 | ||
47 | | (display #t) | |
48 | | (display #f) | |
49 | = #t | |
50 | = #f | |
51 | ||
52 | ### `assert` ### | |
53 | ||
54 | `(assert EXPR)` evaluates the EXPR and, if there was an error evaluating | |
55 | the EXPR, or if the EXPR evaluates to `#f`, aborts processing the file. | |
56 | ||
57 | | (assert #t) | |
58 | = | |
59 | ||
60 | | (assert #f) | |
61 | ? assertion failed: #f | |
62 | ||
63 | | (assert this-identfier-is-not-bound) | |
64 | ? unbound-identifier | |
65 | ||
66 | ### `define` ### | |
67 | ||
68 | `(define ATOM EXPR)` defines a global name. | |
69 | ||
70 | | (define true #t) | |
71 | | (display true) | |
72 | = #t | |
73 | ||
74 | You may not try to define anything that's not an atom. | |
75 | ||
76 | | (define #f #t) | |
77 | | (display #f) | |
78 | ? illegal top-level form: (define #f #t) | |
79 | ||
80 | You may define multiple names. | |
81 | ||
82 | | (define true #t) | |
83 | | (define false #f) | |
84 | | (display false) | |
85 | | (display true) | |
86 | = #f | |
87 | = #t | |
88 | ||
89 | Names may not be redefined once defined. | |
90 | ||
91 | | (define true #t) | |
92 | | (define true #f) | |
93 | ? symbol already defined: true | |
94 | ||
95 | Names previously defined can be used in a definition. | |
96 | ||
97 | | (define true #t) | |
98 | | (define also-true true) | |
99 | | (display also-true) | |
100 | = #t | |
101 | ||
102 | Names that are not yet defined cannot be used in a definition, even if | |
103 | they are defined later on in the file. | |
104 | ||
105 | | (define also-true true) | |
106 | | (define true #t) | |
107 | | (display also-true) | |
108 | ? unbound-identifier | |
109 | ||
110 | ### `reactor` ### | |
111 | ||
112 | `(reactor LIST-OF-ATOMS STATE-EXPR BODY-EXPR)` installs a reactor. Reactors | |
113 | permit the construction of reactive Robin programs. See the | |
114 | [Reactors](#reactors) section for more information on reactors. | |
115 | ||
116 | Intrinsic Data Types | |
9 | * Part 0. Robin Syntax | |
10 | * Part 1. Robin Expression Language | |
11 | * (a) Evaluation Rules | |
12 | * (b) Intrinsic Data Types | |
13 | * (c) Conventional Data Types | |
14 | * (d) Standard Environments | |
15 | * Part 2. Robin Toplevel Language | |
16 | * Part 3. Robin Reactors | |
17 | ||
18 | Robin Expressions and Robin Toplevels are written in the Robin Syntax. | |
19 | A Reactor is defined with Robin Expressions. A Toplevel contains | |
20 | Expressions used for various purposes, including Reactors. | |
21 | ||
22 | Note that, although each part of the specification builds on the | |
23 | parts before it, it is not really possible to give testable examples | |
24 | of some of the parts, without referring to parts that have not yet | |
25 | been seen. (For example, when describing Robin Syntax, we would | |
26 | like to show that it allows one to write Robin Expressions.) Thus | |
27 | many of these examples are given in the Robin Toplevel Language, | |
28 | even though they need not strictly be. | |
29 | ||
30 | Part 0. Robin Syntax | |
117 | 31 | -------------------- |
118 | 32 | |
33 | -> Tests for functionality "Execute core Robin Toplevel Program" | |
34 | ||
119 | 35 | ### S-expressions ### |
120 | 36 | |
121 | An S-expression is a sort of catch-all data type which includes | |
37 | Robin is an S-expression based language, so it has a syntax similar to | |
38 | Common Lisp, Scheme, Racket, and so forth. | |
39 | ||
40 | The basic grammar of these S-Expressions, given in EBNF, is: | |
41 | ||
42 | SExpr ::= [";"] (symbol | number | boolean | Quoted | "(" {SExpr} ")") | |
43 | Quoted ::= "'" sentinel "'" arbitrary-text-not-containing-sentinel "'" sentinel "'" | |
44 | ||
45 | A symbol is denoted by a string which may contain only alphanumeric | |
46 | characters and certain other characters. | |
47 | ||
48 | A number is denoted by a string of decimal digits. | |
49 | ||
50 | A boolean is denoted `#t` or `#f`. | |
51 | ||
52 | A sentinel is any string not containing a single quote (`'`). In a Quoted | |
53 | production, the start sentinel and the end sentinel must match. | |
54 | ||
55 | ### Arbitrary literal strings | |
56 | ||
57 | Robin supports a sugared syntax for specifying literal strings. | |
58 | The characters of the string are given between pairs of single quotes. | |
59 | Such a form is parsed as a conventional string data type (see | |
60 | the "String" section in the Robin Expression Language for details.) | |
61 | ||
62 | | (define literal (macro (s a e) (head a))) | |
63 | | (display | |
64 | | (literal ''Hello'')) | |
65 | = (72 101 108 108 111) | |
66 | ||
67 | A single single quote may appear in string literals of this kind. | |
68 | ||
69 | | (define literal (macro (s a e) (head a))) | |
70 | | (display | |
71 | | (literal ''He'llo'')) | |
72 | = (72 101 39 108 108 111) | |
73 | ||
74 | Between the single quotes delimiting the string literal, a *sentinel* | |
75 | may be given. The sentinel between the leading single quote pair must | |
76 | match the sentinel given between the trailing single quote pair. The | |
77 | sentinel may consist of any text not containing a single quote. | |
78 | ||
79 | | (define literal (macro (s a e) (head a))) | |
80 | | (display | |
81 | | (literal 'X'Hello'X')) | |
82 | = (72 101 108 108 111) | |
83 | ||
84 | | (define literal (macro (s a e) (head a))) | |
85 | | (display | |
86 | | (literal '...('Hello'...(')) | |
87 | = (72 101 108 108 111) | |
88 | ||
89 | | (define literal (macro (s a e) (head a))) | |
90 | | (display | |
91 | | (literal 'X'Hello'Y')) | |
92 | ? unexpected end of input | |
93 | ||
94 | A sentinelized literal like this may embed a pair of single quotes. | |
95 | ||
96 | | (define literal (macro (s a e) (head a))) | |
97 | | (display | |
98 | | (literal 'X'Hel''lo'X')) | |
99 | = (72 101 108 39 39 108 111) | |
100 | ||
101 | By choosing different sentinels, string literals may contain any other | |
102 | string literal. | |
103 | ||
104 | | (define literal (macro (s a e) (head a))) | |
105 | | (display | |
106 | | (literal 'X'Hel'Y'bye'Y'lo'X')) | |
107 | = (72 101 108 39 89 39 98 121 101 39 89 39 108 111) | |
108 | ||
109 | No interpolation of escape sequences is done in a Robin string literal. | |
110 | (Functions to convert escape sequences commonly found in other languages | |
111 | may one day be available in a standard module.) | |
112 | ||
113 | | (define literal (macro (s a e) (head a))) | |
114 | | (display | |
115 | | (literal ''Hello\nworld'')) | |
116 | = (72 101 108 108 111 92 110 119 111 114 108 100) | |
117 | ||
118 | All characters which appear in the source text between the delimiters | |
119 | of the string literal are literally included in the string. | |
120 | ||
121 | | (define literal (macro (s a e) (head a))) | |
122 | | (display | |
123 | | (literal ''Hello | |
124 | | world'')) | |
125 | = (72 101 108 108 111 10 119 111 114 108 100) | |
126 | ||
127 | Adjacent string literals are not automatically concatenated. | |
128 | ||
129 | | (define literal (macro (s a e) (head a))) | |
130 | | (display | |
131 | | (literal (''Hello'' ''world''))) | |
132 | = ((72 101 108 108 111) (119 111 114 108 100)) | |
133 | ||
134 | ### Comments | |
135 | ||
136 | -> Tests for functionality "Evaluate core Robin Expression" | |
137 | ||
138 | Any S-expression preceded by a `;` symbol is a comment. It will still | |
139 | be parsed, but it will be ignored. | |
140 | ||
141 | | ( | |
142 | | ;(this expression evaluates to a list of two booleans) | |
143 | | prepend #f (prepend #f ())) | |
144 | = (#f #f) | |
145 | ||
146 | Because S-expressions may nest, and because comments may appear | |
147 | inside S-expressions, comments may nest. | |
148 | ||
149 | | ( | |
150 | | ;(this expression evaluates to | |
151 | | ;(what you might call) | |
152 | | a list of two booleans) | |
153 | | prepend #f (prepend #f ())) | |
154 | = (#f #f) | |
155 | ||
156 | Comments are still parsed. A syntax error in a comment is an error! | |
157 | ||
158 | | ( | |
159 | | ;(this expression evaluates to | |
160 | | #k | |
161 | | a list of booleans) | |
162 | | prepend #f (prepend #f ())) | |
163 | ? (line 3, column 6): | |
164 | ? unexpected "k" | |
165 | ? expecting "t" or "f" | |
166 | ||
167 | Any number of comments may appear together. | |
168 | ||
169 | | (prepend ;what ;on ;earth #f (prepend #f ()))) | |
170 | = (#f #f) | |
171 | ||
172 | Comments may appear before a closing parenthesis. | |
173 | ||
174 | | (prepend #f (prepend #f ()) ;foo)) | |
175 | = (#f #f) | |
176 | ||
177 | | (prepend #f (prepend #f ()) ;peace ;(on) ;earth)) | |
178 | = (#f #f) | |
179 | ||
180 | Comments may appear in an empty list. | |
181 | ||
182 | | ( ;hi ;there)) | |
183 | = () | |
184 | ||
185 | Comments need not be preceded by spaces. | |
186 | ||
187 | | (;north;by;north;west)) | |
188 | = () | |
189 | ||
190 | To put truly arbitrary text in a comment, the string sugar syntax may be | |
191 | used. | |
192 | ||
193 | | (;''This expression, it evaluates to a list of two booleans. #k ?'' | |
194 | | prepend #f (prepend #f ())) | |
195 | = (#f #f) | |
196 | ||
197 | Part 1. Robin Expression Language | |
198 | --------------------------------- | |
199 | ||
200 | (a) Evaluation Rules | |
201 | -------------------- | |
202 | ||
203 | To be written. The information might be strewn about other sections, | |
204 | should be gathered together here someday. | |
205 | ||
206 | (b) Intrinsic Data Types | |
207 | ------------------------ | |
208 | ||
209 | -> Tests for functionality "Evaluate core Robin Expression" | |
210 | ||
211 | ### Terms ### | |
212 | ||
213 | Whereas an S-expression is a syntactic concept, a term is a semantic | |
214 | concept. Every term maps to an S-expression. Most S-expressions | |
215 | map to a term. By abuse of notation, sometimes we call terms S-expressions | |
216 | (but we'll try to avoid overdoing this.) | |
217 | ||
218 | In Robin, a term is a sort of catch-all data type which includes | |
122 | 219 | all the other data types. It is inductively defined as follows: |
123 | 220 | |
124 | * A symbol is an S-expression. | |
125 | * A boolean is an S-expression. | |
126 | * An integer is an S-expression. | |
127 | * A macro is an S-expression. | |
128 | * An intrinsic is an S-expression. | |
129 | * An empty list is an S-expression. | |
130 | * A list cell containing an S-expression, prepended to another list, | |
131 | is an S-expression. | |
132 | * Nothing else is an S-expression. | |
133 | ||
134 | S-expressions have a textual representation, but not all types have values | |
135 | that can be directly expressed in this textual representation. All | |
136 | S-expressions have some meaning when interpeted as Robin programs, as | |
221 | * A symbol is a term. | |
222 | * A boolean is a term. | |
223 | * An integer is a term. | |
224 | * A macro is a term. | |
225 | * An empty list is a term. | |
226 | * A list cell containing a term, prepended to another list | |
227 | (which may be empty), is a term. | |
228 | * Nothing else is a term. | |
229 | ||
230 | Terms have a textual representation (as S-expressions), but not all types | |
231 | have values that can be directly expressed in this textual representation. | |
232 | All terms have some meaning when interpeted as Robin programs, as | |
137 | 233 | defined by Robin's evaluation rules, but that meaning might be to |
138 | 234 | raise an exception to indicate an error. |
139 | 235 | |
140 | 236 | ### Symbol ### |
141 | 237 | |
142 | A symbol is an atomic value represented by a string of characters | |
238 | A symbol is an atomic value represented by a string of characters | |
143 | 239 | which may not include whitespace or parentheses or a few other |
144 | 240 | characters (TODO: decide which ones) and which may not begin with |
145 | 241 | a `#` (pound sign) or a few other characters (TODO: decide which |
148 | 244 | When in a Robin program proper, a symbol can be bound to a value, and |
149 | 245 | in this context is it referred to as an _identifier_. However, if an |
150 | 246 | attempt is made to evaluate a symbol which is not an identifier, |
151 | an exception will be raised. For a symbol to appear unevaluated in a Robin | |
152 | program, it must be an argument to a macro. For that reason, we can't | |
153 | show an example of a literal symbol without first defining a macro... but | |
154 | will go ahead and show the example, and will explain macros later. | |
155 | ||
156 | | (define literal (macro (self args env) (head args))) | |
157 | | (display (literal hello)) | |
247 | an exception will be raised. | |
248 | ||
249 | | this-symbol-is-not-bound | |
250 | ? uncaught exception: (unbound-identifier this-symbol-is-not-bound) | |
251 | ||
252 | For a symbol to appear unevaluated in a Robin program, it must be | |
253 | introduced as a literal. However, there is no intrinsic way to do this, | |
254 | so in order to demonstrate it, we must use something we haven't | |
255 | covered yet: a macro. We'll just go ahead and show the example, and | |
256 | will explain macros later. | |
257 | ||
258 | | ((macro (s a e) (head a)) hello) | |
158 | 259 | = hello |
159 | 260 | |
160 | 261 | A Robin implementation is not expected to be able to generate new symbols |
172 | 273 | |
173 | 274 | Booleans always evaluate to themselves. |
174 | 275 | |
175 | | (display #t) | |
276 | | #t | |
176 | 277 | = #t |
177 | 278 | |
178 | | (display #f) | |
279 | | #f | |
179 | 280 | = #f |
180 | 281 | |
181 | 282 | Booleans cannot be applied. |
182 | 283 | |
183 | | (display (#t 1 2 3)) | |
284 | | (#t 1 2 3) | |
184 | 285 | ? uncaught exception: (inapplicable-object #t) |
185 | 286 | |
186 | 287 | ### Integers ### |
191 | 292 | |
192 | 293 | For example, 5 is an integer: |
193 | 294 | |
194 | | (display 5) | |
295 | | 5 | |
195 | 296 | = 5 |
196 | 297 | |
197 | 298 | Whereas 6167172726261721 is not, and you get the 32-bit signed integer |
198 | 299 | equivalent: |
199 | 300 | |
200 | | (display 6167172726261721) | |
301 | | 6167172726261721 | |
201 | 302 | = -878835751 |
202 | 303 | |
203 | 304 | Integers always evaluate to themselves. |
204 | 305 | |
205 | 306 | Integers cannot be applied. |
206 | 307 | |
207 | | (display (900 1 2 3)) | |
308 | | (900 1 2 3) | |
208 | 309 | ? uncaught exception: (inapplicable-object 900) |
209 | 310 | |
210 | 311 | ### Macros ### |
211 | 312 | |
212 | A macro is an S-expression, in an environment, which describes how to | |
313 | A macro is a term which, in an environment, describes how to | |
213 | 314 | translate one S-expression to another. |
214 | 315 | |
215 | 316 | One area where Robin diverges significantly from Lisp and Scheme is that, |
245 | 346 | Macros are represented as the S-expression expansion of their |
246 | 347 | implementation. |
247 | 348 | |
248 | | (display | |
249 | | (macro (self args env) args)) | |
349 | | (macro (self args env) args) | |
250 | 350 | = (macro (self args env) args) |
251 | 351 | |
252 | 352 | Macros can be applied, and that is the typical use of them. |
253 | 353 | |
354 | | ((macro (self args env) args) 1) | |
355 | = (1) | |
356 | ||
357 | ### Lists ### | |
358 | ||
359 | A list is either the empty list, or a list cell containing a value of any | |
360 | type, prepended to another list. | |
361 | ||
362 | The "head" of a list cell is the value (of any type) that it contains; | |
363 | the "tail" is the other list that it is prepended to. The empty list | |
364 | has neither head nor tail. | |
365 | ||
366 | Lists have a literal representation in Robin's S-expression based | |
367 | syntax. | |
368 | ||
369 | The empty list is notated `()` and it evaluates to itself. | |
370 | ||
371 | | () | |
372 | = () | |
373 | ||
374 | A list with several elements is notated as a sequence of those | |
375 | elements, preceded by a `(`, followed by a `)`, and delimited | |
376 | by whitespace. | |
377 | ||
378 | Non-empty lists do not evaluate to themselves; rather, they represent a macro | |
379 | application. However, the `literal` macro (whose definition is | |
380 | `(macro (s a e) (head a))`) may be used to obtain a literal list. | |
381 | ||
382 | | ((macro (s a e) (head a)) (7 8))) | |
383 | = (7 8) | |
384 | ||
385 | Lists cannot be directly applied, but since a list itself represents an | |
386 | application, that application is undertaken, and the result of it can | |
387 | be applied. | |
388 | ||
389 | (c) Conventional Data Types | |
390 | ----------------------------- | |
391 | ||
392 | This section lists data types that are not intrinsic, but are rather | |
393 | arrangements of intrinsic types in a way that follows a convention. | |
394 | ||
395 | ### Strings ### | |
396 | ||
397 | Strings are just lists of integers, where each integer refers to a | |
398 | particular Unicode codepoint. There is syntactic sugar for embedding | |
399 | arbitrary text into a Robin Expression (see the Robin Syntax section) | |
400 | and this form parses as a literal string of this type. | |
401 | ||
402 | ### Alists ### | |
403 | ||
404 | An alist, short for "association list", is simply a list of two-element | |
405 | sublists. The idea is that each of these two-elements associates, in some | |
406 | context, the value of its first element with the value of its second element. | |
407 | ||
408 | ### Binding Alists ### | |
409 | ||
410 | When the first element of each two-element sublist in an alist is a symbol, | |
411 | we call it a _binding alist_. The idea is that it is a Robin representation | |
412 | of an evaluation environment, where the symbols in the heads of the sublists | |
413 | are bound to the values in the tails of the pairs. Binding alists can be | |
414 | created from the environment currently in effect (such as in the case of the | |
415 | third argument of a macro) and can be used to change the evaluation | |
416 | environment that is in effect (such as in the first argument to `eval`.) | |
417 | ||
418 | (d) Standard Environments | |
419 | ------------------------- | |
420 | ||
421 | Every Robin Expression is evaluated in some kind of environment | |
422 | (a mapping from symbols to the terms they are bound to.) Robin | |
423 | defines several standard environments in which expressions can be | |
424 | evaluated. Each of these environments is a superset of the others. | |
425 | ||
426 | The smallest environment consists only of intrinsics. The next-smallest | |
427 | environment is called "small". The largest environment is called | |
428 | "stdlib", short for "standard library". The definitions in the standard | |
429 | library are split up into purpose-oriented "packages" (for example, | |
430 | list functions, arithmetic functions, etc.) | |
431 | ||
254 | 432 | ### Intrinsics ### |
255 | ||
256 | An _intrinsic_ is one of the data types in Robin. It is like a macro, except | |
257 | that it is implemented intrinsically (and thus does not support quite | |
258 | every operation that is supported on macros, for example, examining its | |
259 | internals.) | |
260 | 433 | |
261 | 434 | Robin 0.3 provides 15 intrinsics. These represent |
262 | 435 | the fundamental functionality that is used to evaluate programs, and that |
268 | 441 | provide them, or it's not Robin. |
269 | 442 | |
270 | 443 | One important intrinsic is `eval`. Many macros will make use of `eval`, |
271 | to evaluate that literal tail they receive. When they do this in the | |
444 | to evaluate the literal args they receive. When they do this in the | |
272 | 445 | environment in which they were called, they behave a lot like functions. |
273 | 446 | But they are not obligated to; they might evaluate them in a modified |
274 | 447 | environment, or not evaluate them at all and treat them as a literal |
275 | 448 | S-expression. |
276 | 449 | |
277 | Intrinsics evaluate to themselves. | |
278 | ||
279 | An intrinsic is represented thusly. | |
280 | ||
281 | | (display head) | |
450 | Macros that are defined intrinsically does not support every operation | |
451 | that defined macro support; for example, they do not support examining | |
452 | their internals. The canonical representation of an intrinsic is the name | |
453 | its bound to. | |
454 | ||
455 | | head | |
282 | 456 | = head |
283 | 457 | |
284 | One upshot of intrinsics is that all intrinsic Robin functionality | |
285 | (excepting top-level forms) can be passed around as values. | |
286 | ||
287 | | (display | |
288 | | (prepend if (prepend head ()))) | |
458 | All parts of the Robin Expression Language, including intrinsics, | |
459 | can be passed around as values. | |
460 | ||
461 | | (prepend if (prepend head ())) | |
289 | 462 | = (if head) |
290 | 463 | |
291 | Intrinsics can be applied, and that is the typical use of them. | |
464 | All of the 15 intrinsics are macros, but there is nothing ontologically | |
465 | requiring an intrinsic to be a value of macro type. | |
292 | 466 | |
293 | 467 | Each of the 15 intrinsics provided by Robin 0.3 is specified in |
294 | 468 | its own file in the standard library. Because these are intrinsics, |
295 | no Robin implementation is given for them, but tests cases which | |
296 | describe their behaviour are. | |
469 | no Robin implementation is given for them in these files, but tests cases | |
470 | which describe their behaviour are. | |
297 | 471 | |
298 | 472 | * [catch](../stdlib/catch.robin) |
299 | 473 | * [equal?](../stdlib/equal-p.robin) |
311 | 485 | * [symbol?](../stdlib/symbol-p.robin) |
312 | 486 | * [tail](../stdlib/tail.robin) |
313 | 487 | |
314 | ### Lists ### | |
315 | ||
316 | A list is either the empty list, or a list cell containing a value of any | |
317 | type, prepended to another list. | |
318 | ||
319 | The "head" of a list cell is the value (of any type) that it contains; | |
320 | the "tail" is the other list that it is prepended to. The empty list | |
321 | has neither head nor tail. | |
322 | ||
323 | Lists have a literal representation in Robin's S-expression based | |
324 | syntax. | |
325 | ||
326 | The empty list is notated `()` and it evaluates to itself. | |
327 | ||
328 | | (display | |
329 | | ()) | |
330 | = () | |
331 | ||
332 | A list with several elements is notated as a sequence of those | |
333 | elements, preceded by a `(`, followed by a `)`, and delimited | |
334 | by whitespace. | |
335 | ||
336 | Non-empty lists do not evaluate to themselves; rather, they represent a macro | |
337 | application. However, the `literal` macro may be used to obtain a | |
338 | literal list. | |
339 | ||
340 | | (define literal (macro (s a e) (head a))) | |
341 | | (display | |
342 | | (literal (7 8))) | |
343 | = (7 8) | |
344 | ||
345 | Lists cannot be directly applied, but since a list itself represents an | |
346 | application, that application is undertaken, and the result of it can | |
347 | be applied. | |
348 | ||
349 | Conventional Data Types | |
350 | ----------------------- | |
351 | ||
352 | This section lists data types that are not intrinsic, but are rather | |
353 | arrangements of intrinsic types in a way that follows a convention. | |
354 | ||
355 | ### Strings ### | |
356 | ||
357 | Strings are just lists of integers, where each integer refers to a | |
358 | particular Unicode codepoint. Robin supports a sugared syntax for | |
359 | specifying literal strings. The characters of the string are given | |
360 | between pairs of single quotes. | |
361 | ||
362 | | (define literal (macro (s a e) (head a))) | |
363 | | (display | |
364 | | (literal ''Hello'')) | |
365 | = (72 101 108 108 111) | |
366 | ||
367 | A single single quote may appear in string literals of this kind. | |
368 | ||
369 | | (define literal (macro (s a e) (head a))) | |
370 | | (display | |
371 | | (literal ''He'llo'')) | |
372 | = (72 101 39 108 108 111) | |
373 | ||
374 | Between the single quotes delimiting the string literal, a *sentinel* | |
375 | may be given. The sentinel between the leading single quote pair must | |
376 | match the sentinel given between the trailing single quote pair. The | |
377 | sentinel may consist of any text not containing a single quote. | |
378 | ||
379 | | (define literal (macro (s a e) (head a))) | |
380 | | (display | |
381 | | (literal 'X'Hello'X')) | |
382 | = (72 101 108 108 111) | |
383 | ||
384 | | (define literal (macro (s a e) (head a))) | |
385 | | (display | |
386 | | (literal '...('Hello'...(')) | |
387 | = (72 101 108 108 111) | |
388 | ||
389 | | (define literal (macro (s a e) (head a))) | |
390 | | (display | |
391 | | (literal 'X'Hello'Y')) | |
392 | ? unexpected end of input | |
393 | ||
394 | A sentinelized literal like this may embed a pair of single quotes. | |
395 | ||
396 | | (define literal (macro (s a e) (head a))) | |
397 | | (display | |
398 | | (literal 'X'Hel''lo'X')) | |
399 | = (72 101 108 39 39 108 111) | |
400 | ||
401 | By choosing different sentinels, string literals may contain any other | |
402 | string literal. | |
403 | ||
404 | | (define literal (macro (s a e) (head a))) | |
405 | | (display | |
406 | | (literal 'X'Hel'Y'bye'Y'lo'X')) | |
407 | = (72 101 108 39 89 39 98 121 101 39 89 39 108 111) | |
408 | ||
409 | No interpolation of escape sequences is done in a Robin string literal. | |
410 | (Functions to convert escape sequences commonly found in other languages | |
411 | may one day be available in a standard module.) | |
412 | ||
413 | | (define literal (macro (s a e) (head a))) | |
414 | | (display | |
415 | | (literal ''Hello\nworld'')) | |
416 | = (72 101 108 108 111 92 110 119 111 114 108 100) | |
417 | ||
418 | All characters which appear in the source text between the delimiters | |
419 | of the string literal are literally included in the string. | |
420 | ||
421 | | (define literal (macro (s a e) (head a))) | |
422 | | (display | |
423 | | (literal ''Hello | |
424 | | world'')) | |
425 | = (72 101 108 108 111 10 119 111 114 108 100) | |
426 | ||
427 | Adjacent string literals are not automatically concatenated. | |
428 | ||
429 | | (define literal (macro (s a e) (head a))) | |
430 | | (display | |
431 | | (literal (''Hello'' ''world''))) | |
432 | = ((72 101 108 108 111) (119 111 114 108 100)) | |
433 | ||
434 | ### Alists ### | |
435 | ||
436 | An alist, short for "association list", is simply a list of two-element | |
437 | sublists. The idea is that each of these two-elements associates, in some | |
438 | context, the value of its first element with the value of its second element. | |
439 | ||
440 | ### Binding Alists ### | |
441 | ||
442 | When the first element of each two-element sublist in an alist is a symbol, | |
443 | we call it a _binding alist_. The idea is that it is a Robin representation | |
444 | of an evaluation environment, where the symbols in the heads of the sublists | |
445 | are bound to the values in the tails of the pairs. Binding alists can be | |
446 | created from the environment currently in effect (such as in the case of the | |
447 | third argument of a macro) and can be used to change the evaluation | |
448 | environment that is in effect (such as in the first argument to `eval`.) | |
449 | ||
450 | TODO: binding alists may be replaced by abstract map objects of some kind. | |
451 | ||
452 | Comments | |
453 | -------- | |
454 | ||
455 | Any S-expression preceded by a `;` symbol is a comment. It will still | |
456 | be parsed, but it will be ignored. | |
457 | ||
458 | | (display | |
459 | | ;(this program produces a list of two booleans) | |
460 | | (prepend #f (prepend #f ()))) | |
461 | = (#f #f) | |
462 | ||
463 | Because S-expressions may nest, and because comments may appear | |
464 | inside S-expressions, comments may nest. | |
465 | ||
466 | | (display | |
467 | | ;(this program produces | |
468 | | ;(what you might call) | |
469 | | a list of two booleans) | |
470 | | (prepend #f (prepend #f ()))) | |
471 | = (#f #f) | |
472 | ||
473 | Comments are still parsed. A syntax error in a comment is an error! | |
474 | ||
475 | | (display | |
476 | | ;(this program produces | |
477 | | #k | |
478 | | a list of booleans) | |
479 | | (prepend #f (prepend #f ()))) | |
480 | ? (line 3, column 6): | |
481 | ? unexpected "k" | |
482 | ? expecting "t" or "f" | |
483 | ||
484 | Any number of comments may appear together. | |
485 | ||
486 | | (display | |
487 | | (prepend ;what ;on ;earth #f (prepend #f ()))) | |
488 | = (#f #f) | |
489 | ||
490 | Comments may appear before a closing parenthesis. | |
491 | ||
492 | | (display | |
493 | | (prepend #f (prepend #f ()) ;foo)) | |
494 | = (#f #f) | |
495 | ||
496 | | (display | |
497 | | (prepend #f (prepend #f ()) ;peace ;(on) ;earth)) | |
498 | = (#f #f) | |
499 | ||
500 | Comments may appear in an empty list. | |
501 | ||
502 | | (display | |
503 | | ( ;hi ;there)) | |
504 | = () | |
505 | ||
506 | Comments need not be preceded by spaces. | |
507 | ||
508 | | (display | |
509 | | (;north;by;north;west)) | |
510 | = () | |
511 | ||
512 | To put truly arbitrary text in a comment, the string sugar syntax may be | |
513 | used. | |
514 | ||
515 | | (display | |
516 | | ;''This program, it produces a list of two booleans. #k ?'' | |
517 | | (prepend #f (prepend #f ()))) | |
518 | = (#f #f) | |
519 | ||
520 | Reactors | |
521 | -------- | |
488 | ### Small ### | |
489 | ||
490 | The "small" library represents indispensible functionality that | |
491 | all but the most austere Robin programs would like to be built on. | |
492 | ||
493 | * [literal](../stdlib/literal.robin) | |
494 | * [list](../stdlib/list.robin) | |
495 | * [bind](../stdlib/bind.robin) | |
496 | * [env](../stdlib/env.robin) | |
497 | * [let](../stdlib/let.robin) | |
498 | * [choose](../stdlib/choose.robin) | |
499 | * [bind-args](../stdlib/bind-args.robin) | |
500 | ||
501 | ### Standard Library ### | |
502 | ||
503 | The "standard" library represents a rich collection of functionality. | |
504 | It's categorized in "packages". | |
505 | ||
506 | * *boolean*: and or xor not boolean? | |
507 | * *list*: empty? map fold reverse filter find append elem? length index | |
508 | take-while drop-while first rest last prefix? flatten | |
509 | * *alist*: lookup extend delete | |
510 | * *env*: env? bound? export sandbox unbind unshadow | |
511 | * *arith*: abs add > >= < <= multiply divide remainder | |
512 | * *misc*: itoa | |
513 | ||
514 | Part 2. Robin Toplevel Language | |
515 | ------------------------------- | |
516 | ||
517 | -> Tests for functionality "Execute core Robin Toplevel Program" | |
518 | ||
519 | A Robin program consists of a series of "top-level" S-expressions. | |
520 | Each top-level S-expression must have a particular form, but most of these | |
521 | top-level S-expressions may contain general, evaluatable S-expressions | |
522 | themselves. Allowable top-level forms are given in the subsections below. | |
523 | ||
524 | ### `display` ### | |
525 | ||
526 | `(display EXPR)` evaluates the EXPR and displays the result in a canonical | |
527 | S-expression rendering, followed by a newline. | |
528 | ||
529 | | (display #t) | |
530 | = #t | |
531 | ||
532 | Note that a Robin program may be split over several files in the filesystem. | |
533 | Also, more than one top-level S-expression may appear in a single file. | |
534 | ||
535 | | (display #t) | |
536 | | (display #f) | |
537 | = #t | |
538 | = #f | |
539 | ||
540 | ### `assert` ### | |
541 | ||
542 | `(assert EXPR)` evaluates the EXPR and, if there was an error evaluating | |
543 | the EXPR, or if the EXPR evaluates to `#f`, aborts processing the file. | |
544 | ||
545 | | (assert #t) | |
546 | = | |
547 | ||
548 | | (assert #f) | |
549 | ? assertion failed: #f | |
550 | ||
551 | | (assert this-identfier-is-not-bound) | |
552 | ? unbound-identifier | |
553 | ||
554 | ### `require` ### | |
555 | ||
556 | `(require SYMBOL)` is conceptually not different from `(assert (bound? SYMBOL))` | |
557 | (see `bound?` in the stdlib for the meaning of `bound?`.) However, since it | |
558 | is given in a declarative fashion, an implementations may examine this symbol | |
559 | and try to fulfill the requirement that it be bound by e.g. locating and | |
560 | loading an external definition file. Note that an implementation is not | |
561 | required to do this, it is simply permitted. | |
562 | ||
563 | | (require if) | |
564 | = | |
565 | ||
566 | | (require mumbo-jumbo) | |
567 | ? assertion failed: (bound? mumbo-jumbo) | |
568 | ||
569 | | (define mumbo-jumbo 1) | |
570 | | (require mumbo-jumbo) | |
571 | = | |
572 | ||
573 | ### `define` ### | |
574 | ||
575 | `(define SYMBOL EXPR)` defines a global name. | |
576 | ||
577 | | (define true #t) | |
578 | | (display true) | |
579 | = #t | |
580 | ||
581 | You may not try to define anything that's not a symbol. | |
582 | ||
583 | | (define #f #t) | |
584 | | (display #f) | |
585 | ? illegal top-level form: (define #f #t) | |
586 | ||
587 | You may define multiple names. | |
588 | ||
589 | | (define true #t) | |
590 | | (define false #f) | |
591 | | (display false) | |
592 | | (display true) | |
593 | = #f | |
594 | = #t | |
595 | ||
596 | Names may not be redefined once defined. | |
597 | ||
598 | | (define true #t) | |
599 | | (define true #f) | |
600 | ? symbol already defined: true | |
601 | ||
602 | Names previously defined can be used in a definition. | |
603 | ||
604 | | (define true #t) | |
605 | | (define also-true true) | |
606 | | (display also-true) | |
607 | = #t | |
608 | ||
609 | Names that are not yet defined cannot be used in a definition, even if | |
610 | they are defined later on in the file. | |
611 | ||
612 | | (define also-true true) | |
613 | | (define true #t) | |
614 | | (display also-true) | |
615 | ? unbound-identifier | |
616 | ||
617 | ### `reactor` ### | |
618 | ||
619 | `(reactor LIST-OF-SYMBOLS STATE-EXPR BODY-EXPR)` installs a reactor. Reactors | |
620 | permit the construction of reactive Robin programs. See the | |
621 | [Reactors](#reactors) section for more information on reactors. | |
622 | ||
623 | Part 3. Robin Reactors | |
624 | ---------------------- | |
625 | ||
626 | -> Tests for functionality "Execute core Robin Toplevel Program" | |
522 | 627 | |
523 | 628 | To separate the concerns of computation and interaction, Robin provides |
524 | 629 | a construct called a _reactor_. While evaluation of a Robin expression |
573 | 678 | In fact, commands _are_ events. We just call them commands when it is |
574 | 679 | a reactor producing them, and events when a reactor is receiving them. |
575 | 680 | |
576 | Standard Events | |
577 | --------------- | |
578 | ||
579 | ### `init` ### | |
681 | ### Standard Events ### | |
682 | ||
683 | #### `init` #### | |
580 | 684 | |
581 | 685 | When a reactor first starts up it will receive an event telling it that |
582 | 686 | it has started up. The event type for this event is the literal symbol |
595 | 699 | will receive a `not-available` event. It may then elect to abort, |
596 | 700 | or choose an alternate facility, or so forth. |
597 | 701 | |
598 | Standard Commands | |
599 | ----------------- | |
600 | ||
601 | ### `stop` ### | |
702 | ### Standard Commands ### | |
703 | ||
704 | #### `stop` #### | |
602 | 705 | |
603 | 706 | Stops the current reactor, and removes it from the list of active |
604 | 707 | reactors. It will no longer receive any events. |
605 | 708 | |
606 | Standard Facilities | |
607 | ------------------- | |
709 | ### Standard Facilities ### | |
608 | 710 | |
609 | 711 | If a reactor isn't subscribed to any facilities, it won't necessarily |
610 | 712 | receive any events, although this is implementation-specific. |
615 | 717 | |
616 | 718 | Let's describe one such facility for concreteness. |
617 | 719 | |
618 | ### `line-terminal` ### | |
619 | ||
620 | -> Tests for functionality "Interpret Robin Program (with Small)" | |
720 | #### `line-terminal` #### | |
721 | ||
722 | -> Tests for functionality "Execute Robin Toplevel Program (with Small)" | |
621 | 723 | |
622 | 724 | The `line-terminal` facility allows a Robin program to interact with |
623 | 725 | something or someone over a line-oriented protocol, similar to what |
628 | 730 | |
629 | 731 | The `line-terminal` facility understands commands of the form |
630 | 732 | |
631 | (writeln <STRING>) | |
632 | ||
633 | The `<STRING>` argument should be a Robin string (list of integers). Those | |
733 | (writeln STRING) | |
734 | ||
735 | The `STRING` argument should be a Robin string (list of integers). Those | |
634 | 736 | integers, as bytes, are sent to whetever is listening on the other end of |
635 | 737 | the line terminal. When attached to an actual terminal console (whether real |
636 | 738 | or emulated), this would typically cause an ASCII representation of those bytes |
637 | 739 | to be displayed. |
740 | ||
741 | It also understands | |
742 | ||
743 | (write STRING) | |
744 | ||
745 | which will write the STRING but not terminate the line. | |
638 | 746 | |
639 | 747 | Knowing this, we can write a "Hello, world!" program. To keep it |
640 | 748 | simple, we'll simply assume the line-terminal facility exists. |
676 | 784 | = Cat |
677 | 785 | = Dog |
678 | 786 | |
679 | General Reactor properties | |
680 | -------------------------- | |
787 | #### `random-u16-source` #### | |
788 | ||
789 | The `random-u16-source` facility allows a Robin program to request, | |
790 | and obtain, unsigned 16-bit numbers whose value is (ideally speaking) | |
791 | unpredictable. | |
792 | ||
793 | The `random-u16-source` facility understands commands of the form | |
794 | ||
795 | (obtain-random-u16 0) | |
796 | ||
797 | In response to one of these commands, this facility generates | |
798 | an event of the form | |
799 | ||
800 | (random-u16 NUMBER) | |
801 | ||
802 | where NUMBER is a random number between 0 and 65535. | |
803 | ||
804 | ### General Reactor properties ### | |
681 | 805 | |
682 | 806 | A reactor can issue multiple commands in its response to an event. |
683 | 807 |
0 | (assert fun) ;''A way to signal that it requires stdlib.'' | |
0 | (require fun) | |
1 | 1 | |
2 | 2 | (reactor (line-terminal) (list 0 0) (macro (self args env) |
3 | 3 | (let ((event (head args)) |
0 | (require bind) | |
1 | ||
0 | 2 | (reactor (line-terminal) 0 |
1 | 3 | (macro (self args env) |
2 | 4 | (bind event (head args) |
0 | 0 | ;'' |
1 | Deadfish in Robin 0.3 -- requires stdlib | |
1 | Deadfish in Robin 0.3 | |
2 | 2 | '' |
3 | ||
4 | (require itoa) | |
5 | ||
3 | 6 | (reactor (line-terminal) 0 |
4 | 7 | (macro (self args env) |
5 | 8 | (bind event (head args) |
18 | 21 | (if show |
19 | 22 | (list state |
20 | 23 | (list (literal writeln) (itoa state)) |
21 | (list (literal writeln) (literal ''>> ''))) | |
24 | (list (literal write) (literal ''>> ''))) | |
22 | 25 | (list state |
23 | (list (literal writeln) (literal ''>> ''))))))) | |
26 | (list (literal write) (literal ''>> ''))))))) | |
24 | 27 | (choose |
25 | 28 | ((equal? event-type (literal init)) |
26 | 29 | (prompt #f state)) |
0 | (require bind) | |
1 | (require literal) | |
2 | ||
0 | 3 | (reactor (line-terminal) 0 |
1 | 4 | (macro (self args env) |
2 | 5 | (bind event (head args) |
0 | ;''Continually generate random numbers from 0 to 65535 | |
1 | and output the last digit of each number so generated.'' | |
2 | ||
3 | (require let) (require choose) (require itoa) (require abs) (require remainder) | |
4 | ||
5 | (reactor (line-terminal random-u16-source) 0 | |
6 | (macro (self args env) | |
7 | (let ((event (head args)) | |
8 | (event-type (head event)) | |
9 | (event-payload (head (tail event)))) | |
10 | (choose | |
11 | ((equal? event-type (literal init)) | |
12 | (list 0 | |
13 | (list (literal obtain-random-u16) 0))) | |
14 | ((equal? event-type (literal random-u16)) | |
15 | (list 0 | |
16 | (list (literal writeln) (list (add (remainder (abs event-payload) 10) 48))) | |
17 | (list (literal obtain-random-u16) 0))) | |
18 | (else | |
19 | (list 0)))))) |
4 | 4 | import Haste.Events |
5 | 5 | |
6 | 6 | import Language.Robin.Env (mergeEnvs) |
7 | import Language.Robin.Parser (parseRobin) | |
7 | import Language.Robin.Parser (parseToplevel) | |
8 | 8 | import Language.Robin.Intrinsics (robinIntrinsics) |
9 | 9 | import Language.Robin.Builtins (robinBuiltins) |
10 | 10 | import qualified Language.Robin.TopLevel as TopLevel |
17 | 17 | where |
18 | 18 | execute = do |
19 | 19 | Just program <- getValue progElem |
20 | case parseRobin program of | |
20 | case parseToplevel program of | |
21 | 21 | Right topExprs -> do |
22 | 22 | let env = (mergeEnvs robinIntrinsics robinBuiltins) |
23 | 23 | let (env', reactors, results) = TopLevel.collect topExprs env [] [] |
16 | 16 | -- See the relevant files in `stdlib` for normative definitions. |
17 | 17 | -- |
18 | 18 | |
19 | -- | |
20 | -- Helper functions | |
21 | -- | |
22 | ||
19 | 23 | union (List []) env = env |
20 | 24 | union (List (binding:rest)) env = |
21 | 25 | append (List [binding]) (union (List rest) env) |
22 | ||
23 | literal i env (List (expr:_)) cc = | |
24 | cc expr | |
25 | literal i env other cc = raise i (errMsg "illegal-arguments" other) | |
26 | 26 | |
27 | 27 | evalAll i env [] acc cc = |
28 | 28 | cc $ List $ reverse acc |
30 | 30 | eval i env head (\value -> |
31 | 31 | evalAll i env tail (value:acc) cc) |
32 | 32 | |
33 | -- formals actuals origActuals env i cc | |
34 | evalArgs [] [] _ _ _ cc = | |
35 | cc Env.empty | |
36 | evalArgs (formal@(Symbol _):formals) (actual:actuals) origActuals env i cc = | |
37 | eval i env actual (\value -> | |
38 | evalArgs formals actuals origActuals env i (\rest -> | |
39 | cc $ Env.insert formal value rest)) | |
40 | evalArgs _ _ origActuals _ i cc = | |
41 | raise i (errMsg "illegal-arguments" (List origActuals)) | |
42 | ||
43 | -- | |
44 | -- Builtins | |
45 | -- | |
46 | ||
47 | literal :: Evaluable | |
48 | literal i env (List (expr:_)) cc = | |
49 | cc expr | |
50 | literal i env other cc = raise i (errMsg "illegal-arguments" other) | |
51 | ||
52 | robinList :: Evaluable | |
33 | 53 | robinList i env (List exprs) cc = |
34 | 54 | evalAll i env exprs [] cc |
35 | 55 | |
56 | robinEnv :: Evaluable | |
36 | 57 | robinEnv i env (List _) cc = |
37 | 58 | cc env |
38 | 59 | |
60 | choose :: Evaluable | |
39 | 61 | choose i env (List [(List [(Symbol "else"), branch])]) cc = |
40 | 62 | eval i env branch cc |
41 | 63 | choose i env (List ((List [test, branch]):rest)) cc = |
47 | 69 | choose i env (List rest) cc) |
48 | 70 | choose i env other cc = raise i (errMsg "illegal-arguments" other) |
49 | 71 | |
72 | bind :: Evaluable | |
50 | 73 | bind i env (List [name@(Symbol _), expr, body]) cc = |
51 | 74 | eval i env expr (\value -> |
52 | 75 | eval i (Env.insert name value env) body cc) |
53 | 76 | bind i env other cc = raise i (errMsg "illegal-arguments" other) |
54 | 77 | |
78 | robinLet :: Evaluable | |
55 | 79 | robinLet i env (List ((List bindings):body:_)) cc = |
56 | 80 | bindAll bindings env i (\env' -> |
57 | 81 | eval i env' body cc) |
65 | 89 | raise ienv (errMsg "illegal-binding" other) |
66 | 90 | robinLet i env other cc = raise i (errMsg "illegal-arguments" other) |
67 | 91 | |
68 | -- formals actuals origActuals env i cc | |
69 | evalArgs [] [] _ _ _ cc = | |
70 | cc Env.empty | |
71 | evalArgs (formal@(Symbol _):formals) (actual:actuals) origActuals env i cc = | |
72 | eval i env actual (\value -> | |
73 | evalArgs formals actuals origActuals env i (\rest -> | |
74 | cc $ Env.insert formal value rest)) | |
75 | evalArgs _ _ origActuals _ i cc = | |
76 | raise i (errMsg "illegal-arguments" (List origActuals)) | |
77 | ||
92 | robinBindArgs :: Evaluable | |
78 | 93 | robinBindArgs i env (List [(List formals), givenArgs, givenEnv, body]) cc = |
79 | 94 | eval i env givenArgs (\(List actuals) -> |
80 | 95 | eval i env givenEnv (\outerEnv -> |
82 | 97 | eval i (union argEnv env) body cc))) |
83 | 98 | robinBindArgs i env other cc = raise i (errMsg "illegal-arguments" other) |
84 | 99 | |
85 | -- | |
86 | -- Implementation of `fun`. | |
87 | -- | |
88 | ||
100 | robinFun :: Evaluable | |
89 | 101 | robinFun i closedEnv (List [(List formals), body]) cc = |
90 | 102 | cc $ Intrinsic "<lambda>" fun |
91 | 103 | where |
106 | 118 | -- Mapping of names to our functions, providing an evaluation environment. |
107 | 119 | -- |
108 | 120 | |
121 | robinBuiltins :: Expr | |
109 | 122 | robinBuiltins = Env.fromList $ map (\(name,bif) -> (name, Intrinsic name bif)) |
110 | 123 | [ |
111 | 124 | ("literal", literal), |
6 | 6 | -- values (arbitrary S-expressions). |
7 | 7 | -- |
8 | 8 | |
9 | empty :: Expr | |
9 | 10 | empty = List [] |
10 | 11 | |
12 | insert :: Expr -> Expr -> Expr -> Expr | |
11 | 13 | insert s@(Symbol _) value env = |
12 | 14 | append (List [List [s, value]]) env |
13 | 15 | |
16 | find :: Expr -> Expr -> Maybe Expr | |
14 | 17 | find s@(Symbol _) (List []) = Nothing |
15 | 18 | find s@(Symbol _) (List (List [first, value]:rest)) |
16 | 19 | | s == first = Just value |
17 | 20 | | otherwise = find s (List rest) |
18 | 21 | |
22 | fromList :: [(String,Expr)] -> Expr | |
19 | 23 | fromList [] = |
20 | 24 | List [] |
21 | 25 | fromList ((id, val):xs) = |
22 | 26 | append (List [List [(Symbol id), val]]) (fromList xs) |
23 | 27 | |
28 | mergeEnvs :: Expr -> Expr -> Expr | |
24 | 29 | mergeEnvs (List a) (List b) = List (a ++ b) |
10 | 10 | -- |
11 | 11 | -- Expr -> Expr -> Expr -> (Expr -> Expr) -> Expr |
12 | 12 | -- |
13 | -- (This is actually the `Intrinsic` type from `Robin.Expr`.) | |
13 | -- (This is actually the `Evaluable` type from `Robin.Expr`.) | |
14 | 14 | -- |
15 | 15 | -- The first argument is the internal context, which contains things like the |
16 | 16 | -- exception handler, etc. |
22 | 22 | -- value. Then continue the current continuation with that value. |
23 | 23 | -- |
24 | 24 | |
25 | eval :: Intrinsic | |
25 | eval :: Evaluable | |
26 | 26 | |
27 | 27 | eval i (List []) s@(Symbol _) cc = |
28 | 28 | raise i (errMsg "unbound-identifier" s) |
3 | 3 | import Data.Int |
4 | 4 | |
5 | 5 | -- |
6 | -- An _intrinsic_ is an object which behaves much like a macro, but is implemented | |
7 | -- intrinsically (it cannot be (non-meta-circularly) defined in Robin itself.) | |
6 | -- An _evaluable_ is a Haskell object which behaves like a Robin macro. | |
7 | -- It describes builtins (which includes intrinsics), and also happens | |
8 | -- (perhaps unsurprisingly?) to be the type of the evaluator function. | |
8 | 9 | -- |
9 | 10 | |
10 | type Intrinsic = IEnv Expr -> Expr -> Expr -> (Expr -> Expr) -> Expr | |
11 | type Evaluable = IEnv Expr -> Expr -> Expr -> (Expr -> Expr) -> Expr | |
11 | 12 | -- internal-env env args continuation result |
12 | 13 | |
13 | 14 | data Expr = Symbol String |
14 | 15 | | Boolean Bool |
15 | 16 | | Number Int32 |
16 | 17 | | Macro Expr Expr Expr |
17 | | Intrinsic String Intrinsic | |
18 | | Intrinsic String Evaluable | |
18 | 19 | | List [Expr] |
19 | 20 | |
20 | 21 | instance Eq Expr where |
34 | 35 | show (Macro env args body) = ("(macro " ++ (show args) ++ |
35 | 36 | " " ++ (show body) ++ ")") |
36 | 37 | show (Intrinsic name _) = name |
37 | show (List exprs) = "(" ++ (showl exprs) ++ ")" | |
38 | ||
39 | showl [] = "" | |
40 | showl [expr] = show expr | |
41 | showl (expr:exprs) = (show expr) ++ " " ++ (showl exprs) | |
38 | show (List exprs) = "(" ++ (showl exprs) ++ ")" where | |
39 | showl [] = "" | |
40 | showl [expr] = show expr | |
41 | showl (expr:exprs) = (show expr) ++ " " ++ (showl exprs) | |
42 | 42 | |
43 | 43 | -- |
44 | 44 | -- Helpers |
4 | 4 | import Language.Robin.Eval |
5 | 5 | |
6 | 6 | |
7 | robinHead :: Evaluable | |
7 | 8 | robinHead i env (List [expr]) cc = |
8 | 9 | eval i env expr (\x -> |
9 | 10 | assertList i x (\val -> |
12 | 13 | other -> raise i (errMsg "expected-list" other))) |
13 | 14 | robinHead i env other cc = raise i (errMsg "illegal-arguments" other) |
14 | 15 | |
16 | robinTail :: Evaluable | |
15 | 17 | robinTail i env (List [expr]) cc = |
16 | 18 | eval i env expr (\x -> |
17 | 19 | assertList i x (\val -> |
20 | 22 | other -> raise i (errMsg "expected-list" other))) |
21 | 23 | robinTail i env other cc = raise i (errMsg "illegal-arguments" other) |
22 | 24 | |
25 | robinPrepend :: Evaluable | |
23 | 26 | robinPrepend i env (List [e1, e2]) cc = |
24 | 27 | eval i env e1 (\x1 -> eval i env e2 (\val -> |
25 | 28 | case val of |
27 | 30 | other -> raise i (errMsg "expected-list" other))) |
28 | 31 | robinPrepend i env other cc = raise i (errMsg "illegal-arguments" other) |
29 | 32 | |
33 | equalP :: Evaluable | |
30 | 34 | equalP i env (List [e1, e2]) cc = |
31 | 35 | eval i env e1 (\x1 -> eval i env e2 (\x2 -> cc $ Boolean (x1 == x2))) |
32 | 36 | equalP i env other cc = raise i (errMsg "illegal-arguments" other) |
40 | 44 | macroP = predP isMacro |
41 | 45 | numberP = predP isNumber |
42 | 46 | |
47 | robinSubtract :: Evaluable | |
43 | 48 | robinSubtract i env (List [xexpr, yexpr]) cc = |
44 | 49 | eval i env xexpr (\x -> |
45 | 50 | assertNumber i x (\(Number xv) -> |
48 | 53 | cc (Number (xv - yv)))))) |
49 | 54 | robinSubtract i env other cc = raise i (errMsg "illegal-arguments" other) |
50 | 55 | |
56 | robinSign :: Evaluable | |
51 | 57 | robinSign i env (List [expr]) cc = |
52 | 58 | eval i env expr (\x -> |
53 | 59 | assertNumber i x (\(Number xv) -> |
56 | 62 | sign x = if x == 0 then 0 else if x < 0 then -1 else 1 |
57 | 63 | robinSign i env other cc = raise i (errMsg "illegal-arguments" other) |
58 | 64 | |
65 | robinIf :: Evaluable | |
59 | 66 | robinIf i env (List [test, texpr, fexpr]) cc = |
60 | 67 | eval i env test (\x -> |
61 | 68 | assertBoolean i x (\(Boolean b) -> |
64 | 71 | False -> eval i env fexpr cc)) |
65 | 72 | robinIf i env other cc = raise i (errMsg "illegal-arguments" other) |
66 | 73 | |
74 | robinEval :: Evaluable | |
67 | 75 | robinEval i env (List [envlist, form]) cc = |
68 | 76 | eval i env envlist (\newEnv -> |
69 | 77 | eval i env form (\body -> |
70 | 78 | eval i newEnv body cc)) |
71 | 79 | robinEval i env other cc = raise i (errMsg "illegal-arguments" other) |
72 | 80 | |
81 | robinMacro :: Evaluable | |
73 | 82 | robinMacro i env (List [args@(List [(Symbol selfS), (Symbol argsS), (Symbol envS)]), body]) cc = do |
74 | 83 | cc $ Macro env args body |
75 | 84 | robinMacro i env other cc = raise i (errMsg "illegal-arguments" other) |
76 | 85 | |
86 | robinRaise :: Evaluable | |
77 | 87 | robinRaise i env (List [expr]) cc = |
78 | 88 | eval i env expr (\v -> raise i v) |
79 | 89 | robinRaise i env other cc = raise i (errMsg "illegal-arguments" other) |
80 | 90 | |
91 | robinCatch :: Evaluable | |
81 | 92 | robinCatch i env (List [id@(Symbol _), handler, body]) cc = |
82 | 93 | let |
83 | 94 | handlerContinuation = (\errvalue -> |
87 | 98 | eval i' env body cc |
88 | 99 | robinCatch i env other cc = raise i (errMsg "illegal-arguments" other) |
89 | 100 | |
101 | robinIntrinsics :: Expr | |
90 | 102 | robinIntrinsics = Env.fromList $ map (\(name,bif) -> (name, Intrinsic name bif)) |
91 | 103 | [ |
92 | 104 | ("head", robinHead), |
0 | 0 | {-# LANGUAGE FlexibleContexts #-} |
1 | 1 | |
2 | module Language.Robin.Parser (parseRobin, insistParse) where | |
2 | module Language.Robin.Parser (parseToplevel, parseExpr) where | |
3 | 3 | |
4 | 4 | import Data.Char |
5 | 5 | import Data.Int |
78 | 78 | expr |
79 | 79 | |
80 | 80 | -- |
81 | -- The top-level parsing function implements the overall grammar given above. | |
81 | -- The expression parsing function implements the overall grammar given above. | |
82 | 82 | -- Note that we need to give the type of this parser here -- otherwise the |
83 | 83 | -- type inferencer freaks out for some reason. |
84 | 84 | -- |
85 | 85 | |
86 | 86 | expr :: Parser Expr |
87 | 87 | expr = do |
88 | spaces | |
88 | 89 | r <- (symbol <|> number <|> boolean <|> list <|> stringSugar) |
89 | 90 | spaces |
90 | 91 | many comment |
91 | 92 | return r |
92 | 93 | |
93 | robinProgram = do | |
94 | toplevel = do | |
94 | 95 | spaces |
95 | 96 | many comment |
96 | 97 | e <- many expr |
97 | 98 | return $ e |
98 | 99 | |
99 | -- Convenience functions for parsing Robin programs. | |
100 | -- Convenience functions for parsing Robin forms. | |
100 | 101 | |
101 | parseRobin = parse robinProgram "" | |
102 | parseToplevel :: String -> Either ParseError [Expr] | |
103 | parseToplevel = parse toplevel "" | |
102 | 104 | |
103 | insistParse programText = | |
104 | let | |
105 | Right ast = parseRobin programText | |
106 | in | |
107 | ast | |
105 | parseExpr :: String -> Either ParseError Expr | |
106 | parseExpr = parse expr "" |
2 | 2 | import qualified Data.Char as Char |
3 | 3 | import Data.Int |
4 | 4 | import System.IO |
5 | import System.Random | |
5 | 6 | |
6 | 7 | import Language.Robin.Expr |
7 | 8 | import Language.Robin.Eval |
67 | 68 | |
68 | 69 | eventLoop showEvents reactors (event@(List [eventType, eventPayload]):events) = do |
69 | 70 | showEvent showEvents event |
70 | handleLineTerminalEvent event | |
71 | let (reactors', newEvents) = updateMany reactors event | |
72 | eventLoop showEvents reactors' (events ++ newEvents) | |
71 | newEvents1 <- handleLineTerminalEvent event | |
72 | newEvents2 <- handleRandomSourceEvent event | |
73 | let (reactors', newEvents3) = updateMany reactors event | |
74 | eventLoop showEvents reactors' (events ++ newEvents1 ++ newEvents2 ++ newEvents3) | |
73 | 75 | |
74 | 76 | eventLoop showEvents reactors (event:events) = do |
75 | 77 | showEvent showEvents event |
94 | 96 | let payload = List (map (\x -> Number (fromIntegral $ Char.ord x)) inpStr) |
95 | 97 | return $ List [(Symbol "readln"), payload] |
96 | 98 | |
99 | handleLineTerminalEvent (List [Symbol "write", payload]) = do | |
100 | let List l = payload | |
101 | let s = map (\(Number x) -> Char.chr $ fromIntegral $ x) l | |
102 | hPutStr stdout s | |
103 | hFlush stdout | |
104 | return [] | |
97 | 105 | handleLineTerminalEvent (List [Symbol "writeln", payload]) = do |
98 | 106 | let List l = payload |
99 | 107 | let s = map (\(Number x) -> Char.chr $ fromIntegral $ x) l |
100 | 108 | hPutStrLn stdout s |
101 | handleLineTerminalEvent _ = return () | |
109 | hFlush stdout | |
110 | return [] | |
111 | handleLineTerminalEvent _ = return [] | |
112 | ||
113 | handleRandomSourceEvent (List [Symbol "obtain-random-u16", payload]) = do | |
114 | v <- randomRIO (0, 65535) | |
115 | return $ [List [Symbol "random-u16", Number v]] | |
116 | handleRandomSourceEvent _ = return [] |
4 | 4 | import Language.Robin.Eval |
5 | 5 | import Language.Robin.Reactor |
6 | 6 | |
7 | ||
8 | collect :: [Expr] -> Expr -> [Reactor] -> [Either Expr Expr] -> (Expr, [Reactor], [Either Expr Expr]) | |
7 | 9 | |
8 | 10 | collect [] env reactors results = (env, reactors, results) |
9 | 11 | |
22 | 24 | case eval (IEnv stop) env expr id of |
23 | 25 | Boolean False -> |
24 | 26 | error ("assertion failed: " ++ show expr) |
27 | _ -> | |
28 | collect rest env reactors results | |
29 | ||
30 | collect ((List [Symbol "require", expr]):rest) env reactors results = | |
31 | case Env.find expr env of | |
32 | Nothing -> | |
33 | error ("assertion failed: (bound? " ++ show expr ++ ")") | |
25 | 34 | _ -> |
26 | 35 | collect rest env reactors results |
27 | 36 |
4 | 4 | import System.Environment |
5 | 5 | import System.Exit |
6 | 6 | |
7 | import Language.Robin.Expr | |
7 | 8 | import Language.Robin.Env (mergeEnvs) |
8 | import Language.Robin.Parser (parseRobin) | |
9 | import Language.Robin.Parser (parseToplevel, parseExpr) | |
9 | 10 | import Language.Robin.Intrinsics (robinIntrinsics) |
10 | 11 | import Language.Robin.Builtins (robinBuiltins) |
11 | 12 | import qualified Language.Robin.TopLevel as TopLevel |
16 | 17 | args <- getArgs |
17 | 18 | case args of |
18 | 19 | [] -> do |
19 | abortWith "Usage: robin [--no-builtins] [--show-events] {source.robin}" | |
20 | abortWith "Usage: robin [--no-builtins] [--show-events] {[eval] source.robin}" | |
20 | 21 | _ -> do |
21 | 22 | let (args', env', showEvents) = processFlags args (mergeEnvs robinIntrinsics robinBuiltins) False |
22 | 23 | (_, reactors, results) <- processArgs args' env' |
37 | 38 | |
38 | 39 | processArgs args env = processArgs' args env [] [] where |
39 | 40 | processArgs' [] env reactors results = return (env, reactors, results) |
41 | processArgs' ("eval":filename:rest) env reactors results = do | |
42 | exprText <- readFile filename | |
43 | case parseExpr exprText of | |
44 | Right expr -> do | |
45 | let topExprs = [List [Symbol "display", expr]] | |
46 | (env', reactors', results') <- return $ TopLevel.collect topExprs env reactors results | |
47 | processArgs' rest env' reactors' results' | |
48 | Left problem -> do | |
49 | hPutStr stderr (show problem) | |
50 | exitWith $ ExitFailure 1 | |
40 | 51 | processArgs' (filename:rest) env reactors results = do |
41 | 52 | program <- readFile filename |
42 | case parseRobin program of | |
53 | case parseToplevel program of | |
43 | 54 | Right topExprs -> do |
44 | 55 | (env', reactors', results') <- return $ TopLevel.collect topExprs env reactors results |
45 | 56 | processArgs' rest env' reactors' results' |
0 | module QuickCheckTests where | |
1 | ||
2 | import Test.QuickCheck | |
3 | ||
4 | import Data.Int | |
5 | ||
6 | import System.IO | |
7 | import System.Environment | |
8 | import System.Exit | |
9 | ||
10 | import Language.Robin.Expr | |
11 | import Language.Robin.Env (mergeEnvs, fromList) | |
12 | import Language.Robin.Eval (eval) | |
13 | import Language.Robin.Parser (parseToplevel, parseExpr) | |
14 | import Language.Robin.Intrinsics (robinIntrinsics) | |
15 | import Language.Robin.Builtins (robinBuiltins) | |
16 | import qualified Language.Robin.TopLevel as TopLevel | |
17 | ||
18 | ||
19 | insist (Right x) = x | |
20 | ||
21 | robinExpr str = insist $ parseExpr str | |
22 | ||
23 | stdEval env expr = eval (IEnv stop) env expr id | |
24 | ||
25 | ||
26 | -- | |
27 | -- (> a b) should match Haskell's `a > b` in all cases. | |
28 | -- | |
29 | propGt :: Expr -> Int32 -> Int32 -> Bool | |
30 | propGt env a b = | |
31 | stdEval env expr == Boolean (a > b) | |
32 | where | |
33 | expr = List [Symbol ">", Number a, Number b] | |
34 | ||
35 | -- | |
36 | -- (< a b) should match Haskell's `a < b` in all cases. | |
37 | -- | |
38 | propLt :: Expr -> Int32 -> Int32 -> Bool | |
39 | propLt env a b = | |
40 | stdEval env expr == Boolean (a < b) | |
41 | where | |
42 | expr = List [Symbol "<", Number a, Number b] | |
43 | ||
44 | -- | |
45 | -- env? should evaluate to true on any valid binding alist. | |
46 | -- | |
47 | propEnv :: Expr -> [(String, Int32)] -> Bool | |
48 | propEnv env entries = | |
49 | stdEval env expr == Boolean True | |
50 | where | |
51 | expr = List [Symbol "env?", List [Symbol "literal", alist]] | |
52 | alist = fromList $ map (\(k,v) -> (k, Number v)) entries | |
53 | ||
54 | ||
55 | -- | |
56 | -- The following should be true for any symbol s and binding alist a: | |
57 | -- (lookup s (delete s a))) == () | |
58 | -- | |
59 | propDel :: Expr -> String -> [(String, Int32)] -> Property | |
60 | propDel env sym entries = | |
61 | sym /= "" ==> (stdEval env expr == List []) | |
62 | where | |
63 | litSym = List [Symbol "literal", Symbol sym] | |
64 | expr = List [Symbol "lookup", litSym, List [Symbol "delete", litSym, List [Symbol "literal", alist]]] | |
65 | alist = fromList $ map (\(k,v) -> (k, Number v)) entries | |
66 | ||
67 | -- | |
68 | -- The following should be true for any symbol s and binding alist a: | |
69 | -- (lookup s (extend s 1 x))) == (1) | |
70 | -- | |
71 | propExt :: Expr -> String -> [(String, Int32)] -> Property | |
72 | propExt env sym entries = | |
73 | sym /= "" ==> (stdEval env expr == List [Number 1]) | |
74 | where | |
75 | litSym = List [Symbol "literal", Symbol sym] | |
76 | expr = List [Symbol "lookup", litSym, List [Symbol "extend", litSym, Number 1, List [Symbol "literal", alist]]] | |
77 | alist = fromList $ map (\(k,v) -> (k, Number v)) entries | |
78 | ||
79 | ||
80 | testAll = do | |
81 | env <- loadStdEnv | |
82 | quickCheck (propGt env) | |
83 | quickCheck (propLt env) | |
84 | quickCheck (propEnv env) | |
85 | quickCheck (propDel env) | |
86 | quickCheck (propExt env) | |
87 | ||
88 | loadEnv filename env reactors results = do | |
89 | program <- readFile filename | |
90 | case parseToplevel program of | |
91 | Right topExprs -> do | |
92 | (env', reactors', results') <- return $ TopLevel.collect topExprs env reactors results | |
93 | return env' | |
94 | Left problem -> do | |
95 | hPutStr stderr (show problem) | |
96 | exitWith $ ExitFailure 1 | |
97 | ||
98 | loadStdEnv = loadEnv "pkg/stdlib.robin" (mergeEnvs robinIntrinsics robinBuiltins) [] [] |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Arith)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Arith)" | |
3 | 3 | |
4 | 4 | `abs` evaluates its single argument to a number, and evaluates to |
5 | 5 | the absolute value of that number (where the sign is always positive.) |
6 | 6 | |
7 | | (display | |
8 | | (abs 5)) | |
7 | | (abs 5) | |
9 | 8 | = 5 |
10 | 9 | |
11 | | (display | |
12 | | (abs (subtract 0 5))) | |
10 | | (abs (subtract 0 5)) | |
13 | 11 | = 5 |
14 | 12 | |
15 | | (display | |
16 | | (abs 0)) | |
13 | | (abs 0) | |
17 | 14 | = 0 |
18 | 15 | |
19 | 16 | `abs` expects exactly one numeric argument. |
20 | 17 | |
21 | | (display | |
22 | | (abs)) | |
18 | | (abs) | |
23 | 19 | ? uncaught exception: (illegal-arguments ()) |
24 | 20 | |
25 | | (display | |
26 | | (abs 14 23)) | |
21 | | (abs 14 23) | |
27 | 22 | ? uncaught exception: (illegal-arguments (14 23)) |
28 | 23 | |
29 | | (display | |
30 | | (abs #t)) | |
24 | | (abs #t) | |
31 | 25 | ? uncaught exception: (expected-number #t) |
32 | 26 | |
33 | 27 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Arith)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Arith)" | |
3 | 3 | |
4 | 4 | `add` evaluates both of its arguments to numbers and evaluates to the sum |
5 | 5 | of those two numbers. |
6 | 6 | |
7 | | (display | |
8 | | (add 14 23)) | |
7 | | (add 14 23) | |
9 | 8 | = 37 |
10 | 9 | |
11 | 10 | `add` expects exactly two arguments. |
12 | 11 | |
13 | | (display | |
14 | | (add 14)) | |
12 | | (add 14) | |
15 | 13 | ? uncaught exception: (illegal-arguments (14)) |
16 | 14 | |
17 | | (display | |
18 | | (add 6 7 7)) | |
15 | | (add 6 7 7) | |
19 | 16 | ? uncaught exception: (illegal-arguments (6 7 7)) |
20 | 17 | |
21 | 18 | Both of the arguments to `add` must be numbers. |
22 | 19 | |
23 | | (display | |
24 | | (add 14 #t)) | |
20 | | (add 14 #t) | |
25 | 21 | ? uncaught exception: (expected-number #t) |
26 | 22 | |
27 | | (display | |
28 | | (add #t 51)) | |
23 | | (add #t 51) | |
29 | 24 | ? uncaught exception: (expected-number #t) |
30 | 25 | |
31 | 26 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Boolean)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Boolean)" | |
3 | 3 | |
4 | 4 | `and` evaluates both of its arguments to booleans, and evaluates to the |
5 | 5 | logical conjunction (boolean "and") of these two values. |
6 | 6 | |
7 | | (display | |
8 | | (and #t #t)) | |
7 | | (and #t #t) | |
9 | 8 | = #t |
10 | 9 | |
11 | | (display | |
12 | | (and #t #f)) | |
10 | | (and #t #f) | |
13 | 11 | = #f |
14 | 12 | |
15 | | (display | |
16 | | (and #f #t)) | |
13 | | (and #f #t) | |
17 | 14 | = #f |
18 | 15 | |
19 | | (display | |
20 | | (and #f #f)) | |
16 | | (and #f #f) | |
21 | 17 | = #f |
22 | 18 | |
23 | 19 | `and` expects exactly two arguments. |
24 | 20 | |
25 | (Hate to weaken this test, but I'm not a purist -- yet.) | |
26 | ||
27 | | (display | |
28 | | (and #f)) | |
21 | | (and #f) | |
29 | 22 | ? uncaught exception |
30 | 23 | |
31 | | (display | |
32 | | (and #t #f #f)) | |
24 | | (and #t #f #f) | |
33 | 25 | ? uncaught exception: (illegal-arguments (#t #f #f)) |
34 | 26 | |
35 | 27 | `and` expects both of its arguments to be booleans. |
36 | 28 | |
37 | | (display | |
38 | | (and 100 #t)) | |
29 | | (and 100 #t) | |
39 | 30 | ? uncaught exception: (expected-boolean 100) |
40 | 31 | |
41 | | (display | |
42 | | (and #t 99)) | |
32 | | (and #t 99) | |
43 | 33 | ? uncaught exception: (expected-boolean 99) |
44 | 34 | |
45 | 35 | `and` is short-circuiting in the sense that no arguments after the first |
46 | 36 | `#f` argument will be evaluated. Fully testing this requires side-effects, |
47 | 37 | but it can be demonstrated as follows. |
48 | 38 | |
49 | | (display | |
50 | | (and #f 100)) | |
39 | | (and #f 100) | |
51 | 40 | = #f |
52 | 41 | |
53 | 42 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with List)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with List)" | |
3 | 3 | |
4 | 4 | `append` evaluates both of its arguments to lists. It then |
5 | 5 | evaluates to a list which is the concatenation of these lists. |
6 | 6 | |
7 | | (display | |
8 | | (append (list 1 2 3) (list 4 5 6))) | |
7 | | (append (list 1 2 3) (list 4 5 6)) | |
9 | 8 | = (1 2 3 4 5 6) |
10 | 9 | |
11 | | (display | |
12 | | (append () ())) | |
10 | | (append () ()) | |
13 | 11 | = () |
14 | 12 | |
15 | 13 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Small)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Small)" | |
3 | 3 | |
4 | 4 | `bind-args` is a macro for binding the arguments of another value to |
5 | 5 | identifiers, as well as asserting that the correct number of arguments |
11 | 11 | which those expressions will be evaluated, and an expression to evaluate |
12 | 12 | in the new environment in which the identifiers are bound. |
13 | 13 | |
14 | | (display | |
15 | | (bind-args (a b) (literal (1 2)) (env) | |
16 | | (list a b))) | |
14 | | (bind-args (a b) (literal (1 2)) (env) | |
15 | | (list a b)) | |
17 | 16 | = (1 2) |
18 | 17 | |
19 | 18 | Expressions in the list of values are evaluated. |
20 | 19 | |
21 | | (display | |
22 | | (bind-args (a b) (literal ((subtract 5 4) (subtract 10 1))) (env) | |
23 | | (list a b))) | |
20 | | (bind-args (a b) (literal ((subtract 5 4) (subtract 10 1))) (env) | |
21 | | (list a b)) | |
24 | 22 | = (1 9) |
25 | 23 | |
26 | 24 | Too many or too few arguments will raise an `illegal-arguments` |
27 | 25 | exception. |
28 | 26 | |
29 | | (display | |
30 | | (bind-args (a b) (literal (1)) (env) | |
31 | | (list a b))) | |
27 | | (bind-args (a b) (literal (1)) (env) | |
28 | | (list a b)) | |
32 | 29 | ? uncaught exception: (illegal-arguments (1)) |
33 | 30 | |
34 | | (display | |
35 | | (bind-args (a b) (literal (1 2 3)) (env) | |
36 | | (list a b))) | |
31 | | (bind-args (a b) (literal (1 2 3)) (env) | |
32 | | (list a b)) | |
37 | 33 | ? uncaught exception: (illegal-arguments (1 2 3)) |
38 | 34 | |
39 | 35 | The literal arguments are reported in the exception. |
40 | 36 | |
41 | | (display | |
42 | | (bind-args (a) (literal ((subtract 5 4) (subtract 1 0))) (env) | |
43 | | a)) | |
37 | | (bind-args (a) (literal ((subtract 5 4) (subtract 1 0))) (env) | |
38 | | a) | |
44 | 39 | ? uncaught exception: (illegal-arguments ((subtract 5 4) (subtract 1 0))) |
45 | 40 | |
46 | 41 | This is how it might be used in a macro definition. The reason for the |
48 | 43 | become clear here: typically you would just pass the macro's `args` and |
49 | 44 | `env` to those arguments. |
50 | 45 | |
51 | | (display | |
52 | | (bind add (macro (self args env) | |
53 | | (bind-args (a b) args env | |
54 | | (subtract a (subtract 0 b)))) | |
55 | | (add 4 (add 5 6)))) | |
46 | | (bind add (macro (self args env) | |
47 | | (bind-args (a b) args env | |
48 | | (subtract a (subtract 0 b)))) | |
49 | | (add 4 (add 5 6))) | |
56 | 50 | = 15 |
57 | 51 | |
58 | | (display | |
59 | | (bind add (macro (self args env) | |
60 | | (bind-args (a b) args env | |
61 | | (subtract a (subtract 0 b)))) | |
62 | | (bind r 7 | |
63 | | (add r r)))) | |
52 | | (bind add (macro (self args env) | |
53 | | (bind-args (a b) args env | |
54 | | (subtract a (subtract 0 b)))) | |
55 | | (bind r 7 | |
56 | | (add r r))) | |
64 | 57 | = 14 |
65 | 58 | |
66 | | (display | |
67 | | (bind add (macro (self args env) | |
68 | | (bind-args (a b) args env | |
69 | | (subtract a (subtract 0 b)))) | |
70 | | (add (subtract 0 0)))) | |
59 | | (bind add (macro (self args env) | |
60 | | (bind-args (a b) args env | |
61 | | (subtract a (subtract 0 b)))) | |
62 | | (add (subtract 0 0))) | |
71 | 63 | ? uncaught exception: (illegal-arguments ((subtract 0 0))) |
72 | 64 | |
73 | | (display | |
74 | | (bind add (macro (self args env) | |
75 | | (bind-args (a b) args env | |
76 | | (subtract a (subtract 0 b)))) | |
77 | | (add 9 9 9))) | |
65 | | (bind add (macro (self args env) | |
66 | | (bind-args (a b) args env | |
67 | | (subtract a (subtract 0 b)))) | |
68 | | (add 9 9 9)) | |
78 | 69 | ? uncaught exception: (illegal-arguments (9 9 9)) |
79 | 70 | |
80 | | (display | |
81 | | (bind add (macro (self args env) | |
82 | | (bind-args (a b) args env | |
83 | | (subtract a (subtract 0 b)))) | |
84 | | (add 1 n))) | |
71 | | (bind add (macro (self args env) | |
72 | | (bind-args (a b) args env | |
73 | | (subtract a (subtract 0 b)))) | |
74 | | (add 1 n)) | |
85 | 75 | ? uncaught exception: (unbound-identifier n) |
86 | 76 | |
87 | 77 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Small)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Small)" | |
3 | 3 | |
4 | 4 | `bind` binds a single identifier to the result of evaluating a single |
5 | 5 | expression, and makes that binding available in another expression which |
6 | 6 | it evaluates. |
7 | 7 | |
8 | | (display | |
9 | 8 | | (bind x (literal hello) |
10 | | (list x x))) | |
9 | | (list x x)) | |
11 | 10 | = (hello hello) |
12 | 11 | |
13 | | (display | |
14 | 12 | | (bind dup (macro (self args env) |
15 | 13 | | (list (head args) (head args))) |
16 | | (dup g))) | |
14 | | (dup g)) | |
17 | 15 | = (g g) |
18 | 16 | |
19 | | (display | |
20 | 17 | | (bind dup (macro (self args env) |
21 | 18 | | (bind x (eval env (head args)) |
22 | 19 | | (list x x))) |
23 | | (dup (literal g)))) | |
20 | | (dup (literal g))) | |
24 | 21 | = (g g) |
25 | 22 | |
26 | | (display | |
27 | 23 | | (bind dup (macro (self args env) |
28 | 24 | | (bind x (eval env (head args)) |
29 | 25 | | (list x x))) |
30 | | (dup (dup (literal g))))) | |
26 | | (dup (dup (literal g)))) | |
31 | 27 | = ((g g) (g g)) |
32 | 28 | |
33 | | (display | |
34 | 29 | | (bind find (macro (self args env) |
35 | 30 | | (bind-args (alist key) args env |
36 | 31 | | (if (equal? alist (literal ())) (literal ()) |
37 | 32 | | (if (equal? key (head (head alist))) |
38 | 33 | | (head alist) |
39 | 34 | | (self (tail alist) key))))) |
40 | | (find (literal ((c d) (e f) (a b))) (literal a)))) | |
35 | | (find (literal ((c d) (e f) (a b))) (literal a))) | |
41 | 36 | = (a b) |
42 | 37 | |
43 | 38 | `bind` expects exactly three arguments, or else an exception will be raised. |
44 | 39 | |
45 | | (display | |
46 | | (bind smoosh (fun (x y) (list y x)))) | |
40 | | (bind smoosh (fun (x y) (list y x))) | |
47 | 41 | ? uncaught exception |
48 | 42 | |
49 | | (display | |
50 | | (bind smoosh)) | |
43 | | (bind smoosh) | |
51 | 44 | ? uncaught exception |
52 | 45 | |
53 | | (display | |
54 | | (bind)) | |
46 | | (bind) | |
55 | 47 | ? uncaught exception |
56 | 48 | |
57 | 49 | `bind` is basically equivalent to Scheme's `let`, but only one |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Boolean)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Boolean)" | |
3 | 3 | |
4 | 4 | `boolean?` evaluates its argument, then evaluates to `#t` if it is a |
5 | 5 | boolean value, `#f` otherwise. |
6 | 6 | |
7 | | (display | |
8 | | (boolean? #t)) | |
7 | | (boolean? #t) | |
9 | 8 | = #t |
10 | 9 | |
11 | | (display | |
12 | | (boolean? (head (prepend #f ())))) | |
10 | | (boolean? (head (prepend #f ()))) | |
13 | 11 | = #t |
14 | 12 | |
15 | | (display | |
16 | | (boolean? ())) | |
13 | | (boolean? ()) | |
17 | 14 | = #f |
18 | 15 | |
19 | 16 | The argument to `boolean?` may (naturally) be any type, but there must be |
20 | 17 | exactly one argument. |
21 | 18 | |
22 | | (display | |
23 | | (boolean? #t #f)) | |
19 | | (boolean? #t #f) | |
24 | 20 | ? uncaught exception: (illegal-arguments (#t #f)) |
25 | 21 | |
26 | | (display | |
27 | | (boolean?)) | |
22 | | (boolean?) | |
28 | 23 | ? uncaught exception: (illegal-arguments ()) |
29 | 24 | |
30 | 25 | '<<SPEC' |
0 | ;'<<SPEC' | |
1 | ||
2 | -> Tests for functionality "Evaluate Robin Expression (with Env)" | |
3 | ||
4 | `bound?` takes a single argument, which should be a symbol, and | |
5 | evaluates to `#t` if it the symbol is bound to a value in the | |
6 | current environment, or `#f` if it is not. | |
7 | ||
8 | | (bound? bound?) | |
9 | = #t | |
10 | ||
11 | | (bound? xuzumsunazun) | |
12 | = #f | |
13 | ||
14 | | (bind xuzumsunazun 3 (bound? xuzumsunazun)) | |
15 | = #t | |
16 | ||
17 | The single argument to `bound?` should be a symbol. | |
18 | ||
19 | | (bound? 3) | |
20 | ? uncaught exception: (expected-symbol 3) | |
21 | ||
22 | `bound?` expects exactly two arguments. | |
23 | ||
24 | | (bound? bound? bound?) | |
25 | ? uncaught exception: (illegal-arguments (bound? bound?)) | |
26 | ||
27 | | (bound?) | |
28 | ? uncaught exception: (illegal-arguments ()) | |
29 | ||
30 | '<<SPEC' | |
31 | ||
32 | (define bound? (macro (self args env) | |
33 | (if (equal? args ()) | |
34 | (raise (list (literal illegal-arguments) args)) | |
35 | (if (equal? (tail args) ()) | |
36 | (bind symbol (head args) | |
37 | (if (symbol? symbol) | |
38 | (if (equal? (lookup symbol env) ()) #f #t) | |
39 | (raise (list (literal expected-symbol) symbol)))) | |
40 | (raise (list (literal illegal-arguments) args)))))) |
1 | 1 | |
2 | 2 | ### `catch` ### |
3 | 3 | |
4 | -> Tests for functionality "Interpret core Robin Program" | |
4 | FIXME: We don't really need all of List for these tests, just `list` | |
5 | and `literal`. | |
6 | ||
7 | -> Tests for functionality "Evaluate Robin Expression (with List)" | |
5 | 8 | |
6 | 9 | `catch` installs an exception handler. |
7 | 10 | |
10 | 13 | first argument of `catch`, and the second argument of `catch` is |
11 | 14 | evaluated in that new environment. |
12 | 15 | |
13 | | (define literal (macro (s a e) (head a))) | |
14 | | (define list (macro (self args env) | |
15 | | (if (equal? args ()) | |
16 | | () | |
17 | | (prepend (eval env (head args)) | |
18 | | (eval env (prepend self (tail args))))))) | |
19 | | (display | |
20 | | (catch error (list error #f) | |
21 | | (raise (literal (nasty-value 999999))))) | |
16 | | (catch error (list error #f) | |
17 | | (raise (literal (nasty-value 999999)))) | |
22 | 18 | = ((nasty-value 999999) #f) |
23 | 19 | |
24 | `catch` *cannot necessarily* catch exceptions raised by intrinsics. | |
25 | It ought to be able to catch exceptions raised by intrinsics wrappers, | |
26 | though. | |
20 | If an exception is raised in the body of a macro that is applied | |
21 | in the context of a `catch`, it will still be catched. | |
22 | ||
23 | | (bind errorful (macro (self args env) (raise (literal (nasty-value 1111)))) | |
24 | | (catch error (list error #f) | |
25 | | (errorful joe))) | |
26 | = ((nasty-value 1111) #f) | |
27 | ||
28 | If nothing is raised, `catch` will evaluate to whatever its body | |
29 | evaluated to. | |
30 | ||
31 | | (catch error (list error #f) 42) | |
32 | = 42 | |
27 | 33 | |
28 | 34 | The innermost `catch` will catch the exception. |
29 | 35 | |
30 | | (define literal (macro (s a e) (head a))) | |
31 | | (define list (macro (self args env) | |
32 | | (if (equal? args ()) | |
33 | | () | |
34 | | (prepend (eval env (head args)) | |
35 | | (eval env (prepend self (tail args))))))) | |
36 | | (display | |
37 | | (catch error (list error 5) | |
38 | | (catch error (list error 9) | |
39 | | (raise (literal derpy-value))))) | |
36 | | (catch error (list error 5) | |
37 | | (catch error (list error 9) | |
38 | | (raise (literal derpy-value)))) | |
40 | 39 | = (derpy-value 9) |
41 | 40 | |
42 | 41 | An exception raised from within an exception handler is |
43 | 42 | caught by the next innermost exception handler. |
44 | 43 | |
45 | | (define list (macro (self args env) | |
46 | | (if (equal? args ()) | |
47 | | () | |
48 | | (prepend (eval env (head args)) | |
49 | | (eval env (prepend self (tail args))))))) | |
50 | | (display | |
51 | | (catch error (list error 5) | |
52 | | (catch error (list error 9) | |
53 | | (catch error (raise (list error error)) | |
54 | | (raise 7))))) | |
44 | | (catch error (list error 5) | |
45 | | (catch error (list error 9) | |
46 | | (catch error (raise (list error error)) | |
47 | | (raise 7)))) | |
55 | 48 | = ((7 7) 9) |
56 | 49 | |
57 | `catch` expects its first argument to be an identifier. | |
50 | `catch` expects its first argument to be a symbol. | |
51 | ||
52 | | (catch (3 4) (list error #f) 42) | |
53 | ? uncaught exception: (illegal-arguments ((3 4) (list error #f) 42)) | |
58 | 54 | |
59 | 55 | `catch` expects exactly three arguments. |
60 | 56 | |
61 | TODO we should probably have some tests that prove that `catch` can | |
62 | catch errors raised from inside macros. | |
57 | | (catch error (list error #f)) | |
58 | ? uncaught exception: (illegal-arguments (error (list error #f))) | |
59 | ||
60 | | (catch error (list error #f) 42 43) | |
61 | ? uncaught exception: (illegal-arguments (error (list error #f) 42 43)) | |
63 | 62 | |
64 | 63 | '<<SPEC' |
64 | ||
65 | (require catch) |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Small)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Small)" | |
3 | 3 | |
4 | | (display | |
5 | | (choose (#t (literal hi)) (else (literal lo)))) | |
4 | | (choose (#t (literal hi)) (else (literal lo))) | |
6 | 5 | = hi |
7 | 6 | |
8 | | (display | |
9 | | (choose (#f (literal hi)) (#t (literal med)) (else (literal lo)))) | |
7 | | (choose (#f (literal hi)) (#t (literal med)) (else (literal lo))) | |
10 | 8 | = med |
11 | 9 | |
12 | | (display | |
13 | | (choose (#f (literal hi)) (#f (literal med)) (else (literal lo)))) | |
10 | | (choose (#f (literal hi)) (#f (literal med)) (else (literal lo))) | |
14 | 11 | = lo |
15 | 12 | |
16 | 13 | `choose` can have zero tests before the `else`. |
17 | 14 | |
18 | | (display | |
19 | | (choose (else (literal woo)))) | |
15 | | (choose (else (literal woo))) | |
20 | 16 | = woo |
21 | 17 | |
22 | 18 | `choose` does require an `else` branch, or else an exception will be |
23 | 19 | raised. |
24 | 20 | |
25 | | (display | |
26 | | (choose (#f (literal hi)) (#f (literal med)))) | |
21 | | (choose (#f (literal hi)) (#f (literal med))) | |
27 | 22 | ? uncaught exception |
28 | 23 | |
29 | | (display | |
30 | | (choose)) | |
24 | | (choose) | |
31 | 25 | ? uncaught exception |
32 | 26 | |
33 | 27 | Each branch of a `choose` needs to be a two-element list, or else an |
34 | 28 | exception will be raised. |
35 | 29 | |
36 | | (display | |
37 | | (choose (#t) (else (literal lo)))) | |
30 | | (choose (#t) (else (literal lo))) | |
38 | 31 | ? uncaught exception |
39 | 32 | |
40 | | (display | |
41 | | (choose (#f 66) (else))) | |
33 | | (choose (#f 66) (else)) | |
42 | 34 | ? uncaught exception |
43 | 35 | |
44 | 36 | `choose` is basically equivalent to Scheme's `cond`. |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Arith)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Arith)" | |
3 | 3 | |
4 | 4 | ### `>` ### |
5 | 5 | |
6 | 6 | `>` evaluates both of its arguments to numbers, then evaluates to `#t` |
7 | 7 | if the first number is strictly greater than the second. |
8 | 8 | |
9 | | (display | |
10 | | (> 6 4)) | |
9 | | (> 6 4) | |
11 | 10 | = #t |
12 | 11 | |
13 | | (display | |
14 | | (> 6 8)) | |
12 | | (> 6 8) | |
15 | 13 | = #f |
16 | 14 | |
17 | | (display | |
18 | | (> 6 6)) | |
15 | | (> 6 6) | |
19 | 16 | = #f |
17 | ||
18 | | (> 1610612736 (subtract 0 1610612736))) | |
19 | = #t | |
20 | ||
21 | | (> (subtract 0 1610612736) 1610612736) | |
22 | = #f | |
23 | ||
24 | | (> 2147483646 2147483647) | |
25 | = #f | |
26 | ||
27 | | (> 1 2147483647) | |
28 | = #f | |
29 | ||
30 | | (> (subtract 0 2147483647) (subtract 0 2147483646)) | |
31 | = #f | |
32 | ||
33 | | (> (subtract 0 2147483647) (subtract 0 1)) | |
34 | = #f | |
35 | ||
36 | | (> 0 (subtract (subtract 0 1073741824) 1073741824))) | |
37 | = #t | |
20 | 38 | |
21 | 39 | `>` expects exactly two arguments, both numbers. |
22 | 40 | |
23 | | (display | |
24 | | (> 14)) | |
41 | | (> 14) | |
25 | 42 | ? uncaught exception: (illegal-arguments (14)) |
26 | 43 | |
27 | | (display | |
28 | | (> 14 23 57)) | |
44 | | (> 14 23 57) | |
29 | 45 | ? uncaught exception: (illegal-arguments (14 23 57)) |
30 | 46 | |
31 | | (display | |
32 | | (> 14 #t)) | |
47 | | (> 14 #t) | |
33 | 48 | ? uncaught exception: (expected-number #t) |
34 | 49 | |
35 | | (display | |
36 | | (> #t 51)) | |
50 | | (> #t 51) | |
37 | 51 | ? uncaught exception: (expected-number #t) |
38 | 52 | |
39 | 53 | ### `<` ### |
41 | 55 | `<` evaluates both of its arguments to numbers, then evaluates to `#t` |
42 | 56 | if the first number is strictly less than the second. |
43 | 57 | |
44 | | (display | |
45 | | (< 6 4)) | |
58 | | (< 6 4) | |
46 | 59 | = #f |
47 | 60 | |
48 | | (display | |
49 | | (< 6 8)) | |
61 | | (< 6 8) | |
50 | 62 | = #t |
51 | 63 | |
52 | | (display | |
53 | | (< 6 6)) | |
64 | | (< 6 6) | |
54 | 65 | = #f |
66 | ||
67 | | (< 1610612736 (subtract 0 1610612736))) | |
68 | = #f | |
69 | ||
70 | | (< (subtract 0 1610612736) 1610612736) | |
71 | = #t | |
72 | ||
73 | | (< 2147483646 2147483647) | |
74 | = #t | |
75 | ||
76 | | (< 1 2147483647) | |
77 | = #t | |
78 | ||
79 | | (< (subtract 0 2147483647) (subtract 0 2147483646)) | |
80 | = #t | |
81 | ||
82 | | (< (subtract 0 2147483647) (subtract 0 1)) | |
83 | = #t | |
84 | ||
85 | | (< (subtract (subtract 0 1073741824) 1073741824) 0) | |
86 | = #t | |
55 | 87 | |
56 | 88 | `<` expects exactly two arguments, both numbers. |
57 | 89 | |
58 | | (display | |
59 | | (< 14)) | |
90 | | (< 14) | |
60 | 91 | ? uncaught exception: (illegal-arguments (14)) |
61 | 92 | |
62 | | (display | |
63 | | (< 14 23 57)) | |
93 | | (< 14 23 57) | |
64 | 94 | ? uncaught exception: (illegal-arguments (14 23 57)) |
65 | 95 | |
66 | | (display | |
67 | | (< 14 #t)) | |
96 | | (< 14 #t) | |
68 | 97 | ? uncaught exception: (expected-number #t) |
69 | 98 | |
70 | | (display | |
71 | | (< #t 51)) | |
99 | | (< #t 51) | |
72 | 100 | ? uncaught exception: (expected-number #t) |
73 | 101 | |
74 | 102 | ### `>=` ### |
76 | 104 | `>=` evaluates both of its arguments to numbers, then evaluates to `#t` |
77 | 105 | if the first number is greater than or equal to the second. |
78 | 106 | |
79 | | (display | |
80 | | (>= 6 4)) | |
107 | | (>= 6 4) | |
81 | 108 | = #t |
82 | 109 | |
83 | | (display | |
84 | | (>= 6 8)) | |
110 | | (>= 6 8) | |
85 | 111 | = #f |
86 | 112 | |
87 | | (display | |
88 | | (>= 6 6)) | |
113 | | (>= 6 6) | |
89 | 114 | = #t |
115 | ||
116 | | (>= 1610612736 (subtract 0 1610612736))) | |
117 | = #t | |
118 | ||
119 | | (>= (subtract 0 1610612736) 1610612736) | |
120 | = #f | |
90 | 121 | |
91 | 122 | `>=` expects exactly two arguments, both numbers. |
92 | 123 | |
93 | | (display | |
94 | | (>= 14)) | |
124 | | (>= 14) | |
95 | 125 | ? uncaught exception: (illegal-arguments (14)) |
96 | 126 | |
97 | | (display | |
98 | | (>= 14 23 57)) | |
127 | | (>= 14 23 57) | |
99 | 128 | ? uncaught exception: (illegal-arguments (14 23 57)) |
100 | 129 | |
101 | | (display | |
102 | | (>= 14 #t)) | |
130 | | (>= 14 #t) | |
103 | 131 | ? uncaught exception: (expected-number #t) |
104 | 132 | |
105 | | (display | |
106 | | (>= #t 51)) | |
133 | | (>= #t 51) | |
107 | 134 | ? uncaught exception: (expected-number #t) |
108 | 135 | |
109 | 136 | ### `<=` ### |
111 | 138 | `<=` evaluates both of its arguments to numbers, then evaluates to `#t` |
112 | 139 | if the first number is less than or equal to the second. |
113 | 140 | |
114 | | (display | |
115 | | (<= 6 4)) | |
141 | | (<= 6 4) | |
116 | 142 | = #f |
117 | 143 | |
118 | | (display | |
119 | | (<= 6 8)) | |
144 | | (<= 6 8) | |
120 | 145 | = #t |
121 | 146 | |
122 | | (display | |
123 | | (<= 6 6)) | |
147 | | (<= 6 6) | |
148 | = #t | |
149 | ||
150 | | (<= 1610612736 (subtract 0 1610612736))) | |
151 | = #f | |
152 | ||
153 | | (<= (subtract 0 1610612736) 1610612736) | |
124 | 154 | = #t |
125 | 155 | |
126 | 156 | `<=` expects exactly two arguments, both numbers. |
127 | 157 | |
128 | | (display | |
129 | | (<= 14)) | |
158 | | (<= 14) | |
130 | 159 | ? uncaught exception: (illegal-arguments (14)) |
131 | 160 | |
132 | | (display | |
133 | | (<= 14 23 57)) | |
161 | | (<= 14 23 57) | |
134 | 162 | ? uncaught exception: (illegal-arguments (14 23 57)) |
135 | 163 | |
136 | | (display | |
137 | | (<= 14 #t)) | |
164 | | (<= 14 #t) | |
138 | 165 | ? uncaught exception: (expected-number #t) |
139 | 166 | |
140 | | (display | |
141 | | (<= #t 51)) | |
167 | | (<= #t 51) | |
142 | 168 | ? uncaught exception: (expected-number #t) |
143 | 169 | |
144 | 170 | '<<SPEC' |
145 | 171 | |
172 | (define cmp-same-sign? (macro (self args env) | |
173 | (bind-args (a b c) args env | |
174 | (equal? (sign (subtract a b)) c)))) | |
175 | ||
146 | 176 | (define > (macro (self args env) |
147 | 177 | (bind-args (a b) args env |
148 | (equal? (sign (subtract a b)) 1)))) | |
178 | (if (equal? (sign a) (sign b)) | |
179 | (cmp-same-sign? a b 1) | |
180 | (cmp-same-sign? (subtract (sign a) 1) (subtract (sign b) 1) 1))))) | |
181 | ||
149 | 182 | (define >= (macro (self args env) |
150 | 183 | (bind-args (a b) args env |
151 | (if (equal? a b) #t (equal? (sign (subtract a b)) 1))))) | |
184 | (if (equal? a b) #t (> a b))))) | |
185 | ||
152 | 186 | (define < (macro (self args env) |
153 | 187 | (bind-args (a b) args env |
154 | (equal? (sign (subtract a b)) (subtract 0 1))))) | |
188 | (if (equal? (sign a) (sign b)) | |
189 | (cmp-same-sign? a b (subtract 0 1)) | |
190 | (cmp-same-sign? (subtract (sign a) 1) (subtract (sign b) 1) (subtract 0 1)))))) | |
191 | ||
155 | 192 | (define <= (macro (self args env) |
156 | 193 | (bind-args (a b) args env |
157 | (if (equal? a b) #t (equal? (sign (subtract a b)) (subtract 0 1)))))) | |
194 | (if (equal? a b) #t (< a b))))) |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with List)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with List)" | |
3 | 3 | |
4 | | (display | |
5 | | (delete (literal b) (literal ((a 1) (b 2) (c 3))))) | |
4 | | (delete (literal b) (literal ((a 1) (b 2) (c 3)))) | |
6 | 5 | = ((a 1) (c 3)) |
7 | 6 | |
8 | | (display | |
9 | | (delete (literal b) (literal ((a 1) (b 2) (c 3) (b 4))))) | |
7 | | (delete (literal b) (literal ((a 1) (b 2) (c 3) (b 4)))) | |
10 | 8 | = ((a 1) (c 3)) |
11 | 9 | |
12 | | (display | |
13 | | (delete (literal r) (literal ((a 1) (b 2) (c 3))))) | |
10 | | (delete (literal r) (literal ((a 1) (b 2) (c 3)))) | |
14 | 11 | = ((a 1) (b 2) (c 3)) |
15 | 12 | |
16 | 13 | The following should be true for any identifier i and alist x. |
17 | 14 | |
18 | | (display | |
19 | 15 | | (let ((i (literal a)) |
20 | 16 | | (x (literal ((a 5) (b 7))))) |
21 | | (lookup i (delete i x)))) | |
17 | | (lookup i (delete i x))) | |
22 | 18 | = () |
23 | 19 | |
24 | | (display | |
25 | | (delete (literal q) 55)) | |
20 | | (delete (literal q) 55) | |
26 | 21 | ? uncaught exception: (expected-list 55) |
27 | 22 | |
28 | | (display | |
29 | | (delete (literal q) (literal ((a 7) 99 (q 4))))) | |
23 | | (delete (literal q) (literal ((a 7) 99 (q 4)))) | |
30 | 24 | ? uncaught exception: (expected-list 99) |
31 | 25 | |
32 | 26 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Arith)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Arith)" | |
3 | 3 | |
4 | 4 | `divide` evaluates both of its arguments to numbers and evaluates to the |
5 | 5 | result of integer division of the first number by the second. Integer |
6 | 6 | division computes by what integer the second number can be multiplied |
7 | 7 | to make it as big as possible without exceeding the first number. |
8 | 8 | |
9 | | (display | |
10 | | (divide 100 3)) | |
9 | | (divide 100 3) | |
11 | 10 | = 33 |
12 | 11 | |
13 | | (display | |
14 | | (divide (subtract 0 100) 3)) | |
12 | | (divide (subtract 0 100) 3) | |
15 | 13 | = -34 |
16 | 14 | |
17 | | (display | |
18 | | (divide 100 (subtract 0 3))) | |
15 | | (divide 100 (subtract 0 3)) | |
19 | 16 | = -34 |
20 | 17 | |
21 | | (display | |
22 | | (divide 33 33)) | |
18 | | (divide 33 33) | |
23 | 19 | = 1 |
24 | 20 | |
25 | | (display | |
26 | | (divide 33 34)) | |
21 | | (divide 33 34) | |
27 | 22 | = 0 |
28 | 23 | |
29 | | (display | |
30 | | (divide 10 0)) | |
24 | | (divide 10 0) | |
31 | 25 | ? uncaught exception: (division-by-zero 10) |
32 | 26 | |
33 | 27 | Division by zero is undefined, and an exception will be raised. |
34 | 28 | |
35 | | (display | |
36 | | (divide 10 0)) | |
29 | | (divide 10 0) | |
37 | 30 | ? uncaught exception: (division-by-zero 10) |
38 | 31 | |
39 | 32 | `div` expects exactly two arguments, both numbers. |
40 | 33 | |
41 | | (display | |
42 | | (divide 14)) | |
34 | | (divide 14) | |
43 | 35 | ? uncaught exception: (illegal-arguments (14)) |
44 | 36 | |
45 | | (display | |
46 | | (divide 14 23 57)) | |
37 | | (divide 14 23 57) | |
47 | 38 | ? uncaught exception: (illegal-arguments (14 23 57)) |
48 | 39 | |
49 | | (display | |
50 | | (divide 14 #t)) | |
40 | | (divide 14 #t) | |
51 | 41 | ? uncaught exception: (expected-number #t) |
52 | 42 | |
53 | | (display | |
54 | | (divide #t 51)) | |
43 | | (divide #t 51) | |
55 | 44 | ? uncaught exception: (expected-number #t) |
56 | 45 | |
57 | 46 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with List)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with List)" | |
3 | 3 | |
4 | 4 | `drop-while` evaluates its first argument to obtain a predicate and its |
5 | 5 | second argument to obtain a list. It then evaluates to the suffix of the |
6 | 6 | given list, starting at the first element which does not satisfy the |
7 | 7 | predicate. |
8 | 8 | |
9 | | (display | |
10 | | (drop-while (fun (x) (symbol? x)) (literal (one two 3 4 five 6 seven)))) | |
9 | | (drop-while (fun (x) (symbol? x)) (literal (one two 3 4 five 6 seven))) | |
11 | 10 | = (3 4 five 6 seven) |
12 | 11 | |
13 | | (display | |
14 | | (drop-while (fun (x) (symbol? x)) (literal (1 2 3 4 5 6)))) | |
12 | | (drop-while (fun (x) (symbol? x)) (literal (1 2 3 4 5 6))) | |
15 | 13 | = (1 2 3 4 5 6) |
16 | 14 | |
17 | | (display | |
18 | | (drop-while (fun (x) (number? x)) (literal (1 2 3 4 5 6)))) | |
15 | | (drop-while (fun (x) (number? x)) (literal (1 2 3 4 5 6))) | |
19 | 16 | = () |
20 | 17 | |
21 | | (display | |
22 | | (drop-while (fun (x) (symbol? x)) ())) | |
18 | | (drop-while (fun (x) (symbol? x)) ()) | |
23 | 19 | = () |
24 | 20 | |
25 | | (display | |
26 | | (drop-while (fun (x) (symbol? x)) #f)) | |
21 | | (drop-while (fun (x) (symbol? x)) #f) | |
27 | 22 | ? uncaught exception: (expected-list #f) |
28 | 23 | |
29 | 24 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with List)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with List)" | |
3 | 3 | |
4 | 4 | `elem?` evaluates its first argument to a value of any type, and its |
5 | 5 | second argument to obtain a list. It then evaluates to `#t` if the value |
6 | 6 | is `equal?` to some element of the list, `#f` otherwise. |
7 | 7 | |
8 | | (display | |
9 | | (elem? (literal p) (literal (a p e)))) | |
8 | | (elem? (literal p) (literal (a p e))) | |
10 | 9 | = #t |
11 | 10 | |
12 | | (display | |
13 | | (elem? (literal p) (literal (a r k)))) | |
11 | | (elem? (literal p) (literal (a r k))) | |
14 | 12 | = #f |
15 | 13 | |
16 | | (display | |
17 | | (elem? 7 ())) | |
14 | | (elem? 7 ()) | |
18 | 15 | = #f |
19 | 16 | |
20 | | (display | |
21 | | (elem? 7 (list 5 (list 6 7) 8))) | |
17 | | (elem? 7 (list 5 (list 6 7) 8)) | |
22 | 18 | = #f |
23 | 19 | |
24 | 20 | `elem?` can be defined in terms of `find`, in a manner such as: |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with List)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with List)" | |
3 | 3 | |
4 | 4 | `empty?` evaluates its single argument, and evaluates to `#t` if that value |
5 | 5 | is the empty list, `#f` otherwise. |
6 | 6 | |
7 | | (display | |
8 | | (empty? (literal symbol))) | |
7 | | (empty? (literal symbol)) | |
9 | 8 | = #f |
10 | 9 | |
11 | | (display | |
12 | | (empty? ())) | |
10 | | (empty? ()) | |
13 | 11 | = #t |
14 | 12 | |
15 | | (display | |
16 | | (empty? (list 1 2 3))) | |
13 | | (empty? (list 1 2 3)) | |
17 | 14 | = #f |
18 | 15 | |
19 | 16 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Env)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Env)" | |
3 | 3 | |
4 | 4 | `env?` evaluates its single argument, and evaluates to `#t` if |
5 | 5 | and only if it is a well-formed binding alist. |
6 | 6 | |
7 | | (display | |
8 | | (env? (literal ((a 1) (b 2) (c 3))))) | |
7 | | (env? (literal ((a 1) (b 2) (c 3)))) | |
9 | 8 | = #t |
10 | 9 | |
11 | | (display | |
12 | | (env? (literal ((a 1) (999 2) (c 3))))) | |
10 | | (env? (literal ((a 1) (999 2) (c 3)))) | |
13 | 11 | = #f |
14 | 12 | |
15 | | (display | |
16 | | (env? (literal ((a 1) (b 2) c)))) | |
13 | | (env? (literal ((a 1) (b 2) c))) | |
17 | 14 | = #f |
18 | 15 | |
19 | | (display | |
20 | | (env? 7)) | |
16 | | (env? 7) | |
21 | 17 | = #f |
22 | 18 | |
23 | | (display | |
24 | | (env? (env))) | |
19 | | (env? (env)) | |
25 | 20 | = #t |
26 | 21 | |
27 | 22 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Small)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Small)" | |
3 | 3 | |
4 | 4 | `env` evaluates to all the bindings in effect at the point of execution |
5 | 5 | where this form is encountered, as an alist. |
6 | 6 | |
7 | | (display | |
8 | 7 | | (bind find (macro (self args env) |
9 | 8 | | (bind-args (alist key) args env |
10 | 9 | | (if (equal? alist (literal ())) (literal ()) |
12 | 11 | | (head alist) |
13 | 12 | | (self (tail alist) key))))) |
14 | 13 | | (prepend |
15 | | (find (env) (literal symbol?)) (find (env) (literal prepend))))) | |
14 | | (find (env) (literal symbol?)) (find (env) (literal prepend)))) | |
16 | 15 | = ((symbol? symbol?) prepend prepend) |
17 | 16 | |
18 | 17 | `env` expects no arguments. Any arguments supplied will be simply ignored |
19 | 18 | and discarded, without being evaluated. |
20 | 19 | |
21 | | (display | |
22 | 20 | | (bind find (macro (self args env) |
23 | 21 | | (bind-args (alist key) args env |
24 | 22 | | (if (equal? alist (literal ())) (literal ()) |
27 | 25 | | (self (tail alist) key))))) |
28 | 26 | | (prepend |
29 | 27 | | (find (env find) (literal symbol?)) |
30 | | (find (env (goofah whatever)) (literal prepend))))) | |
28 | | (find (env (goofah whatever)) (literal prepend)))) | |
31 | 29 | = ((symbol? symbol?) prepend prepend) |
32 | 30 | |
33 | 31 | '<<SPEC' |
1 | 1 | |
2 | 2 | ### `equal?` ### |
3 | 3 | |
4 | -> Tests for functionality "Interpret core Robin Program" | |
4 | -> Tests for functionality "Evaluate core Robin Expression" | |
5 | 5 | |
6 | 6 | `equal?` evaluates both of its arguments to arbitrary S-expressions |
7 | 7 | and compares them for deep equality. |
8 | 8 | |
9 | 9 | `equal?` works on symbols. |
10 | 10 | |
11 | | (define literal (macro (s a e) (head a))) | |
12 | | (display | |
13 | | (equal? | |
14 | | (literal this-symbol) | |
15 | | (literal this-symbol))) | |
11 | | (equal? | |
12 | | ((macro (s a e) (head a)) this-symbol) | |
13 | | ((macro (s a e) (head a)) this-symbol)) | |
16 | 14 | = #t |
17 | 15 | |
18 | | (define literal (macro (s a e) (head a))) | |
19 | | (display | |
20 | | (equal? | |
21 | | (literal this-symbol) | |
22 | | (literal that-symbol))) | |
16 | | (equal? | |
17 | | ((macro (s a e) (head a)) this-symbol) | |
18 | | ((macro (s a e) (head a)) that-symbol)) | |
23 | 19 | = #f |
24 | 20 | |
25 | 21 | `equal?` works on lists. |
26 | 22 | |
27 | | (display | |
28 | | (equal? (prepend 1 (prepend 2 (prepend 3 ()))) | |
29 | | (prepend 1 (prepend 2 (prepend 3 ()))))) | |
23 | | (equal? (prepend 1 (prepend 2 (prepend 3 ()))) | |
24 | | (prepend 1 (prepend 2 (prepend 3 ())))) | |
30 | 25 | = #t |
31 | 26 | |
32 | 27 | `equal?` works on lists, deeply. |
33 | 28 | |
34 | | (display | |
35 | | (equal? (prepend 1 (prepend 2 (prepend 7 ()))) | |
36 | | (prepend 1 (prepend 2 (prepend 3 ()))))) | |
29 | | (equal? (prepend 1 (prepend 2 (prepend 7 ()))) | |
30 | | (prepend 1 (prepend 2 (prepend 3 ())))) | |
37 | 31 | = #f |
38 | 32 | |
39 | 33 | Two values of different types are never equal. |
40 | 34 | |
41 | | (define literal (macro (s a e) (head a))) | |
42 | | (display | |
43 | | (equal? #t | |
44 | | (prepend (literal a) ()))) | |
35 | | (equal? #t (prepend ((macro (s a e) (head a)) a) ())) | |
45 | 36 | = #f |
46 | 37 | |
47 | | (display | |
48 | | (equal? #f | |
49 | | ())) | |
38 | | (equal? #f ()) | |
50 | 39 | = #f |
51 | 40 | |
52 | 41 | Arguments to `equal?` can be any type, but fewer than or more than |
53 | 42 | two arguments will raise an exception. |
54 | 43 | |
55 | | (display | |
56 | | (equal? 7)) | |
44 | | (equal? 7) | |
57 | 45 | ? uncaught exception: (illegal-arguments (7)) |
58 | 46 | |
59 | | (display | |
60 | | (equal? 7 8 9)) | |
47 | | (equal? 7 8 9) | |
61 | 48 | ? uncaught exception: (illegal-arguments (7 8 9)) |
62 | 49 | |
63 | 50 | '<<SPEC' |
51 | ||
52 | (require equal?) |
1 | 1 | |
2 | 2 | ### `eval` ### |
3 | 3 | |
4 | -> Tests for functionality "Interpret core Robin Program" | |
4 | FIXME: These tests should ideally be for expressions in the Robin Expression Language | |
5 | rather than programs in the Robin Toplevel Language. | |
6 | ||
7 | -> Tests for functionality "Execute core Robin Toplevel Program" | |
5 | 8 | |
6 | 9 | `eval` evaluates its first argument to obtain an environment, then |
7 | 10 | evaluates its second argument to obtain an S-expression; it then |
98 | 101 | ? uncaught exception: (illegal-arguments (4 5 6)) |
99 | 102 | |
100 | 103 | '<<SPEC' |
104 | ||
105 | (require eval) |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Env)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Env)" | |
3 | 3 | |
4 | 4 | `export` treats its arguments as a list of identifiers, and returns an |
5 | 5 | environment where only those identifiers are bound to values. |
14 | 14 | Note: the order of the bindings in the binding alist isn't guaranteed; |
15 | 15 | thus these tests are written to search the resulting alist. |
16 | 16 | |
17 | | (display | |
18 | | (let ((a 1) (b 6)) | |
19 | | (length (export a b)))) | |
17 | | (let ((a 1) (b 6)) | |
18 | | (length (export a b))) | |
20 | 19 | = 2 |
21 | 20 | |
22 | | (display | |
23 | | (let ((a 1) (b 6)) | |
24 | | (lookup (literal a) (export a b)))) | |
21 | | (let ((a 1) (b 6)) | |
22 | | (lookup (literal a) (export a b))) | |
25 | 23 | = (1) |
26 | 24 | |
27 | | (display | |
28 | | (let ((a 1) (b 6)) | |
29 | | (lookup (literal b) (export a b)))) | |
25 | | (let ((a 1) (b 6)) | |
26 | | (lookup (literal b) (export a b))) | |
30 | 27 | = (6) |
31 | 28 | |
32 | | (display | |
33 | | (lookup (literal head) (export head tail))) | |
29 | | (lookup (literal head) (export head tail)) | |
34 | 30 | = (head) |
35 | 31 | |
36 | | (display | |
37 | | (lookup (literal prepend) (export head tail))) | |
32 | | (lookup (literal prepend) (export head tail)) | |
38 | 33 | = () |
39 | 34 | |
40 | 35 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with List)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with List)" | |
3 | 3 | |
4 | | (display | |
5 | | (extend (literal b) 6 (literal ((a 1) (b 2) (c 3))))) | |
4 | | (extend (literal b) 6 (literal ((a 1) (b 2) (c 3)))) | |
6 | 5 | = ((b 6) (a 1) (b 2) (c 3)) |
7 | 6 | |
8 | 7 | The following should be true for any identifier i and alist x. |
9 | 8 | |
10 | | (display | |
11 | | (let ((i (literal a)) | |
12 | | (x (literal ((f 5) (g 7))))) | |
13 | | (lookup i (extend i 1 x)))) | |
9 | | (let ((i (literal a)) | |
10 | | (x (literal ((f 5) (g 7))))) | |
11 | | (lookup i (extend i 1 x))) | |
14 | 12 | = (1) |
15 | 13 | |
16 | | (display | |
17 | | (extend (literal b) 6 ())) | |
14 | | (extend (literal b) 6 ()) | |
18 | 15 | = ((b 6)) |
19 | 16 | |
20 | | (display | |
21 | | (extend (literal b) 6 81)) | |
17 | | (extend (literal b) 6 81) | |
22 | 18 | ? uncaught exception: (expected-list 81) |
23 | 19 | |
24 | 20 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with List)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with List)" | |
3 | 3 | |
4 | 4 | `filter` evaluates its first argument to obtain a macro, generally assumed |
5 | 5 | to be a predicate (a one-argument function which evaluates to a boolean). |
7 | 7 | to a list which contains all the elements of the given list, in the same |
8 | 8 | order, which satisfy the predicate. |
9 | 9 | |
10 | | (display | |
11 | | (filter (fun (x) (symbol? x)) (literal (1 two #f 3 () four 5 six)))) | |
10 | | (filter (fun (x) (symbol? x)) (literal (1 two #f 3 () four 5 six))) | |
12 | 11 | = (two four six) |
13 | 12 | |
14 | | (display | |
15 | | (filter (fun (x) x) (literal (#t #t #f banana #t #f)))) | |
13 | | (filter (fun (x) x) (literal (#t #t #f banana #t #f))) | |
16 | 14 | ? uncaught exception: (expected-boolean banana) |
17 | 15 | |
18 | 16 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with List)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with List)" | |
3 | 3 | |
4 | 4 | `find` evaluates its first argument to obtain a predicate, then evaluates |
5 | 5 | its second argument to obtain a list. It then evaluates to a list which |
7 | 7 | a list which contains exactly one element, which will be the first |
8 | 8 | element from the list which satisfies the predicate. |
9 | 9 | |
10 | | (display | |
11 | | (find (fun (x) (symbol? x)) ())) | |
10 | | (find (fun (x) (symbol? x)) ()) | |
12 | 11 | = () |
13 | 12 | |
14 | | (display | |
15 | | (find (fun (x) (symbol? x)) (list 1 2 3))) | |
13 | | (find (fun (x) (symbol? x)) (list 1 2 3)) | |
16 | 14 | = () |
17 | 15 | |
18 | | (display | |
19 | | (find (fun (x) #t) (list 1 2 3))) | |
16 | | (find (fun (x) #t) (list 1 2 3)) | |
20 | 17 | = (1) |
21 | 18 | |
22 | | (display | |
23 | | (find (fun (x) (symbol? x)) (literal (1 two #f 3 () four 5 six)))) | |
19 | | (find (fun (x) (symbol? x)) (literal (1 two #f 3 () four 5 six))) | |
24 | 20 | = (two) |
25 | 21 | |
26 | 22 | `find` could be defined in terms of `filter`, but in practice it would |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with List)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with List)" | |
3 | 3 | |
4 | 4 | `first` evaluates its first argument to obtain a non-negative integer, |
5 | 5 | considered to be a desired length, and its second argument to obtain a list. |
6 | 6 | It then evaluates to the prefix of the given list of the desired length. |
7 | 7 | |
8 | | (display | |
9 | | (first 0 (list 1 2 3 4 5))) | |
8 | | (first 0 (list 1 2 3 4 5)) | |
10 | 9 | = () |
11 | 10 | |
12 | | (display | |
13 | | (first 3 (list 1 2 3 4 5))) | |
11 | | (first 3 (list 1 2 3 4 5)) | |
14 | 12 | = (1 2 3) |
15 | 13 | |
16 | | (display | |
17 | | (first 6 (list 1 2 3 4 5))) | |
14 | | (first 6 (list 1 2 3 4 5)) | |
18 | 15 | ? uncaught exception: (expected-list ()) |
19 | 16 | |
20 | | (display | |
21 | | (first 1 (literal foo))) | |
17 | | (first 1 (literal foo)) | |
22 | 18 | ? uncaught exception: (expected-list foo) |
23 | 19 | |
24 | | (display | |
25 | | (first 0 (literal foo))) | |
20 | | (first 0 (literal foo)) | |
26 | 21 | = () |
27 | 22 | |
28 | 23 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with List)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with List)" | |
3 | 3 | |
4 | 4 | `flatten` evaluates its first argument to obtain a list, then evaluates |
5 | 5 | to the list obtained by interpolating all elements into a single list. |
9 | 9 | is applied recursively to any elements in sublists which are themselves |
10 | 10 | sublists. |
11 | 11 | |
12 | | (display | |
13 | | (flatten ())) | |
12 | | (flatten ()) | |
14 | 13 | = () |
15 | 14 | |
16 | | (display | |
17 | | (flatten (list 1 2 3))) | |
15 | | (flatten (list 1 2 3)) | |
18 | 16 | = (1 2 3) |
19 | 17 | |
20 | | (display | |
21 | | (flatten (list 1 (list 2 3 4) 5))) | |
18 | | (flatten (list 1 (list 2 3 4) 5)) | |
22 | 19 | = (1 2 3 4 5) |
23 | 20 | |
24 | | (display | |
25 | | (flatten (list 1 (list 2 3 (list 4 4 4)) 5))) | |
21 | | (flatten (list 1 (list 2 3 (list 4 4 4)) 5)) | |
26 | 22 | = (1 2 3 4 4 4 5) |
27 | 23 | |
28 | | (display | |
29 | | (flatten (list 1 () 5))) | |
24 | | (flatten (list 1 () 5)) | |
30 | 25 | = (1 5) |
31 | 26 | |
32 | 27 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with List)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with List)" | |
3 | 3 | |
4 | 4 | `fold` evaluates its first argument to obtain a macro, generally assumed to |
5 | 5 | be a two-argument function, its second argument to obtain an initial value, |
11 | 11 | the second argument. `fold` evaluates to the result of the the final |
12 | 12 | application of the function. |
13 | 13 | |
14 | | (display | |
15 | | (fold (fun (x a) x) () (literal (three dog night)))) | |
14 | | (fold (fun (x a) x) () (literal (three dog night))) | |
16 | 15 | = night |
17 | 16 | |
18 | | (display | |
19 | | (fold (fun (x a) a) 541 (literal (archie moffam)))) | |
17 | | (fold (fun (x a) a) 541 (literal (archie moffam))) | |
20 | 18 | = 541 |
21 | 19 | |
22 | | (display | |
23 | | (fold (fun (x a) (list a x)) () (literal (three dog night)))) | |
20 | | (fold (fun (x a) (list a x)) () (literal (three dog night))) | |
24 | 21 | = (((() three) dog) night) |
25 | 22 | |
26 | | (display | |
27 | | (fold 99 (fun (x a) a) (literal (three dog night)))) | |
23 | | (fold 99 (fun (x a) a) (literal (three dog night))) | |
28 | 24 | ? uncaught exception: (inapplicable-object 99) |
29 | 25 | |
30 | 26 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Small)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Small)" | |
3 | 3 | |
4 | 4 | You can define functions with `fun`. They can be anonymous. |
5 | 5 | |
6 | | (display | |
7 | | ((fun (a) a) (literal whee))) | |
6 | | ((fun (a) a) (literal whee)) | |
8 | 7 | = whee |
9 | 8 | |
10 | 9 | Function have "closure" behavior; that is, bindings in force when a |
11 | 10 | function is defined will still be in force when the function is applied, |
12 | 11 | even if they are no longer lexically in scope. |
13 | 12 | |
14 | | (display | |
15 | 13 | | ((let |
16 | 14 | | ((a (literal (hi))) |
17 | 15 | | (f (fun (x) (list x a)))) |
18 | | f) (literal oh))) | |
16 | | f) (literal oh)) | |
19 | 17 | = (oh (hi)) |
20 | 18 | |
21 | 19 | Functions can take functions. |
22 | 20 | |
23 | | (display | |
24 | 21 | | (let |
25 | 22 | | ((apply (fun (x) (x (literal a))))) |
26 | | (apply (fun (r) (list r))))) | |
23 | | (apply (fun (r) (list r)))) | |
27 | 24 | = (a) |
28 | 25 | |
29 | 26 | Functions can return functions. |
30 | 27 | |
31 | | (display | |
32 | 28 | | (let |
33 | 29 | | ((mk (fun (x) (fun (y) (prepend y x)))) |
34 | 30 | | (mk2 (mk (literal (vindaloo))))) |
35 | | (mk2 (literal chicken)))) | |
31 | | (mk2 (literal chicken))) | |
36 | 32 | = (chicken vindaloo) |
37 | 33 | |
38 | 34 | Arguments to functions shadow any other bindings in effect. |
39 | 35 | |
40 | | (display | |
41 | 36 | | (let |
42 | 37 | | ((a (literal a)) |
43 | 38 | | (b (fun (a) (list a a)))) |
44 | | (b 7))) | |
39 | | (b 7)) | |
45 | 40 | = (7 7) |
46 | 41 | |
47 | 42 | A function may have no arguments at all. |
48 | 43 | |
49 | | (display | |
50 | | ((fun () 7))) | |
44 | | ((fun () 7)) | |
51 | 45 | = 7 |
52 | 46 | |
53 | 47 | But, a function must have exactly both a body and a list of formal arguments. |
54 | 48 | Otherwise, an exception will be raised. |
55 | 49 | |
56 | | (display | |
57 | | ((fun ()))) | |
50 | | ((fun ())) | |
58 | 51 | ? uncaught exception |
59 | 52 | |
60 | | (display | |
61 | | ((fun))) | |
53 | | ((fun)) | |
62 | 54 | ? uncaught exception |
63 | 55 | |
64 | | (display | |
65 | | ((fun (a) a a))) | |
56 | | ((fun (a) a a)) | |
66 | 57 | ? uncaught exception |
67 | 58 | |
68 | 59 | An `illegal-arguments` exception will be raised if not enough arguments are |
69 | 60 | supplied to a function call. |
70 | 61 | |
71 | | (display | |
72 | 62 | | ((fun (a b) (list b a)) |
73 | | (prepend 1 ()))) | |
63 | | (prepend 1 ())) | |
74 | 64 | ? uncaught exception: (illegal-arguments |
75 | 65 | |
76 | 66 | An `illegal-arguments` exception will be raised if too many arguments are |
77 | 67 | supplied to a function call. |
78 | 68 | |
79 | | (display | |
80 | 69 | | ((fun (a b) (list b a)) |
81 | | 1 (prepend 2 ()) 3)) | |
70 | | 1 (prepend 2 ()) 3) | |
82 | 71 | ? uncaught exception: (illegal-arguments |
83 | 72 | |
84 | 73 | `fun` is basically equivalent to Scheme's `lambda`. |
1 | 1 | |
2 | 2 | ### `head` ### |
3 | 3 | |
4 | -> Tests for functionality "Interpret core Robin Program" | |
4 | -> Tests for functionality "Evaluate core Robin Expression" | |
5 | 5 | |
6 | 6 | `head` evaluates its argument to a list, and evaluates to the first element |
7 | 7 | of that list. |
8 | 8 | |
9 | | (display | |
10 | | (head (prepend #t ()))) | |
9 | | (head (prepend #t ())) | |
11 | 10 | = #t |
12 | 11 | |
13 | 12 | `head` expects its argument to be a list. |
14 | 13 | |
15 | | (display | |
16 | | (head #f)) | |
14 | | (head #f) | |
17 | 15 | ? uncaught exception: (expected-list #f) |
18 | 16 | |
19 | 17 | `head` expects exactly one argument. |
20 | 18 | |
21 | | (display | |
22 | | (head (@prepend #t ()) (@prepend #f ()))) | |
23 | ? uncaught exception: (illegal-arguments ((@prepend #t ()) (@prepend #f ()))) | |
19 | | (head (prepend #t ()) (prepend #f ())) | |
20 | ? uncaught exception: (illegal-arguments ((prepend #t ()) (prepend #f ()))) | |
24 | 21 | |
25 | | (display | |
26 | | (head)) | |
22 | | (head) | |
27 | 23 | ? uncaught exception: (illegal-arguments ()) |
28 | 24 | |
29 | 25 | `head` is basically equivalent to Scheme's `car`. |
30 | 26 | |
31 | 27 | '<<SPEC' |
28 | ||
29 | (require head) |
1 | 1 | |
2 | 2 | ### `if` ### |
3 | 3 | |
4 | -> Tests for functionality "Interpret core Robin Program" | |
4 | -> Tests for functionality "Evaluate core Robin Expression" | |
5 | 5 | |
6 | 6 | `if` evaluates its first argument to a boolean value. If that value is |
7 | 7 | `#t`, it evaluates, and evaluates to, its second argument; or if that value |
8 | 8 | is `#f` it evaluates, and evaluates to, its third argument. In all cases, |
9 | 9 | at most two arguments are evaluated. |
10 | 10 | |
11 | | (display | |
12 | | (if #t 7 9)) | |
11 | | (if #t 7 9) | |
13 | 12 | = 7 |
14 | 13 | |
15 | | (display | |
16 | | (if #f 7 9)) | |
14 | | (if #f 7 9) | |
17 | 15 | = 9 |
18 | 16 | |
19 | 17 | The identifiers named in the branch which is not evaluated need not be |
20 | 18 | properly bound to values in the environment. |
21 | 19 | |
22 | | (display | |
23 | | (if #t 1 (prepend fred ethel))) | |
20 | | (if #t 1 (prepend fred ethel)) | |
24 | 21 | = 1 |
25 | 22 | |
26 | 23 | The second and third arguments can be arbitrary expressions, but `if` |
27 | 24 | expects its first argument to be a boolean. |
28 | 25 | |
29 | | (display | |
30 | | (if 5 7 9)) | |
26 | | (if 5 7 9) | |
31 | 27 | ? uncaught exception: (expected-boolean 5) |
32 | 28 | |
33 | 29 | `if` expects exactly three arguments. |
34 | 30 | |
35 | | (display | |
36 | | (if #t 7)) | |
31 | | (if #t 7) | |
37 | 32 | ? uncaught exception: (illegal-arguments (#t 7)) |
38 | 33 | |
39 | | (display | |
40 | | (if #t 7 8 9)) | |
34 | | (if #t 7 8 9) | |
41 | 35 | ? uncaught exception: (illegal-arguments (#t 7 8 9)) |
42 | 36 | |
43 | 37 | `if` is basically equivalent to Scheme's `if`. |
44 | 38 | |
45 | 39 | '<<SPEC' |
40 | ||
41 | (require if) |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with List)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with List)" | |
3 | 3 | |
4 | 4 | `index` evaluates its first argument to a natural number, and its |
5 | 5 | second argument to a list. It then evaluates to the element of the |
6 | 6 | list at the index given by the natural number. The index is 0-based; |
7 | 7 | 0 refers to the element at the head of the list. |
8 | 8 | |
9 | | (display | |
10 | | (index 0 (literal (the girl from ipanema)))) | |
9 | | (index 0 (literal (the girl from ipanema))) | |
11 | 10 | = the |
12 | 11 | |
13 | | (display | |
14 | | (index 2 (literal (the girl from ipanema)))) | |
12 | | (index 2 (literal (the girl from ipanema))) | |
15 | 13 | = from |
16 | 14 | |
17 | | (display | |
18 | 15 | | (bind last (fun (li) (index (subtract (length li) 1) li)) |
19 | | (last (literal (the girl from ipanema))))) | |
16 | | (last (literal (the girl from ipanema)))) | |
20 | 17 | = ipanema |
21 | 18 | |
22 | 19 | Attempting to index beyond the end of the list will raise an exception. |
23 | 20 | |
24 | | (display | |
25 | | (index 7 (literal (the girl from ipanema)))) | |
21 | | (index 7 (literal (the girl from ipanema))) | |
26 | 22 | ? uncaught exception: (expected-list ()) |
27 | 23 | |
28 | 24 | `index` expects its first argument to be a number. |
29 | 25 | |
30 | | (display | |
31 | | (index (literal goofy) (list 1 2 3 4 5))) | |
26 | | (index (literal goofy) (list 1 2 3 4 5)) | |
32 | 27 | ? uncaught exception: (expected-number goofy) |
33 | 28 | |
34 | 29 | `index` expects its second argument to be a list. |
35 | 30 | |
36 | | (display | |
37 | | (index 8 (literal whatnot))) | |
31 | | (index 8 (literal whatnot)) | |
38 | 32 | ? uncaught exception: (expected-list whatnot) |
39 | 33 | |
40 | 34 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Stdlib)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Stdlib)" | |
3 | 3 | |
4 | 4 | `itoa` evaluates its sole argument to an integer, then evaluates to |
5 | 5 | a string representing that integer in decimal. |
6 | 6 | |
7 | | (display | |
8 | | (itoa 100)) | |
7 | | (itoa 100) | |
9 | 8 | = (49 48 48) |
10 | 9 | |
11 | | (display | |
12 | | (itoa 99)) | |
10 | | (itoa 99) | |
13 | 11 | = (57 57) |
14 | 12 | |
15 | | (display | |
16 | | (itoa 0)) | |
13 | | (itoa 0) | |
17 | 14 | = (48) |
18 | 15 | |
19 | | (display | |
20 | | (itoa (subtract 0 1))) | |
16 | | (itoa (subtract 0 1)) | |
21 | 17 | = (45 49) |
22 | 18 | |
23 | | (display | |
24 | | (itoa (subtract 0 765))) | |
19 | | (itoa (subtract 0 765)) | |
25 | 20 | = (45 55 54 53) |
26 | 21 | |
27 | | (display | |
28 | | (itoa (literal m))) | |
22 | | (itoa (literal m)) | |
29 | 23 | ? uncaught exception: (expected-number m) |
30 | 24 | |
31 | 25 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with List)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with List)" | |
3 | 3 | |
4 | 4 | `last` evaluates its first argument to obtain a non-negative integer, |
5 | 5 | considered to be a desired length, and its second argument to obtain a |
6 | 6 | list. It then evaluates to the suffix of the given list of the desired |
7 | 7 | length. |
8 | 8 | |
9 | | (display | |
10 | | (last 0 (list 1 2 3 4 5))) | |
9 | | (last 0 (list 1 2 3 4 5)) | |
11 | 10 | = () |
12 | 11 | |
13 | | (display | |
14 | | (head (last 1 (list 1 2 3 4 5)))) | |
12 | | (head (last 1 (list 1 2 3 4 5))) | |
15 | 13 | = 5 |
16 | 14 | |
17 | | (display | |
18 | | (last 3 (list 1 2 3 4 5))) | |
15 | | (last 3 (list 1 2 3 4 5)) | |
19 | 16 | = (3 4 5) |
20 | 17 | |
21 | | (display | |
22 | | (last 6 (list 1 2 3 4 5))) | |
18 | | (last 6 (list 1 2 3 4 5)) | |
23 | 19 | ? uncaught exception: (expected-list ()) |
24 | 20 | |
25 | | (display | |
26 | | (last 1 (literal foo))) | |
21 | | (last 1 (literal foo)) | |
27 | 22 | ? uncaught exception: (expected-list foo) |
28 | 23 | |
29 | 24 | Unlike `first`, `last` does care if it's not a list, even when the count |
30 | 25 | is zero. |
31 | 26 | |
32 | | (display | |
33 | 27 | | (last 0 (literal foo))) |
34 | 28 | ? uncaught exception: (expected-list foo) |
35 | 29 |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with List)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with List)" | |
3 | 3 | |
4 | 4 | `length` evaluates its single argument to obtain a proper list, then |
5 | 5 | evaluates to a non-negative integer which is the length of the list |
6 | 6 | (the number of cells, not counting nested cells and not counting the |
7 | 7 | empty list at the very tail.) |
8 | 8 | |
9 | | (display | |
10 | | (length ())) | |
9 | | (length ()) | |
11 | 10 | = 0 |
12 | 11 | |
13 | | (display | |
14 | | (length (list 1 2 #t #f 3))) | |
12 | | (length (list 1 2 #t #f 3)) | |
15 | 13 | = 5 |
16 | 14 | |
17 | | (display | |
18 | | (length (literal whatnot))) | |
15 | | (length (literal whatnot)) | |
19 | 16 | ? uncaught exception: (expected-list whatnot) |
20 | 17 | |
21 | 18 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Small)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Small)" | |
3 | 3 | |
4 | 4 | `let` lets you bind multiple identifiers to multiple values. |
5 | 5 | |
6 | 6 | An identifier can be bound to a symbol. |
7 | 7 | |
8 | | (display | |
9 | | (let ((a (literal hello))) a)) | |
8 | | (let ((a (literal hello))) a) | |
10 | 9 | = hello |
11 | 10 | |
12 | 11 | `let` can appear in the binding expression in a `let`. |
13 | 12 | |
14 | | (display | |
15 | | (let ((a (let ((b (literal c))) b))) a)) | |
13 | | (let ((a (let ((b (literal c))) b))) a) | |
16 | 14 | = c |
17 | 15 | |
18 | 16 | `let` can bind a symbol to a macro. |
19 | 17 | |
20 | | (display | |
21 | | (let ((a (macro (self args env) | |
22 | | (let ((x (eval env (head args))) | |
23 | | (y (eval env (head (tail args))))) | |
24 | | (prepend y x))))) | |
25 | | (a () (literal foo)))) | |
18 | | (let ((a (macro (self args env) | |
19 | | (let ((x (eval env (head args))) | |
20 | | (y (eval env (head (tail args))))) | |
21 | | (prepend y x))))) | |
22 | | (a () (literal foo))) | |
26 | 23 | = (foo) |
27 | 24 | |
28 | 25 | Bindings established in a `let` remain in effect when evaluating |
29 | 26 | the arguments things in the body of the `let`. |
30 | 27 | |
31 | | (display | |
32 | | (let ((dup (macro (self args env) | |
33 | | (bind x (eval env (head args)) | |
34 | | (list x x))))) | |
35 | | (dup (dup (literal g))))) | |
28 | | (let ((dup (macro (self args env) | |
29 | | (bind x (eval env (head args)) | |
30 | | (list x x))))) | |
31 | | (dup (dup (literal g)))) | |
36 | 32 | = ((g g) (g g)) |
37 | 33 | |
38 | 34 | Bindings established in a binding in a `let` can be seen in |
39 | 35 | subsequent bindings in the same `let`. |
40 | 36 | |
41 | | (display | |
42 | | (let ((a (literal hello)) (b (list a))) b)) | |
37 | | (let ((a (literal hello)) (b (list a))) b) | |
43 | 38 | = (hello) |
44 | 39 | |
45 | 40 | Shadowing happens. |
46 | 41 | |
47 | | (display | |
48 | | (let ((a (literal hello))) (let ((a (literal goodbye))) a))) | |
42 | | (let ((a (literal hello))) (let ((a (literal goodbye))) a)) | |
49 | 43 | = goodbye |
50 | 44 | |
51 | 45 | `let` can have an empty list of bindings. |
52 | 46 | |
53 | | (display | |
54 | | (let () (literal hi))) | |
47 | | (let () (literal hi)) | |
55 | 48 | = hi |
56 | 49 | |
57 | 50 | The list of bindings must be a list, or else an exception will be raised. |
58 | 51 | |
59 | | (display | |
60 | | (let 999 (literal hi))) | |
52 | | (let 999 (literal hi)) | |
61 | 53 | ? uncaught exception |
62 | 54 | |
63 | 55 | Each binding in a list must be a list, or else an exception will be raised. |
64 | 56 | |
65 | | (display | |
66 | | (let (999) (literal hi))) | |
57 | | (let (999) (literal hi)) | |
67 | 58 | ? uncaught exception |
68 | 59 | |
69 | 60 | Both the body and the list of bindings are required, or else an exception |
70 | 61 | will be raised. |
71 | 62 | |
72 | | (display | |
73 | | (let ())) | |
63 | | (let ())) | |
74 | 64 | ? uncaught exception |
75 | 65 | |
76 | | (display | |
77 | | (let)) | |
66 | | (let) | |
78 | 67 | ? uncaught exception |
79 | 68 | |
80 | 69 | Any arguments given beyond the body and list of bindings will be ignored |
81 | 70 | and discarded, without being evaluated. |
82 | 71 | |
83 | | (display | |
84 | | (let ((a 1)) a foo)) | |
72 | | (let ((a 1)) a foo) | |
85 | 73 | = 1 |
86 | 74 | |
87 | 75 | Each binding must have at least a name and a value, or else an exception |
88 | 76 | will be raised. |
89 | ||
90 | | (display | |
91 | | (let ((a)) a)) | |
77 | ||
78 | | (let ((a)) a) | |
92 | 79 | ? uncaught exception |
93 | 80 | |
94 | | (display | |
95 | | (let (()) 7)) | |
81 | | (let (()) 7) | |
96 | 82 | ? uncaught exception |
97 | 83 | |
98 | 84 | Anything given in a binding beyond the name and the value will simply be |
99 | 85 | ignored and discarded, without being evaluated or otherwise examined. |
100 | 86 | |
101 | | (display | |
102 | | (let ((a 1 foo)) a)) | |
87 | | (let ((a 1 foo)) a) | |
103 | 88 | = 1 |
104 | 89 | |
105 | 90 | The identifier in a binding must be a symbol. |
106 | 91 | |
107 | | (display | |
108 | | (let ((3 1)) 3)) | |
92 | | (let ((3 1)) 3) | |
109 | 93 | ? uncaught exception: (illegal-binding (3 1)) |
110 | 94 | |
111 | 95 | `let` is basically equivalent to Scheme's `let*` or Haskell's `let`. |
1 | 1 | |
2 | 2 | ### `list?` ### |
3 | 3 | |
4 | -> Tests for functionality "Interpret core Robin Program" | |
4 | -> Tests for functionality "Evaluate core Robin Expression" | |
5 | 5 | |
6 | 6 | `list?` evaluates its argument, then evaluates to `#t` if it is a list, |
7 | 7 | `#f` otherwise. |
8 | 8 | |
9 | | (define literal (macro (s a e) (head a))) | |
10 | | (display | |
11 | | (list? (literal (a b)))) | |
9 | | (list? ((macro (s a e) (head a)) (a b))) | |
12 | 10 | = #t |
13 | 11 | |
14 | | (define literal (macro (s a e) (head a))) | |
15 | | (display | |
16 | | (list? (literal (a b c d e f)))) | |
12 | | (list? ((macro (s a e) (head a)) (a b c d e f))) | |
17 | 13 | = #t |
18 | 14 | |
19 | | (display | |
20 | | (list? (prepend 4 (prepend 5 ())))) | |
15 | | (list? (prepend 4 (prepend 5 ()))) | |
21 | 16 | = #t |
22 | 17 | |
23 | 18 | The empty list is a list. |
24 | 19 | |
25 | | (display | |
26 | | (list? ())) | |
20 | | (list? ()) | |
27 | 21 | = #t |
28 | 22 | |
29 | 23 | Symbols are not lists. |
30 | 24 | |
31 | | (define literal (macro (s a e) (head a))) | |
32 | | (display | |
33 | | (list? (literal a))) | |
25 | | (list? ((macro (s a e) (head a)) a)) | |
34 | 26 | = #f |
35 | 27 | |
36 | 28 | The argument to `list?` may (naturally) be any type, but there must be |
37 | 29 | exactly one argument. |
38 | 30 | |
39 | | (display | |
40 | | (list? (prepend 4 ()) (prepend 6 ()))) | |
31 | | (list? (prepend 4 ()) (prepend 6 ())) | |
41 | 32 | ? uncaught exception: (illegal-arguments ((prepend 4 ()) (prepend 6 ()))) |
42 | 33 | |
43 | | (display | |
44 | | (list?)) | |
34 | | (list?) | |
45 | 35 | ? uncaught exception: (illegal-arguments ()) |
46 | 36 | |
47 | 37 | '<<SPEC' |
38 | ||
39 | (require list?) |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Small)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Small)" | |
3 | 3 | |
4 | 4 | `list` is a macro which evaluates each of its arguments, and evaluates to a |
5 | 5 | (proper) list containing each of the results, in the same order. |
6 | 6 | |
7 | | (display | |
8 | | (list 1 2 3 4 5)) | |
7 | | (list 1 2 3 4 5) | |
9 | 8 | = (1 2 3 4 5) |
10 | 9 | |
11 | | (display | |
12 | | (list (list 2 3) (list 6 7))) | |
10 | | (list (list 2 3) (list 6 7)) | |
13 | 11 | = ((2 3) (6 7)) |
14 | 12 | |
15 | 13 | `list` need not have any arguments at all; the result is the empty list. |
16 | 14 | |
17 | | (display | |
18 | | (list)) | |
15 | | (list) | |
19 | 16 | = () |
20 | 17 | |
21 | 18 | Unlike `literal`, `list` does evaluate its arguments, all of them. |
22 | 19 | |
23 | | (display | |
24 | | (list (literal x) (literal y))) | |
20 | | (list (literal x) (literal y)) | |
25 | 21 | = (x y) |
26 | ||
27 | `list` does not require any arguments. | |
28 | ||
29 | | (display | |
30 | | (list)) | |
31 | = () | |
32 | 22 | |
33 | 23 | '<<SPEC' |
34 | 24 |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Small)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Small)" | |
3 | 3 | |
4 | 4 | One of the most basic identifiers available in `small` is `literal`, |
5 | 5 | which evaluates to the literal content of its sole argument, which can be |
6 | 6 | any S-expression. |
7 | 7 | |
8 | | (display | |
9 | | (literal symbol)) | |
8 | | (literal symbol)) | |
10 | 9 | = symbol |
11 | 10 | |
12 | | (display | |
13 | | (literal (hello (there) world))) | |
11 | | (literal (hello (there) world))) | |
14 | 12 | = (hello (there) world) |
15 | 13 | |
16 | 14 | `literal` requires at least one argument; otherwise, an exception will |
17 | 15 | be raised. |
18 | 16 | |
19 | | (display | |
20 | | (literal)) | |
17 | | (literal) | |
21 | 18 | ? uncaught exception |
22 | ||
23 | TODO Unlike other things in `stdlib`, this does not use builtin wrappers | |
24 | 19 | |
25 | 20 | Any arguments beyond the first argument are simply ignored and discarded. |
26 | 21 | |
27 | | (display | |
28 | | (literal a b c)) | |
22 | | (literal a b c)) | |
29 | 23 | = a |
30 | 24 | |
31 | 25 | `literal` is basically equivalent to Scheme's `quote`. |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with List)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with List)" | |
3 | 3 | |
4 | | (display | |
5 | | (lookup (literal b) (literal ((a 1) (b 2) (c 3))))) | |
4 | | (lookup (literal b) (literal ((a 1) (b 2) (c 3)))) | |
6 | 5 | = (2) |
7 | 6 | |
8 | | (display | |
9 | | (lookup (literal a) (literal ((a 1) (a 2) (a 3))))) | |
7 | | (lookup (literal a) (literal ((a 1) (a 2) (a 3)))) | |
10 | 8 | = (1) |
11 | 9 | |
12 | | (display | |
13 | | (lookup (literal r) (literal ((a 1) (b 2) (c 3))))) | |
10 | | (lookup (literal r) (literal ((a 1) (b 2) (c 3)))) | |
14 | 11 | = () |
15 | 12 | |
16 | | (display | |
17 | | (lookup (literal q) ())) | |
13 | | (lookup (literal q) ()) | |
18 | 14 | = () |
19 | 15 | |
20 | | (display | |
21 | | (lookup (literal q) 55)) | |
16 | | (lookup (literal q) 55) | |
22 | 17 | ? uncaught exception: (expected-list 55) |
23 | 18 | |
24 | | (display | |
25 | | (lookup (literal q) (literal ((a 7) 99 (q 4))))) | |
19 | | (lookup (literal q) (literal ((a 7) 99 (q 4)))) | |
26 | 20 | ? uncaught exception: (expected-list 99) |
27 | 21 | |
28 | 22 | '<<SPEC' |
1 | 1 | |
2 | 2 | ### `macro?` ### |
3 | 3 | |
4 | -> Tests for functionality "Interpret core Robin Program" | |
4 | -> Tests for functionality "Evaluate core Robin Expression" | |
5 | 5 | |
6 | 6 | `macro?` evaluates its argument, then evaluates to `#t` if it is a macro, |
7 | 7 | or `#f` if it is not. |
8 | 8 | |
9 | | (display | |
10 | | (macro? (macro (self args env) args))) | |
9 | | (macro? (macro (self args env) args)) | |
11 | 10 | = #t |
12 | 11 | |
13 | TODO: this should probably be false. Intrinsics are slightly different | |
14 | from macros. Either that, or, it should be, like `applyable?`, or | |
15 | something. | |
12 | Intrinsic macros are macros. | |
16 | 13 | |
17 | | (display | |
18 | | (macro? macro)) | |
14 | | (macro? macro) | |
19 | 15 | = #t |
20 | 16 | |
21 | | (display | |
22 | | (macro? ((macro (self args env) (head args)) macro))) | |
17 | Literal symbols are not macros, even if they're the name of one. | |
18 | ||
19 | | (macro? ((macro (self args env) (head args)) macro)) | |
23 | 20 | = #f |
24 | 21 | |
25 | | (display | |
26 | | (macro? 5)) | |
22 | Numbers are not macros. | |
23 | ||
24 | | (macro? 5) | |
27 | 25 | = #f |
28 | 26 | |
29 | 27 | The argument to `macro?` may (naturally) be any type, but there must be |
30 | 28 | exactly one argument. |
31 | 29 | |
32 | | (display | |
33 | | (macro? @macro @macro)) | |
34 | ? uncaught exception: (illegal-arguments (@macro @macro)) | |
30 | | (macro? macro macro) | |
31 | ? uncaught exception: (illegal-arguments (macro macro)) | |
35 | 32 | |
36 | | (display | |
37 | | (macro?)) | |
33 | | (macro?) | |
38 | 34 | ? uncaught exception: (illegal-arguments ()) |
39 | 35 | |
40 | 36 | '<<SPEC' |
37 | ||
38 | (require macro?) |
1 | 1 | |
2 | 2 | ### `macro` ### |
3 | 3 | |
4 | -> Tests for functionality "Interpret core Robin Program" | |
4 | FIXME: These tests should ideally be for expressions in the Robin Expression Language | |
5 | rather than programs in the Robin Toplevel Language. | |
6 | ||
7 | -> Tests for functionality "Execute core Robin Toplevel Program" | |
5 | 8 | |
6 | 9 | `macro` takes its first argument to be a list of three formal |
7 | 10 | parameters, and its second argument to be an arbitrary expression, |
204 | 207 | ? uncaught exception: (illegal-arguments ((self args 99) prepend)) |
205 | 208 | |
206 | 209 | '<<SPEC' |
210 | ||
211 | (require macro) |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with List)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with List)" | |
3 | 3 | |
4 | 4 | `map` evaluates its first argument to obtain a macro, and its second argument |
5 | 5 | to obtain a list. It then evaluates to a list which is obtained by applying |
6 | 6 | the macro to each element of the given list. The macro is generally assumed |
7 | 7 | to be a one-argument function. |
8 | 8 | |
9 | | (display | |
10 | | (map (fun (x) (list x)) (literal (three dog night)))) | |
9 | | (map (fun (x) (list x)) (literal (three dog night))) | |
11 | 10 | = ((three) (dog) (night)) |
12 | 11 | |
13 | 12 | While it is possible to pass a macro that is not a function, it is not |
14 | 13 | very productive. (Also, it exposes the implementation of `map`, so this |
15 | 14 | is not a very good test.) |
16 | 15 | |
17 | | (display | |
18 | | (map (macro (self args env) args) (literal (three dog night)))) | |
16 | | (map (macro (self args env) args) (literal (three dog night))) | |
19 | 17 | = (((head li)) ((head li)) ((head li))) |
20 | 18 | |
21 | 19 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Arith)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Arith)" | |
3 | 3 | |
4 | 4 | `multiply` evaluates both of its arguments to numbers and evaluates to the product |
5 | 5 | of those two numbers. |
6 | 6 | |
7 | | (display | |
8 | | (multiply 6 7)) | |
7 | | (multiply 6 7) | |
9 | 8 | = 42 |
10 | 9 | |
11 | | (display | |
12 | | (multiply (subtract 0 6) 7)) | |
10 | | (multiply (subtract 0 6) 7) | |
13 | 11 | = -42 |
14 | 12 | |
15 | | (display | |
16 | | (multiply 6 (subtract 0 7))) | |
13 | | (multiply 6 (subtract 0 7)) | |
17 | 14 | = -42 |
18 | 15 | |
19 | | (display | |
20 | | (multiply (subtract 0 6) (subtract 0 7))) | |
16 | | (multiply (subtract 0 6) (subtract 0 7)) | |
21 | 17 | = -42 |
22 | 18 | |
23 | 19 | `multiply` expects exactly two arguments. |
24 | 20 | |
25 | | (display | |
26 | | (multiply 14)) | |
21 | | (multiply 14) | |
27 | 22 | ? uncaught exception: (illegal-arguments (14)) |
28 | 23 | |
29 | | (display | |
30 | | (multiply 6 7 7)) | |
24 | | (multiply 6 7 7) | |
31 | 25 | ? uncaught exception: (illegal-arguments (6 7 7)) |
32 | 26 | |
33 | 27 | Both of the arguments to `multiply` must be numbers. |
34 | 28 | |
35 | | (display | |
36 | | (multiply 14 #t)) | |
29 | | (multiply 14 #t) | |
37 | 30 | ? uncaught exception: (expected-number #t) |
38 | 31 | |
39 | | (display | |
40 | | (multiply #t 51)) | |
32 | | (multiply #t 51) | |
41 | 33 | ? uncaught exception: (expected-number #t) |
42 | 34 | |
43 | 35 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Boolean)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Boolean)" | |
3 | 3 | |
4 | 4 | `not` evaluates its single argument to a boolean, then evaluates to |
5 | 5 | the logical negation of that boolean. |
6 | 6 | |
7 | | (display | |
8 | | (not #t)) | |
7 | | (not #t) | |
9 | 8 | = #f |
10 | 9 | |
11 | | (display | |
12 | | (not #f)) | |
10 | | (not #f) | |
13 | 11 | = #t |
14 | 12 | |
15 | 13 | `not` expects exactly one argument. |
16 | 14 | |
17 | | (display | |
18 | | (not)) | |
15 | | (not) | |
19 | 16 | ? uncaught exception: (illegal-arguments ()) |
20 | 17 | |
21 | | (display | |
22 | | (not #t #f)) | |
18 | | (not #t #f) | |
23 | 19 | ? uncaught exception: (illegal-arguments (#t #f)) |
24 | 20 | |
25 | 21 | `not` expects its single argument to be a boolean. |
26 | 22 | |
27 | | (display | |
28 | | (not 33)) | |
23 | | (not 33) | |
29 | 24 | ? uncaught exception: (expected-boolean 33) |
30 | 25 | |
31 | 26 | '<<SPEC' |
1 | 1 | |
2 | 2 | ### `number?` ### |
3 | 3 | |
4 | -> Tests for functionality "Interpret core Robin Program" | |
4 | -> Tests for functionality "Evaluate core Robin Expression" | |
5 | 5 | |
6 | 6 | `number?` evaluates its argument, then evaluates to `#t` if it is a |
7 | 7 | number, `#f` otherwise. |
8 | 8 | |
9 | | (display | |
10 | | (number? 7)) | |
9 | | (number? 7) | |
11 | 10 | = #t |
12 | 11 | |
13 | | (display | |
14 | | (number? 0)) | |
12 | | (number? 0) | |
15 | 13 | = #t |
16 | 14 | |
17 | | (display | |
18 | | (number? ())) | |
15 | | (number? ()) | |
19 | 16 | = #f |
20 | 17 | |
21 | | (display | |
22 | | (number? #t)) | |
18 | | (number? #t) | |
23 | 19 | = #f |
24 | 20 | |
25 | | (define literal (macro (s a e) (head a))) | |
26 | | (display | |
27 | | (number? (literal seven))) | |
21 | | (number? ((macro (s a e) (head a)) seven)) | |
28 | 22 | = #f |
29 | 23 | |
30 | 24 | That's a good question... |
31 | 25 | |
32 | | (define literal (macro (s a e) (head a))) | |
33 | | (display | |
34 | | (number? (literal 7))) | |
26 | | (number? ((macro (s a e) (head a)) 7)) | |
35 | 27 | = #t |
36 | 28 | |
37 | 29 | The argument to `number?` may (naturally) be any type, but there must be |
38 | 30 | exactly one argument. |
39 | 31 | |
40 | | (display | |
41 | | (number? 6 4)) | |
32 | | (number? 6 4) | |
42 | 33 | ? uncaught exception: (illegal-arguments (6 4)) |
43 | 34 | |
44 | | (display | |
45 | | (number?)) | |
35 | | (number?) | |
46 | 36 | ? uncaught exception: (illegal-arguments ()) |
47 | 37 | |
48 | 38 | '<<SPEC' |
39 | ||
40 | (require number?) |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Boolean)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Boolean)" | |
3 | 3 | |
4 | 4 | `or` evaluates both of its arguments to booleans, and evaluates to the |
5 | 5 | logical disjunction (boolean "or") of these two values. |
6 | 6 | |
7 | | (display | |
8 | | (or #t #t)) | |
7 | | (or #t #t) | |
9 | 8 | = #t |
10 | 9 | |
11 | | (display | |
12 | | (or #t #f)) | |
10 | | (or #t #f) | |
13 | 11 | = #t |
14 | 12 | |
15 | | (display | |
16 | | (or #f #t)) | |
13 | | (or #f #t) | |
17 | 14 | = #t |
18 | 15 | |
19 | | (display | |
20 | | (or #f #f)) | |
16 | | (or #f #f) | |
21 | 17 | = #f |
22 | 18 | |
23 | 19 | `or` expects exactly two arguments. |
24 | 20 | |
25 | (Hate to weaken this test, but I'm not a purist -- yet.) | |
26 | ||
27 | | (display | |
28 | | (or #f)) | |
21 | | (or #f) | |
29 | 22 | ? uncaught exception |
30 | 23 | |
31 | | (display | |
32 | | (or #t #f #f)) | |
24 | | (or #t #f #f) | |
33 | 25 | ? uncaught exception: (illegal-arguments (#t #f #f)) |
34 | 26 | |
35 | 27 | `or` expects both of its arguments to be booleans. |
36 | 28 | |
37 | | (display | |
38 | | (or 100 #f)) | |
29 | | (or 100 #f) | |
39 | 30 | ? uncaught exception: (expected-boolean 100) |
40 | 31 | |
41 | | (display | |
42 | | (or #f 99)) | |
32 | | (or #f 99) | |
43 | 33 | ? uncaught exception: (expected-boolean 99) |
44 | 34 | |
45 | 35 | `or` is short-circuiting in the sense that no arguments after the first |
46 | 36 | `#t` argument will be evaluated. Fully testing this requires side-effects, |
47 | 37 | but it can be demonstrated as follows. |
48 | 38 | |
49 | | (display | |
50 | | (or #t 100)) | |
39 | | (or #t 100) | |
51 | 40 | = #t |
52 | 41 | |
53 | 42 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with List)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with List)" | |
3 | 3 | |
4 | 4 | `prefix?` evaluates its first and second arguments to obtain lists. |
5 | 5 | It then evaluates to `#t` if the first list is a prefix of the second |
7 | 7 | or the head of A is `equal?` to the head of B and the tail of A is a |
8 | 8 | prefix of the tail of B. |
9 | 9 | |
10 | | (display | |
11 | | (prefix? (list 1 2 3) (list 1 2 3 4 5 6))) | |
10 | | (prefix? (list 1 2 3) (list 1 2 3 4 5 6)) | |
12 | 11 | = #t |
13 | 12 | |
14 | | (display | |
15 | | (prefix? (list 1 2 5) (list 1 2 3 4 5 6))) | |
13 | | (prefix? (list 1 2 5) (list 1 2 3 4 5 6)) | |
16 | 14 | = #f |
17 | 15 | |
18 | | (display | |
19 | | (prefix? () (list 1 2 3 4 5 6))) | |
16 | | (prefix? () (list 1 2 3 4 5 6)) | |
20 | 17 | = #t |
21 | 18 | |
22 | | (display | |
23 | | (prefix? () (literal schpritz))) | |
19 | | (prefix? () (literal schpritz)) | |
24 | 20 | = #t |
25 | 21 | |
26 | | (display | |
27 | | (prefix? (list 1 2 3) (list 1 2 3))) | |
22 | | (prefix? (list 1 2 3) (list 1 2 3)) | |
28 | 23 | = #t |
29 | 24 | |
30 | | (display | |
31 | | (prefix? (list 1 2 3 4) (list 1 2 3))) | |
25 | | (prefix? (list 1 2 3 4) (list 1 2 3)) | |
32 | 26 | = #f |
33 | 27 | |
34 | 28 | '<<SPEC' |
1 | 1 | |
2 | 2 | ### `prepend` ### |
3 | 3 | |
4 | -> Tests for functionality "Interpret core Robin Program" | |
4 | -> Tests for functionality "Evaluate core Robin Expression" | |
5 | 5 | |
6 | 6 | `prepend` evaluates both of its arguments, then evaluates to a list cell |
7 | 7 | which contains the first value as its data and the second value as the |
8 | 8 | continuation of the list. |
9 | 9 | |
10 | | (display | |
11 | | (prepend () ())) | |
10 | | (prepend () ()) | |
12 | 11 | = (()) |
13 | 12 | |
14 | | (display | |
15 | | (prepend #t (prepend #f ()))) | |
13 | | (prepend #t (prepend #f ())) | |
16 | 14 | = (#t #f) |
17 | 15 | |
18 | 16 | The second argument to `prepend` must be a list. |
19 | 17 | |
20 | | (display | |
21 | | (prepend #t #f)) | |
18 | | (prepend #t #f) | |
22 | 19 | ? uncaught exception: (expected-list #f) |
23 | 20 | |
24 | 21 | The first argument to `prepend` can be any type, but fewer than or more than |
25 | 22 | two arguments will raise an exception. |
26 | 23 | |
27 | | (display | |
28 | | (prepend #t)) | |
24 | | (prepend #t) | |
29 | 25 | ? uncaught exception: (illegal-arguments (#t)) |
30 | 26 | |
31 | | (display | |
32 | | (prepend #f #t #f)) | |
27 | | (prepend #f #t #f) | |
33 | 28 | ? uncaught exception: (illegal-arguments (#f #t #f)) |
34 | 29 | |
35 | 30 | `prepend` is basically equivalent to Scheme's `cons`, except for the |
36 | 31 | requirement that the second argument be a list. |
37 | 32 | |
38 | 33 | '<<SPEC' |
34 | ||
35 | (require prepend) |
1 | 1 | |
2 | 2 | ### `raise` ### |
3 | 3 | |
4 | -> Tests for functionality "Interpret core Robin Program" | |
4 | -> Tests for functionality "Evaluate core Robin Expression" | |
5 | 5 | |
6 | 6 | `raise` evaluates its argument to obtain a value, then raises an |
7 | 7 | exception with that value. |
13 | 13 | refers to the exception that triggered it, but this is not a strict |
14 | 14 | requirement. |
15 | 15 | |
16 | | (display | |
17 | | (raise 999999)) | |
16 | | (raise 999999) | |
18 | 17 | ? uncaught exception: 999999 |
19 | 18 | |
20 | 19 | `raise`'s single argument may be any kind of value, but `raise` expects |
21 | 20 | exactly one argument. |
22 | 21 | |
23 | | (display | |
24 | | (raise)) | |
22 | | (raise) | |
25 | 23 | ? uncaught exception: (illegal-arguments ()) |
26 | 24 | |
27 | | (display | |
28 | | (raise 2 3 4)) | |
25 | | (raise 2 3 4) | |
29 | 26 | ? uncaught exception: (illegal-arguments (2 3 4)) |
30 | 27 | |
31 | 28 | A Robin environment may install exception handlers which are not defined |
33 | 30 | strict requirement.) |
34 | 31 | |
35 | 32 | '<<SPEC' |
33 | ||
34 | (require raise) |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Arith)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Arith)" | |
3 | 3 | |
4 | 4 | `remainder` evaluates both of its arguments to numbers and evaluates to the |
5 | 5 | remainder of the division of the first number by the second. |
6 | 6 | |
7 | | (display | |
8 | | (remainder 12 3)) | |
7 | | (remainder 12 3) | |
9 | 8 | = 0 |
10 | 9 | |
11 | | (display | |
12 | | (remainder 11 3)) | |
10 | | (remainder 11 3) | |
13 | 11 | = 2 |
14 | 12 | |
15 | | (display | |
16 | | (remainder 10 3)) | |
13 | | (remainder 10 3) | |
17 | 14 | = 1 |
18 | 15 | |
19 | | (display | |
20 | | (remainder 9 3)) | |
16 | | (remainder 9 3) | |
21 | 17 | = 0 |
22 | 18 | |
23 | The remainder is *always positive*. | |
19 | The remainder is *always non-negative*. | |
24 | 20 | |
25 | | (display | |
26 | | (remainder (subtract 0 10) 3)) | |
21 | | (remainder (subtract 0 10) 3) | |
27 | 22 | = 2 |
28 | 23 | |
29 | | (display | |
30 | | (remainder 10 (subtract 0 3))) | |
24 | | (remainder 10 (subtract 0 3)) | |
31 | 25 | = 2 |
32 | 26 | |
33 | 27 | Trying to find the remainder of a division by zero is undefined, and an |
34 | 28 | exception will be raised. |
35 | 29 | |
36 | | (display | |
37 | | (remainder 10 0)) | |
30 | | (remainder 10 0) | |
38 | 31 | ? uncaught exception: (division-by-zero 10) |
39 | 32 | |
40 | 33 | `remainder` expects exactly two arguments, both numbers. |
41 | 34 | |
42 | | (display | |
43 | | (remainder 14)) | |
35 | | (remainder 14) | |
44 | 36 | ? uncaught exception: (illegal-arguments (14)) |
45 | 37 | |
46 | | (display | |
47 | | (remainder 14 23 57)) | |
38 | | (remainder 14 23 57) | |
48 | 39 | ? uncaught exception: (illegal-arguments (14 23 57)) |
49 | 40 | |
50 | | (display | |
51 | | (remainder 14 #t)) | |
41 | | (remainder 14 #t) | |
52 | 42 | ? uncaught exception: (expected-number #t) |
53 | 43 | |
54 | | (display | |
55 | | (remainder #t 51)) | |
44 | | (remainder #t 51) | |
56 | 45 | ? uncaught exception: (expected-number #t) |
57 | 46 | |
58 | 47 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with List)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with List)" | |
3 | 3 | |
4 | 4 | `rest` evaluates its first argument to obtain a non-negative integer, |
5 | 5 | considered to be a desired position, and its second argument to obtain a |
6 | 6 | list. It then evaluates to the suffix of the given list starting at the |
7 | 7 | desired position. The position 0 indicates the beginning of the list. |
8 | 8 | |
9 | | (display | |
10 | | (rest 0 (list 1 2 3 4 5))) | |
9 | | (rest 0 (list 1 2 3 4 5)) | |
11 | 10 | = (1 2 3 4 5) |
12 | 11 | |
13 | | (display | |
14 | | (rest 3 (list 1 2 3 4 5))) | |
12 | | (rest 3 (list 1 2 3 4 5)) | |
15 | 13 | = (4 5) |
16 | 14 | |
17 | | (display | |
18 | | (rest 5 (list 1 2 3 4 5))) | |
15 | | (rest 5 (list 1 2 3 4 5)) | |
19 | 16 | = () |
20 | 17 | |
21 | | (display | |
22 | | (rest 6 (list 1 2 3 4 5))) | |
18 | | (rest 6 (list 1 2 3 4 5)) | |
23 | 19 | ? uncaught exception: (expected-list ()) |
24 | 20 | |
25 | | (display | |
26 | | (rest 1 (literal foo))) | |
21 | | (rest 1 (literal foo)) | |
27 | 22 | ? uncaught exception: (expected-list foo) |
28 | 23 | |
29 | | (display | |
30 | | (rest 0 (literal foo))) | |
24 | | (rest 0 (literal foo)) | |
31 | 25 | = foo |
32 | 26 | |
33 | 27 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with List)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with List)" | |
3 | 3 | |
4 | 4 | `reverse` evaluates its argument to a list, then evaluates to a list which |
5 | 5 | is the same as the given list in every respect except that the order of |
6 | 6 | the elements is reversed. |
7 | 7 | |
8 | | (display | |
9 | | (reverse (literal (1 2 3 4 5)))) | |
8 | | (reverse (literal (1 2 3 4 5))) | |
10 | 9 | = (5 4 3 2 1) |
11 | 10 | |
12 | | (display | |
13 | | (reverse (literal fairies-wear-boots))) | |
11 | | (reverse (literal fairies-wear-boots)) | |
14 | 12 | ? uncaught exception: (expected-list fairies-wear-boots) |
15 | 13 | |
16 | 14 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Env)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Env)" | |
3 | 3 | |
4 | 4 | `sandbox` takes a list of identifiers as its first argument, and evaluates |
5 | 5 | its second argument in an environment where all bindings *except* those |
6 | 6 | for the listed identifiers have been unbound. |
7 | 7 | |
8 | | (display | |
9 | | (sandbox (prepend tail) | |
10 | | (tail (prepend 8 (prepend 9 ()))))) | |
8 | | (sandbox (prepend tail) | |
9 | | (tail (prepend 8 (prepend 9 ())))) | |
11 | 10 | = (9) |
12 | 11 | |
13 | | (display | |
14 | | (sandbox (prepend tail) | |
15 | | (head (prepend 8 (prepend 9 ()))))) | |
12 | | (sandbox (prepend tail) | |
13 | | (head (prepend 8 (prepend 9 ())))) | |
16 | 14 | ? uncaught exception: (unbound-identifier head) |
17 | 15 | |
18 | 16 | '<<SPEC' |
1 | 1 | |
2 | 2 | ### `sign` ### |
3 | 3 | |
4 | -> Tests for functionality "Interpret core Robin Program" | |
4 | -> Tests for functionality "Evaluate core Robin Expression" | |
5 | 5 | |
6 | 6 | `sign` evaluates its sole argument to a number, then |
7 | 7 | evaluates to 0 if that number is 0, 1 if that number is positive, or |
8 | 8 | -1 if that number is negative. |
9 | 9 | |
10 | | (display | |
11 | | (sign 26)) | |
10 | | (sign 26) | |
12 | 11 | = 1 |
13 | 12 | |
14 | | (display | |
15 | | (sign 0)) | |
13 | | (sign 0) | |
16 | 14 | = 0 |
17 | 15 | |
18 | | (display | |
19 | | (sign (subtract 0 200))) | |
16 | | (sign (subtract 0 200)) | |
20 | 17 | = -1 |
21 | 18 | |
22 | 19 | `sign` expects a number. |
23 | 20 | |
24 | | (display | |
25 | | (sign #f)) | |
21 | | (sign #f) | |
26 | 22 | ? uncaught exception: (expected-number #f) |
27 | 23 | |
28 | | (define literal (macro (s a e) (head a))) | |
29 | | (display | |
30 | | (sign (literal k))) | |
24 | | (sign ((macro (s a e) (head a)) k)) | |
31 | 25 | ? uncaught exception: (expected-number k) |
32 | 26 | |
33 | 27 | `sign` expects exactly one argument. |
34 | 28 | |
35 | | (display | |
36 | | (sign 100 200 300)) | |
29 | | (sign 100 200 300) | |
37 | 30 | ? uncaught exception: (illegal-arguments (100 200 300)) |
38 | 31 | |
39 | | (display | |
40 | | (sign)) | |
32 | | (sign) | |
41 | 33 | ? uncaught exception: (illegal-arguments ()) |
42 | 34 | |
43 | 35 | '<<SPEC' |
36 | ||
37 | (require sign) |
1 | 1 | |
2 | 2 | ### `subtract` ### |
3 | 3 | |
4 | -> Tests for functionality "Interpret core Robin Program" | |
4 | -> Tests for functionality "Evaluate core Robin Expression" | |
5 | 5 | |
6 | 6 | `subtract` evaluates its first argument to a number, then |
7 | 7 | evaluates its second argument to a number, then evaluates |
8 | 8 | to the difference between the first and second numbers. |
9 | 9 | |
10 | | (display | |
11 | | (subtract 6 4)) | |
10 | | (subtract 6 4) | |
12 | 11 | = 2 |
13 | 12 | |
14 | | (display | |
15 | | (subtract 1000 8000)) | |
13 | | (subtract 1000 8000) | |
16 | 14 | = -7000 |
17 | 15 | |
18 | 16 | Addition may be accomplished by negating the second argument. |
19 | 17 | |
20 | | (display | |
21 | | (subtract 999 (subtract 0 999))) | |
18 | | (subtract 999 (subtract 0 999)) | |
22 | 19 | = 1998 |
23 | 20 | |
24 | 21 | `subtract` expects both of its arguments to be numbers. |
25 | 22 | |
26 | | (display | |
27 | | (subtract #f 100)) | |
23 | | (subtract #f 100) | |
28 | 24 | ? uncaught exception: (expected-number #f) |
29 | 25 | |
30 | | (display | |
31 | | (subtract 100 ())) | |
26 | | (subtract 100 ()) | |
32 | 27 | ? uncaught exception: (expected-number ()) |
33 | 28 | |
34 | 29 | `subtract` expects exactly two arguments. |
35 | 30 | |
36 | | (display | |
37 | | (subtract 100 200 300)) | |
31 | | (subtract 100 200 300) | |
38 | 32 | ? uncaught exception: (illegal-arguments (100 200 300)) |
39 | 33 | |
40 | | (display | |
41 | | (subtract)) | |
34 | | (subtract) | |
42 | 35 | ? uncaught exception: (illegal-arguments ()) |
43 | 36 | |
44 | 37 | '<<SPEC' |
38 | ||
39 | (require subtract) |
1 | 1 | |
2 | 2 | ### `symbol?` ### |
3 | 3 | |
4 | -> Tests for functionality "Interpret core Robin Program" | |
4 | -> Tests for functionality "Evaluate core Robin Expression" | |
5 | 5 | |
6 | 6 | `symbol?` evaluates its argument, then evaluates to `#t` if it is a symbol, |
7 | 7 | `#f` otherwise. |
8 | 8 | |
9 | | (define literal (macro (s a e) (head a))) | |
10 | | (display | |
11 | | (symbol? (literal this-symbol))) | |
9 | | (symbol? ((macro (s a e) (head a)) this-symbol)) | |
12 | 10 | = #t |
13 | 11 | |
14 | 12 | Numbers are not symbols. |
15 | 13 | |
16 | | (display | |
17 | | (symbol? 9)) | |
14 | | (symbol? 9) | |
18 | 15 | = #f |
19 | 16 | |
20 | 17 | Lists are not symbols. |
21 | 18 | |
22 | | (display | |
23 | | (symbol? (prepend 1 ()))) | |
19 | | (symbol? (prepend 1 ())) | |
24 | 20 | = #f |
25 | 21 | |
26 | 22 | The argument to `symbol?` may (naturally) be any type, but there must be |
27 | 23 | exactly one argument. |
28 | 24 | |
29 | | (display | |
30 | | (symbol? 77 88)) | |
25 | | (symbol? 77 88) | |
31 | 26 | ? uncaught exception: (illegal-arguments (77 88)) |
32 | 27 | |
33 | | (display | |
34 | | (symbol?)) | |
28 | | (symbol?) | |
35 | 29 | ? uncaught exception: (illegal-arguments ()) |
36 | 30 | |
37 | 31 | '<<SPEC' |
32 | ||
33 | (require symbol?) |
1 | 1 | |
2 | 2 | ### `tail` ### |
3 | 3 | |
4 | -> Tests for functionality "Interpret core Robin Program" | |
4 | -> Tests for functionality "Evaluate core Robin Expression" | |
5 | 5 | |
6 | 6 | `tail` evaluates its argument to a list, and evaluates to the tail of that |
7 | 7 | list (the sublist obtained by removing the first element.) |
8 | 8 | |
9 | | (display | |
10 | | (tail (prepend #t (prepend #f ())))) | |
9 | | (tail (prepend #t (prepend #f ()))) | |
11 | 10 | = (#f) |
12 | 11 | |
13 | 12 | `tail` expects its argument to be a list. |
14 | 13 | |
15 | | (display | |
16 | | (tail #f)) | |
14 | | (tail #f) | |
17 | 15 | ? uncaught exception: (expected-list #f) |
18 | 16 | |
19 | 17 | `tail` expects exactly one argument. |
20 | 18 | |
21 | | (display | |
22 | | (tail (@prepend #t ()) (@prepend #f ()))) | |
23 | ? uncaught exception: (illegal-arguments ((@prepend #t ()) (@prepend #f ()))) | |
19 | | (tail (prepend #t ()) (prepend #f ())) | |
20 | ? uncaught exception: (illegal-arguments ((prepend #t ()) (prepend #f ()))) | |
24 | 21 | |
25 | | (display | |
26 | | (tail)) | |
22 | | (tail) | |
27 | 23 | ? uncaught exception: (illegal-arguments ()) |
28 | 24 | |
29 | 25 | `tail` is basically equivalent to Scheme's `cdr`. |
30 | 26 | |
31 | 27 | '<<SPEC' |
28 | ||
29 | (require tail) |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with List)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with List)" | |
3 | 3 | |
4 | 4 | `take-while` evaluates its first argument to obtain a predicate and its |
5 | 5 | second argument to obtain a list. It then evaluates to the longest prefix |
6 | 6 | of the list whose elements all satisfy the predicate. |
7 | 7 | |
8 | | (display | |
9 | | (take-while (fun (x) (symbol? x)) (literal (one two 3 4 five 6 seven)))) | |
8 | | (take-while (fun (x) (symbol? x)) (literal (one two 3 4 five 6 seven))) | |
10 | 9 | = (one two) |
11 | 10 | |
12 | | (display | |
13 | | (take-while (fun (x) (symbol? x)) (literal (1 2 3 4 five six)))) | |
11 | | (take-while (fun (x) (symbol? x)) (literal (1 2 3 4 five six))) | |
14 | 12 | = () |
15 | 13 | |
16 | | (display | |
17 | | (take-while (fun (x) (number? x)) (literal (1 2 3 4 5 6)))) | |
14 | | (take-while (fun (x) (number? x)) (literal (1 2 3 4 5 6))) | |
18 | 15 | = (1 2 3 4 5 6) |
19 | 16 | |
20 | | (display | |
21 | | (take-while (fun (x) (symbol? x)) ())) | |
17 | | (take-while (fun (x) (symbol? x)) ()) | |
22 | 18 | = () |
23 | 19 | |
24 | | (display | |
25 | | (take-while (fun (x) (symbol? x)) #f)) | |
20 | | (take-while (fun (x) (symbol? x)) #f) | |
26 | 21 | ? uncaught exception: (expected-list #f) |
27 | 22 | |
28 | 23 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Env)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Env)" | |
3 | 3 | |
4 | 4 | `unbind` removes the given identifier from the environment and evaluates its |
5 | 5 | second argument in that reduced environment. |
6 | 6 | |
7 | | (display | |
8 | | (unbind if (if #t (literal x) (literal y)))) | |
7 | | (unbind if (if #t (literal x) (literal y))) | |
9 | 8 | ? uncaught exception: (unbound-identifier if) |
10 | 9 | |
11 | 10 | If the identifier doesn't exist in the environment, no change is made to |
12 | 11 | the environment. |
13 | 12 | |
14 | | (display | |
15 | | (unbind yog-sothoth (if #t (literal x) (literal y)))) | |
13 | | (unbind yog-sothoth (if #t (literal x) (literal y))) | |
16 | 14 | = x |
17 | 15 | |
18 | 16 | `unbind` removes all trace of binding from the given identifier; if that |
19 | 17 | identifier has several definitions that are shadowed, none of them will be |
20 | 18 | in effect. |
21 | 19 | |
22 | | (display | |
23 | | (let ((x 7)) | |
24 | | (let ((x 8)) | |
25 | | (unbind x | |
26 | | x)))) | |
20 | | (let ((x 7)) | |
21 | | (let ((x 8)) | |
22 | | (unbind x | |
23 | | x))) | |
27 | 24 | ? uncaught exception: (unbound-identifier x) |
28 | 25 | |
29 | 26 | '<<SPEC' |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Env)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Env)" | |
3 | 3 | |
4 | 4 | `unshadow` is similar to `unbind`, but only removes the latest binding |
5 | 5 | for the given identifier; previously shadowed bindings, if any exist, |
6 | 6 | will be visible instead. |
7 | 7 | |
8 | | (display | |
9 | | (unshadow yog-sothoth (if #t (literal x) (literal y)))) | |
8 | | (unshadow yog-sothoth (if #t (literal x) (literal y))) | |
10 | 9 | = x |
11 | 10 | |
12 | | (display | |
13 | | (unshadow if (if #t (literal x) (literal y)))) | |
11 | | (unshadow if (if #t (literal x) (literal y))) | |
14 | 12 | ? uncaught exception: (unbound-identifier if) |
15 | 13 | |
16 | | (display | |
17 | | (bind if (literal what) | |
18 | | (unshadow if (if #t (literal x) (literal y))))) | |
14 | | (bind if (literal what) | |
15 | | (unshadow if (if #t (literal x) (literal y)))) | |
19 | 16 | = x |
20 | 17 | |
21 | | (display | |
18 | | (bind q 400 | |
19 | | (unshadow q q)) | |
20 | ? uncaught exception: (unbound-identifier q) | |
21 | ||
22 | | (bind q 200 | |
22 | 23 | | (bind q 400 |
23 | 24 | | (unshadow q q))) |
24 | ? uncaught exception: (unbound-identifier q) | |
25 | = 200 | |
25 | 26 | |
26 | | (display | |
27 | | (bind q 100 | |
27 | 28 | | (bind q 200 |
28 | 29 | | (bind q 400 |
29 | | (unshadow q q)))) | |
30 | = 200 | |
31 | ||
32 | | (display | |
33 | | (bind q 100 | |
34 | | (bind q 200 | |
35 | | (bind q 400 | |
36 | | (unshadow q (unshadow q q)))))) | |
30 | | (unshadow q (unshadow q q))))) | |
37 | 31 | = 100 |
38 | 32 | |
39 | | (display | |
40 | | (let ((q 100) | |
41 | | (q 200) | |
42 | | (q 400)) | |
43 | | (unshadow q (unshadow q q)))) | |
33 | | (let ((q 100) | |
34 | | (q 200) | |
35 | | (q 400)) | |
36 | | (unshadow q (unshadow q q))) | |
44 | 37 | = 100 |
45 | 38 | |
46 | 39 | `unshadow` is something of a gimmick that shows off Robin's ability |
0 | 0 | ;'<<SPEC' |
1 | 1 | |
2 | -> Tests for functionality "Interpret Robin Program (with Boolean)" | |
2 | -> Tests for functionality "Evaluate Robin Expression (with Boolean)" | |
3 | 3 | |
4 | 4 | `xor` evaluates both of its arguments to boolean, then evaluates to |
5 | 5 | the "exclusive-or" of those booleans. |
6 | 6 | |
7 | | (display | |
8 | | (xor #t #t)) | |
7 | | (xor #t #t) | |
9 | 8 | = #f |
10 | 9 | |
11 | | (display | |
12 | | (xor #t #f)) | |
10 | | (xor #t #f) | |
13 | 11 | = #t |
14 | 12 | |
15 | | (display | |
16 | | (xor #f #t)) | |
13 | | (xor #f #t) | |
17 | 14 | = #t |
18 | 15 | |
19 | | (display | |
20 | | (xor #f #f)) | |
16 | | (xor #f #f) | |
21 | 17 | = #f |
22 | 18 | |
23 | 19 | `xor` expects exactly two arguments. |
24 | 20 | |
25 | | (display | |
26 | | (xor #f)) | |
21 | | (xor #f) | |
27 | 22 | ? uncaught exception: (illegal-arguments (#f)) |
28 | 23 | |
29 | | (display | |
30 | | (xor #t #f #f)) | |
24 | | (xor #t #f #f) | |
31 | 25 | ? uncaught exception: (illegal-arguments (#t #f #f)) |
32 | 26 | |
33 | 27 | `xor` expects both of its arguments to be booleans. |
34 | 28 | |
35 | | (display | |
36 | | (xor 100 #t)) | |
29 | | (xor 100 #t) | |
37 | 30 | ? uncaught exception: (expected-boolean 100) |
38 | 31 | |
39 | | (display | |
40 | | (xor #t 99)) | |
32 | | (xor #t 99) | |
41 | 33 | ? uncaught exception: (expected-boolean 99) |
42 | 34 | |
43 | 35 | This test demonstrates that these functions really do evaluate their |
44 | 36 | arguments. |
45 | 37 | |
46 | | (display | |
47 | | (and (or (xor (and #t (not (not #t))) #f) #f) #t)) | |
38 | | (and (or (xor (and #t (not (not #t))) #f) #f) #t) | |
48 | 39 | = #t |
49 | 40 | |
50 | 41 | '<<SPEC' |