git @ Cat's Eye Technologies Robin / 0c1c668
Merge pull request #2 from catseye/develop-0.4 Develop 0.4 Chris Pressey authored 5 years ago GitHub committed 5 years ago
87 changed file(s) with 1535 addition(s) and 1400 deletion(s). Raw diff Collapse all Expand all
00 History of Robin
11 ================
22
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)
425 ---------
526
627 * The "intrinsics wrappers" were removed. Their semantics have been
5273 ---------
5374
5475 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
00 Robin
11 =====
22
3 _Version 0.3. Work-in-progress, subject to change._
3 _Version 0.4. Work-in-progress, subject to change._
44
55 Overview
66 --------
5656
5757 APPLIANCES="appliances/robin.md" ./test.sh
5858
59 There are also some QuickCheck tests which you can run with
60
61 ghc -isrc src/QuickCheckTests.hs -e testAll
62
5963 Extended Description
6064 --------------------
6165
6569 ### Scheme ###
6670
6771 Like [Scheme][], Robin is eagerly evaluated, latently typed, and homoiconic,
72 as well as properly tail-recursive and lexically scoped (at least by default),
6873 and tries hard to be well-defined and system-agnostic, but (as you can read
6974 below) diverges significantly from Scheme in other ways.
7075
7883
7984 ### PicoLisp ###
8085
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!
8489
8590 ### Haskell ###
8691
3333
3434 Rename "small" to "core" or "base" or something.
3535
36 `(bound? sym)` returns `#t` if the symbol is bound, `#f` if not.
37
3836 `(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.
4237
4338 Other libs
4439 ----------
5550 Note that this relies on the assumption that all standard symbols
5651 have their standard meanings.
5752
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
7053 Tests
7154 -----
7255
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)
7557
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.
7859
7960 Reactors
8061 --------
10283 Rename `fun` to `function`. This is because Robin prefers full words
10384 over abbreviations, which are jargon-y.
10485
86 Rename `>` to `greater-than?`, and so forth. Figure out a place to
87 put the aliases-which-are-punctuation-y.
88
10589 Rename `raise` to `throw`. This is because `throw` is the opposite
10690 of `catch`. Also "raise" suggests an error, but it might be merely a
10791 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
126 -> "bin/robin --no-builtins %(test-body-file)"
227
3 -> Functionality "Interpret Robin Program" is implemented by shell command
28 -> Functionality "Execute Robin Toplevel Program (with Small)" is implemented by shell command
429 -> "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
126 -> "bin/robin --no-builtins %(test-body-file)"
227
3 -> Functionality "Interpret Robin Program" is implemented by shell command
28 -> Functionality "Execute Robin Toplevel Program (with Small)" is implemented by shell command
429 -> "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)"
2828 stdlib/not.robin stdlib/and.robin stdlib/or.robin \
2929 stdlib/xor.robin > pkg/boolean.robin
3030
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 \
3232 stdlib/unbind.robin stdlib/unshadow.robin > pkg/env.robin
3333
3434 cat stdlib/itoa.robin > pkg/misc.robin
88 echo "ghc not found, not building $PROG.exe"
99 fi
1010
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
1126 if command -v hastec >/dev/null 2>&1; then
1227 echo "building $PROG.js with hastec"
1328 (cd src && hastec --make HasteMain.hs -o ../demo/$PROG.js) || exit 1
1429 else
1530 echo "hastec not found, not building $PROG.js"
1631 fi
32
33 ./build-packages.sh
180180
181181 <p><i>(alist)</i> lookup extend delete</p>
182182
183 <p><i>(env)</i> env? export sandbox unbind unshadow</p>
183 <p><i>(env)</i> env? bound? export sandbox unbind unshadow</p>
184184
185185 <p><i>(arith)</i> abs add &gt; &gt;= &lt; &lt;= multiply divide remainder</p>
186186
00 Robin
11 =====
22
3 This document defines version 0.3 of the Robin programming language.
3 This document defines version 0.4 of the Robin programming language.
44
55 The Robin specification is modular in the sense that it consists
66 of several smaller specifications, some of which depend on others,
77 that can be composed or used in isolation. These specifications are:
88
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
11731 --------------------
11832
33 -> Tests for functionality "Execute core Robin Toplevel Program"
34
11935 ### S-expressions ###
12036
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
122219 all the other data types. It is inductively defined as follows:
123220
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
137233 defined by Robin's evaluation rules, but that meaning might be to
138234 raise an exception to indicate an error.
139235
140236 ### Symbol ###
141237
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
143239 which may not include whitespace or parentheses or a few other
144240 characters (TODO: decide which ones) and which may not begin with
145241 a `#` (pound sign) or a few other characters (TODO: decide which
148244 When in a Robin program proper, a symbol can be bound to a value, and
149245 in this context is it referred to as an _identifier_. However, if an
150246 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)
158259 = hello
159260
160261 A Robin implementation is not expected to be able to generate new symbols
172273
173274 Booleans always evaluate to themselves.
174275
175 | (display #t)
276 | #t
176277 = #t
177278
178 | (display #f)
279 | #f
179280 = #f
180281
181282 Booleans cannot be applied.
182283
183 | (display (#t 1 2 3))
284 | (#t 1 2 3)
184285 ? uncaught exception: (inapplicable-object #t)
185286
186287 ### Integers ###
191292
192293 For example, 5 is an integer:
193294
194 | (display 5)
295 | 5
195296 = 5
196297
197298 Whereas 6167172726261721 is not, and you get the 32-bit signed integer
198299 equivalent:
199300
200 | (display 6167172726261721)
301 | 6167172726261721
201302 = -878835751
202303
203304 Integers always evaluate to themselves.
204305
205306 Integers cannot be applied.
206307
207 | (display (900 1 2 3))
308 | (900 1 2 3)
208309 ? uncaught exception: (inapplicable-object 900)
209310
210311 ### Macros ###
211312
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
213314 translate one S-expression to another.
214315
215316 One area where Robin diverges significantly from Lisp and Scheme is that,
245346 Macros are represented as the S-expression expansion of their
246347 implementation.
247348
248 | (display
249 | (macro (self args env) args))
349 | (macro (self args env) args)
250350 = (macro (self args env) args)
251351
252352 Macros can be applied, and that is the typical use of them.
253353
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
254432 ### 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.)
260433
261434 Robin 0.3 provides 15 intrinsics. These represent
262435 the fundamental functionality that is used to evaluate programs, and that
268441 provide them, or it's not Robin.
269442
270443 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
272445 environment in which they were called, they behave a lot like functions.
273446 But they are not obligated to; they might evaluate them in a modified
274447 environment, or not evaluate them at all and treat them as a literal
275448 S-expression.
276449
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
282456 = head
283457
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 ()))
289462 = (if head)
290463
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.
292466
293467 Each of the 15 intrinsics provided by Robin 0.3 is specified in
294468 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.
297471
298472 * [catch](../stdlib/catch.robin)
299473 * [equal?](../stdlib/equal-p.robin)
311485 * [symbol?](../stdlib/symbol-p.robin)
312486 * [tail](../stdlib/tail.robin)
313487
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"
522627
523628 To separate the concerns of computation and interaction, Robin provides
524629 a construct called a _reactor_. While evaluation of a Robin expression
573678 In fact, commands _are_ events. We just call them commands when it is
574679 a reactor producing them, and events when a reactor is receiving them.
575680
576 Standard Events
577 ---------------
578
579 ### `init` ###
681 ### Standard Events ###
682
683 #### `init` ####
580684
581685 When a reactor first starts up it will receive an event telling it that
582686 it has started up. The event type for this event is the literal symbol
595699 will receive a `not-available` event. It may then elect to abort,
596700 or choose an alternate facility, or so forth.
597701
598 Standard Commands
599 -----------------
600
601 ### `stop` ###
702 ### Standard Commands ###
703
704 #### `stop` ####
602705
603706 Stops the current reactor, and removes it from the list of active
604707 reactors. It will no longer receive any events.
605708
606 Standard Facilities
607 -------------------
709 ### Standard Facilities ###
608710
609711 If a reactor isn't subscribed to any facilities, it won't necessarily
610712 receive any events, although this is implementation-specific.
615717
616718 Let's describe one such facility for concreteness.
617719
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)"
621723
622724 The `line-terminal` facility allows a Robin program to interact with
623725 something or someone over a line-oriented protocol, similar to what
628730
629731 The `line-terminal` facility understands commands of the form
630732
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
634736 integers, as bytes, are sent to whetever is listening on the other end of
635737 the line terminal. When attached to an actual terminal console (whether real
636738 or emulated), this would typically cause an ASCII representation of those bytes
637739 to be displayed.
740
741 It also understands
742
743 (write STRING)
744
745 which will write the STRING but not terminate the line.
638746
639747 Knowing this, we can write a "Hello, world!" program. To keep it
640748 simple, we'll simply assume the line-terminal facility exists.
676784 = Cat
677785 = Dog
678786
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 ###
681805
682806 A reactor can issue multiple commands in its response to an event.
683807
0 (assert fun) ;''A way to signal that it requires stdlib.''
0 (require fun)
11
22 (reactor (line-terminal) (list 0 0) (macro (self args env)
33 (let ((event (head args))
0 (require bind)
1
02 (reactor (line-terminal) 0
13 (macro (self args env)
24 (bind event (head args)
00 ;''
1 Deadfish in Robin 0.3 -- requires stdlib
1 Deadfish in Robin 0.3
22 ''
3
4 (require itoa)
5
36 (reactor (line-terminal) 0
47 (macro (self args env)
58 (bind event (head args)
1821 (if show
1922 (list state
2023 (list (literal writeln) (itoa state))
21 (list (literal writeln) (literal ''>> '')))
24 (list (literal write) (literal ''>> '')))
2225 (list state
23 (list (literal writeln) (literal ''>> '')))))))
26 (list (literal write) (literal ''>> '')))))))
2427 (choose
2528 ((equal? event-type (literal init))
2629 (prompt #f state))
0 (require multiply)
1
02 (define fact (fun (self n)
13 (multiply n
24 (if (> n 1)
0 (require bind)
1 (require literal)
2
03 (reactor (line-terminal) 0
14 (macro (self args env)
25 (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))))))
44 import Haste.Events
55
66 import Language.Robin.Env (mergeEnvs)
7 import Language.Robin.Parser (parseRobin)
7 import Language.Robin.Parser (parseToplevel)
88 import Language.Robin.Intrinsics (robinIntrinsics)
99 import Language.Robin.Builtins (robinBuiltins)
1010 import qualified Language.Robin.TopLevel as TopLevel
1717 where
1818 execute = do
1919 Just program <- getValue progElem
20 case parseRobin program of
20 case parseToplevel program of
2121 Right topExprs -> do
2222 let env = (mergeEnvs robinIntrinsics robinBuiltins)
2323 let (env', reactors, results) = TopLevel.collect topExprs env [] []
1616 -- See the relevant files in `stdlib` for normative definitions.
1717 --
1818
19 --
20 -- Helper functions
21 --
22
1923 union (List []) env = env
2024 union (List (binding:rest)) env =
2125 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)
2626
2727 evalAll i env [] acc cc =
2828 cc $ List $ reverse acc
3030 eval i env head (\value ->
3131 evalAll i env tail (value:acc) cc)
3232
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
3353 robinList i env (List exprs) cc =
3454 evalAll i env exprs [] cc
3555
56 robinEnv :: Evaluable
3657 robinEnv i env (List _) cc =
3758 cc env
3859
60 choose :: Evaluable
3961 choose i env (List [(List [(Symbol "else"), branch])]) cc =
4062 eval i env branch cc
4163 choose i env (List ((List [test, branch]):rest)) cc =
4769 choose i env (List rest) cc)
4870 choose i env other cc = raise i (errMsg "illegal-arguments" other)
4971
72 bind :: Evaluable
5073 bind i env (List [name@(Symbol _), expr, body]) cc =
5174 eval i env expr (\value ->
5275 eval i (Env.insert name value env) body cc)
5376 bind i env other cc = raise i (errMsg "illegal-arguments" other)
5477
78 robinLet :: Evaluable
5579 robinLet i env (List ((List bindings):body:_)) cc =
5680 bindAll bindings env i (\env' ->
5781 eval i env' body cc)
6589 raise ienv (errMsg "illegal-binding" other)
6690 robinLet i env other cc = raise i (errMsg "illegal-arguments" other)
6791
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
7893 robinBindArgs i env (List [(List formals), givenArgs, givenEnv, body]) cc =
7994 eval i env givenArgs (\(List actuals) ->
8095 eval i env givenEnv (\outerEnv ->
8297 eval i (union argEnv env) body cc)))
8398 robinBindArgs i env other cc = raise i (errMsg "illegal-arguments" other)
8499
85 --
86 -- Implementation of `fun`.
87 --
88
100 robinFun :: Evaluable
89101 robinFun i closedEnv (List [(List formals), body]) cc =
90102 cc $ Intrinsic "<lambda>" fun
91103 where
106118 -- Mapping of names to our functions, providing an evaluation environment.
107119 --
108120
121 robinBuiltins :: Expr
109122 robinBuiltins = Env.fromList $ map (\(name,bif) -> (name, Intrinsic name bif))
110123 [
111124 ("literal", literal),
66 -- values (arbitrary S-expressions).
77 --
88
9 empty :: Expr
910 empty = List []
1011
12 insert :: Expr -> Expr -> Expr -> Expr
1113 insert s@(Symbol _) value env =
1214 append (List [List [s, value]]) env
1315
16 find :: Expr -> Expr -> Maybe Expr
1417 find s@(Symbol _) (List []) = Nothing
1518 find s@(Symbol _) (List (List [first, value]:rest))
1619 | s == first = Just value
1720 | otherwise = find s (List rest)
1821
22 fromList :: [(String,Expr)] -> Expr
1923 fromList [] =
2024 List []
2125 fromList ((id, val):xs) =
2226 append (List [List [(Symbol id), val]]) (fromList xs)
2327
28 mergeEnvs :: Expr -> Expr -> Expr
2429 mergeEnvs (List a) (List b) = List (a ++ b)
1010 --
1111 -- Expr -> Expr -> Expr -> (Expr -> Expr) -> Expr
1212 --
13 -- (This is actually the `Intrinsic` type from `Robin.Expr`.)
13 -- (This is actually the `Evaluable` type from `Robin.Expr`.)
1414 --
1515 -- The first argument is the internal context, which contains things like the
1616 -- exception handler, etc.
2222 -- value. Then continue the current continuation with that value.
2323 --
2424
25 eval :: Intrinsic
25 eval :: Evaluable
2626
2727 eval i (List []) s@(Symbol _) cc =
2828 raise i (errMsg "unbound-identifier" s)
33 import Data.Int
44
55 --
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.
89 --
910
10 type Intrinsic = IEnv Expr -> Expr -> Expr -> (Expr -> Expr) -> Expr
11 type Evaluable = IEnv Expr -> Expr -> Expr -> (Expr -> Expr) -> Expr
1112 -- internal-env env args continuation result
1213
1314 data Expr = Symbol String
1415 | Boolean Bool
1516 | Number Int32
1617 | Macro Expr Expr Expr
17 | Intrinsic String Intrinsic
18 | Intrinsic String Evaluable
1819 | List [Expr]
1920
2021 instance Eq Expr where
3435 show (Macro env args body) = ("(macro " ++ (show args) ++
3536 " " ++ (show body) ++ ")")
3637 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)
4242
4343 --
4444 -- Helpers
44 import Language.Robin.Eval
55
66
7 robinHead :: Evaluable
78 robinHead i env (List [expr]) cc =
89 eval i env expr (\x ->
910 assertList i x (\val ->
1213 other -> raise i (errMsg "expected-list" other)))
1314 robinHead i env other cc = raise i (errMsg "illegal-arguments" other)
1415
16 robinTail :: Evaluable
1517 robinTail i env (List [expr]) cc =
1618 eval i env expr (\x ->
1719 assertList i x (\val ->
2022 other -> raise i (errMsg "expected-list" other)))
2123 robinTail i env other cc = raise i (errMsg "illegal-arguments" other)
2224
25 robinPrepend :: Evaluable
2326 robinPrepend i env (List [e1, e2]) cc =
2427 eval i env e1 (\x1 -> eval i env e2 (\val ->
2528 case val of
2730 other -> raise i (errMsg "expected-list" other)))
2831 robinPrepend i env other cc = raise i (errMsg "illegal-arguments" other)
2932
33 equalP :: Evaluable
3034 equalP i env (List [e1, e2]) cc =
3135 eval i env e1 (\x1 -> eval i env e2 (\x2 -> cc $ Boolean (x1 == x2)))
3236 equalP i env other cc = raise i (errMsg "illegal-arguments" other)
4044 macroP = predP isMacro
4145 numberP = predP isNumber
4246
47 robinSubtract :: Evaluable
4348 robinSubtract i env (List [xexpr, yexpr]) cc =
4449 eval i env xexpr (\x ->
4550 assertNumber i x (\(Number xv) ->
4853 cc (Number (xv - yv))))))
4954 robinSubtract i env other cc = raise i (errMsg "illegal-arguments" other)
5055
56 robinSign :: Evaluable
5157 robinSign i env (List [expr]) cc =
5258 eval i env expr (\x ->
5359 assertNumber i x (\(Number xv) ->
5662 sign x = if x == 0 then 0 else if x < 0 then -1 else 1
5763 robinSign i env other cc = raise i (errMsg "illegal-arguments" other)
5864
65 robinIf :: Evaluable
5966 robinIf i env (List [test, texpr, fexpr]) cc =
6067 eval i env test (\x ->
6168 assertBoolean i x (\(Boolean b) ->
6471 False -> eval i env fexpr cc))
6572 robinIf i env other cc = raise i (errMsg "illegal-arguments" other)
6673
74 robinEval :: Evaluable
6775 robinEval i env (List [envlist, form]) cc =
6876 eval i env envlist (\newEnv ->
6977 eval i env form (\body ->
7078 eval i newEnv body cc))
7179 robinEval i env other cc = raise i (errMsg "illegal-arguments" other)
7280
81 robinMacro :: Evaluable
7382 robinMacro i env (List [args@(List [(Symbol selfS), (Symbol argsS), (Symbol envS)]), body]) cc = do
7483 cc $ Macro env args body
7584 robinMacro i env other cc = raise i (errMsg "illegal-arguments" other)
7685
86 robinRaise :: Evaluable
7787 robinRaise i env (List [expr]) cc =
7888 eval i env expr (\v -> raise i v)
7989 robinRaise i env other cc = raise i (errMsg "illegal-arguments" other)
8090
91 robinCatch :: Evaluable
8192 robinCatch i env (List [id@(Symbol _), handler, body]) cc =
8293 let
8394 handlerContinuation = (\errvalue ->
8798 eval i' env body cc
8899 robinCatch i env other cc = raise i (errMsg "illegal-arguments" other)
89100
101 robinIntrinsics :: Expr
90102 robinIntrinsics = Env.fromList $ map (\(name,bif) -> (name, Intrinsic name bif))
91103 [
92104 ("head", robinHead),
00 {-# LANGUAGE FlexibleContexts #-}
11
2 module Language.Robin.Parser (parseRobin, insistParse) where
2 module Language.Robin.Parser (parseToplevel, parseExpr) where
33
44 import Data.Char
55 import Data.Int
7878 expr
7979
8080 --
81 -- The top-level parsing function implements the overall grammar given above.
81 -- The expression parsing function implements the overall grammar given above.
8282 -- Note that we need to give the type of this parser here -- otherwise the
8383 -- type inferencer freaks out for some reason.
8484 --
8585
8686 expr :: Parser Expr
8787 expr = do
88 spaces
8889 r <- (symbol <|> number <|> boolean <|> list <|> stringSugar)
8990 spaces
9091 many comment
9192 return r
9293
93 robinProgram = do
94 toplevel = do
9495 spaces
9596 many comment
9697 e <- many expr
9798 return $ e
9899
99 -- Convenience functions for parsing Robin programs.
100 -- Convenience functions for parsing Robin forms.
100101
101 parseRobin = parse robinProgram ""
102 parseToplevel :: String -> Either ParseError [Expr]
103 parseToplevel = parse toplevel ""
102104
103 insistParse programText =
104 let
105 Right ast = parseRobin programText
106 in
107 ast
105 parseExpr :: String -> Either ParseError Expr
106 parseExpr = parse expr ""
22 import qualified Data.Char as Char
33 import Data.Int
44 import System.IO
5 import System.Random
56
67 import Language.Robin.Expr
78 import Language.Robin.Eval
6768
6869 eventLoop showEvents reactors (event@(List [eventType, eventPayload]):events) = do
6970 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)
7375
7476 eventLoop showEvents reactors (event:events) = do
7577 showEvent showEvents event
9496 let payload = List (map (\x -> Number (fromIntegral $ Char.ord x)) inpStr)
9597 return $ List [(Symbol "readln"), payload]
9698
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 []
97105 handleLineTerminalEvent (List [Symbol "writeln", payload]) = do
98106 let List l = payload
99107 let s = map (\(Number x) -> Char.chr $ fromIntegral $ x) l
100108 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 []
44 import Language.Robin.Eval
55 import Language.Robin.Reactor
66
7
8 collect :: [Expr] -> Expr -> [Reactor] -> [Either Expr Expr] -> (Expr, [Reactor], [Either Expr Expr])
79
810 collect [] env reactors results = (env, reactors, results)
911
2224 case eval (IEnv stop) env expr id of
2325 Boolean False ->
2426 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 ++ ")")
2534 _ ->
2635 collect rest env reactors results
2736
44 import System.Environment
55 import System.Exit
66
7 import Language.Robin.Expr
78 import Language.Robin.Env (mergeEnvs)
8 import Language.Robin.Parser (parseRobin)
9 import Language.Robin.Parser (parseToplevel, parseExpr)
910 import Language.Robin.Intrinsics (robinIntrinsics)
1011 import Language.Robin.Builtins (robinBuiltins)
1112 import qualified Language.Robin.TopLevel as TopLevel
1617 args <- getArgs
1718 case args of
1819 [] -> do
19 abortWith "Usage: robin [--no-builtins] [--show-events] {source.robin}"
20 abortWith "Usage: robin [--no-builtins] [--show-events] {[eval] source.robin}"
2021 _ -> do
2122 let (args', env', showEvents) = processFlags args (mergeEnvs robinIntrinsics robinBuiltins) False
2223 (_, reactors, results) <- processArgs args' env'
3738
3839 processArgs args env = processArgs' args env [] [] where
3940 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
4051 processArgs' (filename:rest) env reactors results = do
4152 program <- readFile filename
42 case parseRobin program of
53 case parseToplevel program of
4354 Right topExprs -> do
4455 (env', reactors', results') <- return $ TopLevel.collect topExprs env reactors results
4556 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) [] []
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Arith)"
2 -> Tests for functionality "Evaluate Robin Expression (with Arith)"
33
44 `abs` evaluates its single argument to a number, and evaluates to
55 the absolute value of that number (where the sign is always positive.)
66
7 | (display
8 | (abs 5))
7 | (abs 5)
98 = 5
109
11 | (display
12 | (abs (subtract 0 5)))
10 | (abs (subtract 0 5))
1311 = 5
1412
15 | (display
16 | (abs 0))
13 | (abs 0)
1714 = 0
1815
1916 `abs` expects exactly one numeric argument.
2017
21 | (display
22 | (abs))
18 | (abs)
2319 ? uncaught exception: (illegal-arguments ())
2420
25 | (display
26 | (abs 14 23))
21 | (abs 14 23)
2722 ? uncaught exception: (illegal-arguments (14 23))
2823
29 | (display
30 | (abs #t))
24 | (abs #t)
3125 ? uncaught exception: (expected-number #t)
3226
3327 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Arith)"
2 -> Tests for functionality "Evaluate Robin Expression (with Arith)"
33
44 `add` evaluates both of its arguments to numbers and evaluates to the sum
55 of those two numbers.
66
7 | (display
8 | (add 14 23))
7 | (add 14 23)
98 = 37
109
1110 `add` expects exactly two arguments.
1211
13 | (display
14 | (add 14))
12 | (add 14)
1513 ? uncaught exception: (illegal-arguments (14))
1614
17 | (display
18 | (add 6 7 7))
15 | (add 6 7 7)
1916 ? uncaught exception: (illegal-arguments (6 7 7))
2017
2118 Both of the arguments to `add` must be numbers.
2219
23 | (display
24 | (add 14 #t))
20 | (add 14 #t)
2521 ? uncaught exception: (expected-number #t)
2622
27 | (display
28 | (add #t 51))
23 | (add #t 51)
2924 ? uncaught exception: (expected-number #t)
3025
3126 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Boolean)"
2 -> Tests for functionality "Evaluate Robin Expression (with Boolean)"
33
44 `and` evaluates both of its arguments to booleans, and evaluates to the
55 logical conjunction (boolean "and") of these two values.
66
7 | (display
8 | (and #t #t))
7 | (and #t #t)
98 = #t
109
11 | (display
12 | (and #t #f))
10 | (and #t #f)
1311 = #f
1412
15 | (display
16 | (and #f #t))
13 | (and #f #t)
1714 = #f
1815
19 | (display
20 | (and #f #f))
16 | (and #f #f)
2117 = #f
2218
2319 `and` expects exactly two arguments.
2420
25 (Hate to weaken this test, but I'm not a purist -- yet.)
26
27 | (display
28 | (and #f))
21 | (and #f)
2922 ? uncaught exception
3023
31 | (display
32 | (and #t #f #f))
24 | (and #t #f #f)
3325 ? uncaught exception: (illegal-arguments (#t #f #f))
3426
3527 `and` expects both of its arguments to be booleans.
3628
37 | (display
38 | (and 100 #t))
29 | (and 100 #t)
3930 ? uncaught exception: (expected-boolean 100)
4031
41 | (display
42 | (and #t 99))
32 | (and #t 99)
4333 ? uncaught exception: (expected-boolean 99)
4434
4535 `and` is short-circuiting in the sense that no arguments after the first
4636 `#f` argument will be evaluated. Fully testing this requires side-effects,
4737 but it can be demonstrated as follows.
4838
49 | (display
50 | (and #f 100))
39 | (and #f 100)
5140 = #f
5241
5342 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with List)"
2 -> Tests for functionality "Evaluate Robin Expression (with List)"
33
44 `append` evaluates both of its arguments to lists. It then
55 evaluates to a list which is the concatenation of these lists.
66
7 | (display
8 | (append (list 1 2 3) (list 4 5 6)))
7 | (append (list 1 2 3) (list 4 5 6))
98 = (1 2 3 4 5 6)
109
11 | (display
12 | (append () ()))
10 | (append () ())
1311 = ()
1412
1513 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Small)"
2 -> Tests for functionality "Evaluate Robin Expression (with Small)"
33
44 `bind-args` is a macro for binding the arguments of another value to
55 identifiers, as well as asserting that the correct number of arguments
1111 which those expressions will be evaluated, and an expression to evaluate
1212 in the new environment in which the identifiers are bound.
1313
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))
1716 = (1 2)
1817
1918 Expressions in the list of values are evaluated.
2019
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))
2422 = (1 9)
2523
2624 Too many or too few arguments will raise an `illegal-arguments`
2725 exception.
2826
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))
3229 ? uncaught exception: (illegal-arguments (1))
3330
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))
3733 ? uncaught exception: (illegal-arguments (1 2 3))
3834
3935 The literal arguments are reported in the exception.
4036
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)
4439 ? uncaught exception: (illegal-arguments ((subtract 5 4) (subtract 1 0)))
4540
4641 This is how it might be used in a macro definition. The reason for the
4843 become clear here: typically you would just pass the macro's `args` and
4944 `env` to those arguments.
5045
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)))
5650 = 15
5751
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)))
6457 = 14
6558
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)))
7163 ? uncaught exception: (illegal-arguments ((subtract 0 0)))
7264
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))
7869 ? uncaught exception: (illegal-arguments (9 9 9))
7970
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))
8575 ? uncaught exception: (unbound-identifier n)
8676
8777 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Small)"
2 -> Tests for functionality "Evaluate Robin Expression (with Small)"
33
44 `bind` binds a single identifier to the result of evaluating a single
55 expression, and makes that binding available in another expression which
66 it evaluates.
77
8 | (display
98 | (bind x (literal hello)
10 | (list x x)))
9 | (list x x))
1110 = (hello hello)
1211
13 | (display
1412 | (bind dup (macro (self args env)
1513 | (list (head args) (head args)))
16 | (dup g)))
14 | (dup g))
1715 = (g g)
1816
19 | (display
2017 | (bind dup (macro (self args env)
2118 | (bind x (eval env (head args))
2219 | (list x x)))
23 | (dup (literal g))))
20 | (dup (literal g)))
2421 = (g g)
2522
26 | (display
2723 | (bind dup (macro (self args env)
2824 | (bind x (eval env (head args))
2925 | (list x x)))
30 | (dup (dup (literal g)))))
26 | (dup (dup (literal g))))
3127 = ((g g) (g g))
3228
33 | (display
3429 | (bind find (macro (self args env)
3530 | (bind-args (alist key) args env
3631 | (if (equal? alist (literal ())) (literal ())
3732 | (if (equal? key (head (head alist)))
3833 | (head alist)
3934 | (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)))
4136 = (a b)
4237
4338 `bind` expects exactly three arguments, or else an exception will be raised.
4439
45 | (display
46 | (bind smoosh (fun (x y) (list y x))))
40 | (bind smoosh (fun (x y) (list y x)))
4741 ? uncaught exception
4842
49 | (display
50 | (bind smoosh))
43 | (bind smoosh)
5144 ? uncaught exception
5245
53 | (display
54 | (bind))
46 | (bind)
5547 ? uncaught exception
5648
5749 `bind` is basically equivalent to Scheme's `let`, but only one
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Boolean)"
2 -> Tests for functionality "Evaluate Robin Expression (with Boolean)"
33
44 `boolean?` evaluates its argument, then evaluates to `#t` if it is a
55 boolean value, `#f` otherwise.
66
7 | (display
8 | (boolean? #t))
7 | (boolean? #t)
98 = #t
109
11 | (display
12 | (boolean? (head (prepend #f ()))))
10 | (boolean? (head (prepend #f ())))
1311 = #t
1412
15 | (display
16 | (boolean? ()))
13 | (boolean? ())
1714 = #f
1815
1916 The argument to `boolean?` may (naturally) be any type, but there must be
2017 exactly one argument.
2118
22 | (display
23 | (boolean? #t #f))
19 | (boolean? #t #f)
2420 ? uncaught exception: (illegal-arguments (#t #f))
2521
26 | (display
27 | (boolean?))
22 | (boolean?)
2823 ? uncaught exception: (illegal-arguments ())
2924
3025 '<<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))))))
11
22 ### `catch` ###
33
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)"
58
69 `catch` installs an exception handler.
710
1013 first argument of `catch`, and the second argument of `catch` is
1114 evaluated in that new environment.
1215
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))))
2218 = ((nasty-value 999999) #f)
2319
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
2733
2834 The innermost `catch` will catch the exception.
2935
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))))
4039 = (derpy-value 9)
4140
4241 An exception raised from within an exception handler is
4342 caught by the next innermost exception handler.
4443
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))))
5548 = ((7 7) 9)
5649
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))
5854
5955 `catch` expects exactly three arguments.
6056
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))
6362
6463 '<<SPEC'
64
65 (require catch)
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Small)"
2 -> Tests for functionality "Evaluate Robin Expression (with Small)"
33
4 | (display
5 | (choose (#t (literal hi)) (else (literal lo))))
4 | (choose (#t (literal hi)) (else (literal lo)))
65 = hi
76
8 | (display
9 | (choose (#f (literal hi)) (#t (literal med)) (else (literal lo))))
7 | (choose (#f (literal hi)) (#t (literal med)) (else (literal lo)))
108 = med
119
12 | (display
13 | (choose (#f (literal hi)) (#f (literal med)) (else (literal lo))))
10 | (choose (#f (literal hi)) (#f (literal med)) (else (literal lo)))
1411 = lo
1512
1613 `choose` can have zero tests before the `else`.
1714
18 | (display
19 | (choose (else (literal woo))))
15 | (choose (else (literal woo)))
2016 = woo
2117
2218 `choose` does require an `else` branch, or else an exception will be
2319 raised.
2420
25 | (display
26 | (choose (#f (literal hi)) (#f (literal med))))
21 | (choose (#f (literal hi)) (#f (literal med)))
2722 ? uncaught exception
2823
29 | (display
30 | (choose))
24 | (choose)
3125 ? uncaught exception
3226
3327 Each branch of a `choose` needs to be a two-element list, or else an
3428 exception will be raised.
3529
36 | (display
37 | (choose (#t) (else (literal lo))))
30 | (choose (#t) (else (literal lo)))
3831 ? uncaught exception
3932
40 | (display
41 | (choose (#f 66) (else)))
33 | (choose (#f 66) (else))
4234 ? uncaught exception
4335
4436 `choose` is basically equivalent to Scheme's `cond`.
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Arith)"
2 -> Tests for functionality "Evaluate Robin Expression (with Arith)"
33
44 ### `>` ###
55
66 `>` evaluates both of its arguments to numbers, then evaluates to `#t`
77 if the first number is strictly greater than the second.
88
9 | (display
10 | (> 6 4))
9 | (> 6 4)
1110 = #t
1211
13 | (display
14 | (> 6 8))
12 | (> 6 8)
1513 = #f
1614
17 | (display
18 | (> 6 6))
15 | (> 6 6)
1916 = #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
2038
2139 `>` expects exactly two arguments, both numbers.
2240
23 | (display
24 | (> 14))
41 | (> 14)
2542 ? uncaught exception: (illegal-arguments (14))
2643
27 | (display
28 | (> 14 23 57))
44 | (> 14 23 57)
2945 ? uncaught exception: (illegal-arguments (14 23 57))
3046
31 | (display
32 | (> 14 #t))
47 | (> 14 #t)
3348 ? uncaught exception: (expected-number #t)
3449
35 | (display
36 | (> #t 51))
50 | (> #t 51)
3751 ? uncaught exception: (expected-number #t)
3852
3953 ### `<` ###
4155 `<` evaluates both of its arguments to numbers, then evaluates to `#t`
4256 if the first number is strictly less than the second.
4357
44 | (display
45 | (< 6 4))
58 | (< 6 4)
4659 = #f
4760
48 | (display
49 | (< 6 8))
61 | (< 6 8)
5062 = #t
5163
52 | (display
53 | (< 6 6))
64 | (< 6 6)
5465 = #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
5587
5688 `<` expects exactly two arguments, both numbers.
5789
58 | (display
59 | (< 14))
90 | (< 14)
6091 ? uncaught exception: (illegal-arguments (14))
6192
62 | (display
63 | (< 14 23 57))
93 | (< 14 23 57)
6494 ? uncaught exception: (illegal-arguments (14 23 57))
6595
66 | (display
67 | (< 14 #t))
96 | (< 14 #t)
6897 ? uncaught exception: (expected-number #t)
6998
70 | (display
71 | (< #t 51))
99 | (< #t 51)
72100 ? uncaught exception: (expected-number #t)
73101
74102 ### `>=` ###
76104 `>=` evaluates both of its arguments to numbers, then evaluates to `#t`
77105 if the first number is greater than or equal to the second.
78106
79 | (display
80 | (>= 6 4))
107 | (>= 6 4)
81108 = #t
82109
83 | (display
84 | (>= 6 8))
110 | (>= 6 8)
85111 = #f
86112
87 | (display
88 | (>= 6 6))
113 | (>= 6 6)
89114 = #t
115
116 | (>= 1610612736 (subtract 0 1610612736)))
117 = #t
118
119 | (>= (subtract 0 1610612736) 1610612736)
120 = #f
90121
91122 `>=` expects exactly two arguments, both numbers.
92123
93 | (display
94 | (>= 14))
124 | (>= 14)
95125 ? uncaught exception: (illegal-arguments (14))
96126
97 | (display
98 | (>= 14 23 57))
127 | (>= 14 23 57)
99128 ? uncaught exception: (illegal-arguments (14 23 57))
100129
101 | (display
102 | (>= 14 #t))
130 | (>= 14 #t)
103131 ? uncaught exception: (expected-number #t)
104132
105 | (display
106 | (>= #t 51))
133 | (>= #t 51)
107134 ? uncaught exception: (expected-number #t)
108135
109136 ### `<=` ###
111138 `<=` evaluates both of its arguments to numbers, then evaluates to `#t`
112139 if the first number is less than or equal to the second.
113140
114 | (display
115 | (<= 6 4))
141 | (<= 6 4)
116142 = #f
117143
118 | (display
119 | (<= 6 8))
144 | (<= 6 8)
120145 = #t
121146
122 | (display
123 | (<= 6 6))
147 | (<= 6 6)
148 = #t
149
150 | (<= 1610612736 (subtract 0 1610612736)))
151 = #f
152
153 | (<= (subtract 0 1610612736) 1610612736)
124154 = #t
125155
126156 `<=` expects exactly two arguments, both numbers.
127157
128 | (display
129 | (<= 14))
158 | (<= 14)
130159 ? uncaught exception: (illegal-arguments (14))
131160
132 | (display
133 | (<= 14 23 57))
161 | (<= 14 23 57)
134162 ? uncaught exception: (illegal-arguments (14 23 57))
135163
136 | (display
137 | (<= 14 #t))
164 | (<= 14 #t)
138165 ? uncaught exception: (expected-number #t)
139166
140 | (display
141 | (<= #t 51))
167 | (<= #t 51)
142168 ? uncaught exception: (expected-number #t)
143169
144170 '<<SPEC'
145171
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
146176 (define > (macro (self args env)
147177 (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
149182 (define >= (macro (self args env)
150183 (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
152186 (define < (macro (self args env)
153187 (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
155192 (define <= (macro (self args env)
156193 (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)))))
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with List)"
2 -> Tests for functionality "Evaluate Robin Expression (with List)"
33
4 | (display
5 | (delete (literal b) (literal ((a 1) (b 2) (c 3)))))
4 | (delete (literal b) (literal ((a 1) (b 2) (c 3))))
65 = ((a 1) (c 3))
76
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))))
108 = ((a 1) (c 3))
119
12 | (display
13 | (delete (literal r) (literal ((a 1) (b 2) (c 3)))))
10 | (delete (literal r) (literal ((a 1) (b 2) (c 3))))
1411 = ((a 1) (b 2) (c 3))
1512
1613 The following should be true for any identifier i and alist x.
1714
18 | (display
1915 | (let ((i (literal a))
2016 | (x (literal ((a 5) (b 7)))))
21 | (lookup i (delete i x))))
17 | (lookup i (delete i x)))
2218 = ()
2319
24 | (display
25 | (delete (literal q) 55))
20 | (delete (literal q) 55)
2621 ? uncaught exception: (expected-list 55)
2722
28 | (display
29 | (delete (literal q) (literal ((a 7) 99 (q 4)))))
23 | (delete (literal q) (literal ((a 7) 99 (q 4))))
3024 ? uncaught exception: (expected-list 99)
3125
3226 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Arith)"
2 -> Tests for functionality "Evaluate Robin Expression (with Arith)"
33
44 `divide` evaluates both of its arguments to numbers and evaluates to the
55 result of integer division of the first number by the second. Integer
66 division computes by what integer the second number can be multiplied
77 to make it as big as possible without exceeding the first number.
88
9 | (display
10 | (divide 100 3))
9 | (divide 100 3)
1110 = 33
1211
13 | (display
14 | (divide (subtract 0 100) 3))
12 | (divide (subtract 0 100) 3)
1513 = -34
1614
17 | (display
18 | (divide 100 (subtract 0 3)))
15 | (divide 100 (subtract 0 3))
1916 = -34
2017
21 | (display
22 | (divide 33 33))
18 | (divide 33 33)
2319 = 1
2420
25 | (display
26 | (divide 33 34))
21 | (divide 33 34)
2722 = 0
2823
29 | (display
30 | (divide 10 0))
24 | (divide 10 0)
3125 ? uncaught exception: (division-by-zero 10)
3226
3327 Division by zero is undefined, and an exception will be raised.
3428
35 | (display
36 | (divide 10 0))
29 | (divide 10 0)
3730 ? uncaught exception: (division-by-zero 10)
3831
3932 `div` expects exactly two arguments, both numbers.
4033
41 | (display
42 | (divide 14))
34 | (divide 14)
4335 ? uncaught exception: (illegal-arguments (14))
4436
45 | (display
46 | (divide 14 23 57))
37 | (divide 14 23 57)
4738 ? uncaught exception: (illegal-arguments (14 23 57))
4839
49 | (display
50 | (divide 14 #t))
40 | (divide 14 #t)
5141 ? uncaught exception: (expected-number #t)
5242
53 | (display
54 | (divide #t 51))
43 | (divide #t 51)
5544 ? uncaught exception: (expected-number #t)
5645
5746 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with List)"
2 -> Tests for functionality "Evaluate Robin Expression (with List)"
33
44 `drop-while` evaluates its first argument to obtain a predicate and its
55 second argument to obtain a list. It then evaluates to the suffix of the
66 given list, starting at the first element which does not satisfy the
77 predicate.
88
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)))
1110 = (3 4 five 6 seven)
1211
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)))
1513 = (1 2 3 4 5 6)
1614
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)))
1916 = ()
2017
21 | (display
22 | (drop-while (fun (x) (symbol? x)) ()))
18 | (drop-while (fun (x) (symbol? x)) ())
2319 = ()
2420
25 | (display
26 | (drop-while (fun (x) (symbol? x)) #f))
21 | (drop-while (fun (x) (symbol? x)) #f)
2722 ? uncaught exception: (expected-list #f)
2823
2924 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with List)"
2 -> Tests for functionality "Evaluate Robin Expression (with List)"
33
44 `elem?` evaluates its first argument to a value of any type, and its
55 second argument to obtain a list. It then evaluates to `#t` if the value
66 is `equal?` to some element of the list, `#f` otherwise.
77
8 | (display
9 | (elem? (literal p) (literal (a p e))))
8 | (elem? (literal p) (literal (a p e)))
109 = #t
1110
12 | (display
13 | (elem? (literal p) (literal (a r k))))
11 | (elem? (literal p) (literal (a r k)))
1412 = #f
1513
16 | (display
17 | (elem? 7 ()))
14 | (elem? 7 ())
1815 = #f
1916
20 | (display
21 | (elem? 7 (list 5 (list 6 7) 8)))
17 | (elem? 7 (list 5 (list 6 7) 8))
2218 = #f
2319
2420 `elem?` can be defined in terms of `find`, in a manner such as:
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with List)"
2 -> Tests for functionality "Evaluate Robin Expression (with List)"
33
44 `empty?` evaluates its single argument, and evaluates to `#t` if that value
55 is the empty list, `#f` otherwise.
66
7 | (display
8 | (empty? (literal symbol)))
7 | (empty? (literal symbol))
98 = #f
109
11 | (display
12 | (empty? ()))
10 | (empty? ())
1311 = #t
1412
15 | (display
16 | (empty? (list 1 2 3)))
13 | (empty? (list 1 2 3))
1714 = #f
1815
1916 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Env)"
2 -> Tests for functionality "Evaluate Robin Expression (with Env)"
33
44 `env?` evaluates its single argument, and evaluates to `#t` if
55 and only if it is a well-formed binding alist.
66
7 | (display
8 | (env? (literal ((a 1) (b 2) (c 3)))))
7 | (env? (literal ((a 1) (b 2) (c 3))))
98 = #t
109
11 | (display
12 | (env? (literal ((a 1) (999 2) (c 3)))))
10 | (env? (literal ((a 1) (999 2) (c 3))))
1311 = #f
1412
15 | (display
16 | (env? (literal ((a 1) (b 2) c))))
13 | (env? (literal ((a 1) (b 2) c)))
1714 = #f
1815
19 | (display
20 | (env? 7))
16 | (env? 7)
2117 = #f
2218
23 | (display
24 | (env? (env)))
19 | (env? (env))
2520 = #t
2621
2722 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Small)"
2 -> Tests for functionality "Evaluate Robin Expression (with Small)"
33
44 `env` evaluates to all the bindings in effect at the point of execution
55 where this form is encountered, as an alist.
66
7 | (display
87 | (bind find (macro (self args env)
98 | (bind-args (alist key) args env
109 | (if (equal? alist (literal ())) (literal ())
1211 | (head alist)
1312 | (self (tail alist) key)))))
1413 | (prepend
15 | (find (env) (literal symbol?)) (find (env) (literal prepend)))))
14 | (find (env) (literal symbol?)) (find (env) (literal prepend))))
1615 = ((symbol? symbol?) prepend prepend)
1716
1817 `env` expects no arguments. Any arguments supplied will be simply ignored
1918 and discarded, without being evaluated.
2019
21 | (display
2220 | (bind find (macro (self args env)
2321 | (bind-args (alist key) args env
2422 | (if (equal? alist (literal ())) (literal ())
2725 | (self (tail alist) key)))))
2826 | (prepend
2927 | (find (env find) (literal symbol?))
30 | (find (env (goofah whatever)) (literal prepend)))))
28 | (find (env (goofah whatever)) (literal prepend))))
3129 = ((symbol? symbol?) prepend prepend)
3230
3331 '<<SPEC'
11
22 ### `equal?` ###
33
4 -> Tests for functionality "Interpret core Robin Program"
4 -> Tests for functionality "Evaluate core Robin Expression"
55
66 `equal?` evaluates both of its arguments to arbitrary S-expressions
77 and compares them for deep equality.
88
99 `equal?` works on symbols.
1010
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))
1614 = #t
1715
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))
2319 = #f
2420
2521 `equal?` works on lists.
2622
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 ()))))
3025 = #t
3126
3227 `equal?` works on lists, deeply.
3328
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 ()))))
3731 = #f
3832
3933 Two values of different types are never equal.
4034
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) ()))
4536 = #f
4637
47 | (display
48 | (equal? #f
49 | ()))
38 | (equal? #f ())
5039 = #f
5140
5241 Arguments to `equal?` can be any type, but fewer than or more than
5342 two arguments will raise an exception.
5443
55 | (display
56 | (equal? 7))
44 | (equal? 7)
5745 ? uncaught exception: (illegal-arguments (7))
5846
59 | (display
60 | (equal? 7 8 9))
47 | (equal? 7 8 9)
6148 ? uncaught exception: (illegal-arguments (7 8 9))
6249
6350 '<<SPEC'
51
52 (require equal?)
11
22 ### `eval` ###
33
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"
58
69 `eval` evaluates its first argument to obtain an environment, then
710 evaluates its second argument to obtain an S-expression; it then
98101 ? uncaught exception: (illegal-arguments (4 5 6))
99102
100103 '<<SPEC'
104
105 (require eval)
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Env)"
2 -> Tests for functionality "Evaluate Robin Expression (with Env)"
33
44 `export` treats its arguments as a list of identifiers, and returns an
55 environment where only those identifiers are bound to values.
1414 Note: the order of the bindings in the binding alist isn't guaranteed;
1515 thus these tests are written to search the resulting alist.
1616
17 | (display
18 | (let ((a 1) (b 6))
19 | (length (export a b))))
17 | (let ((a 1) (b 6))
18 | (length (export a b)))
2019 = 2
2120
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)))
2523 = (1)
2624
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)))
3027 = (6)
3128
32 | (display
33 | (lookup (literal head) (export head tail)))
29 | (lookup (literal head) (export head tail))
3430 = (head)
3531
36 | (display
37 | (lookup (literal prepend) (export head tail)))
32 | (lookup (literal prepend) (export head tail))
3833 = ()
3934
4035 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with List)"
2 -> Tests for functionality "Evaluate Robin Expression (with List)"
33
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))))
65 = ((b 6) (a 1) (b 2) (c 3))
76
87 The following should be true for any identifier i and alist x.
98
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)))
1412 = (1)
1513
16 | (display
17 | (extend (literal b) 6 ()))
14 | (extend (literal b) 6 ())
1815 = ((b 6))
1916
20 | (display
21 | (extend (literal b) 6 81))
17 | (extend (literal b) 6 81)
2218 ? uncaught exception: (expected-list 81)
2319
2420 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with List)"
2 -> Tests for functionality "Evaluate Robin Expression (with List)"
33
44 `filter` evaluates its first argument to obtain a macro, generally assumed
55 to be a predicate (a one-argument function which evaluates to a boolean).
77 to a list which contains all the elements of the given list, in the same
88 order, which satisfy the predicate.
99
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)))
1211 = (two four six)
1312
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)))
1614 ? uncaught exception: (expected-boolean banana)
1715
1816 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with List)"
2 -> Tests for functionality "Evaluate Robin Expression (with List)"
33
44 `find` evaluates its first argument to obtain a predicate, then evaluates
55 its second argument to obtain a list. It then evaluates to a list which
77 a list which contains exactly one element, which will be the first
88 element from the list which satisfies the predicate.
99
10 | (display
11 | (find (fun (x) (symbol? x)) ()))
10 | (find (fun (x) (symbol? x)) ())
1211 = ()
1312
14 | (display
15 | (find (fun (x) (symbol? x)) (list 1 2 3)))
13 | (find (fun (x) (symbol? x)) (list 1 2 3))
1614 = ()
1715
18 | (display
19 | (find (fun (x) #t) (list 1 2 3)))
16 | (find (fun (x) #t) (list 1 2 3))
2017 = (1)
2118
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)))
2420 = (two)
2521
2622 `find` could be defined in terms of `filter`, but in practice it would
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with List)"
2 -> Tests for functionality "Evaluate Robin Expression (with List)"
33
44 `first` evaluates its first argument to obtain a non-negative integer,
55 considered to be a desired length, and its second argument to obtain a list.
66 It then evaluates to the prefix of the given list of the desired length.
77
8 | (display
9 | (first 0 (list 1 2 3 4 5)))
8 | (first 0 (list 1 2 3 4 5))
109 = ()
1110
12 | (display
13 | (first 3 (list 1 2 3 4 5)))
11 | (first 3 (list 1 2 3 4 5))
1412 = (1 2 3)
1513
16 | (display
17 | (first 6 (list 1 2 3 4 5)))
14 | (first 6 (list 1 2 3 4 5))
1815 ? uncaught exception: (expected-list ())
1916
20 | (display
21 | (first 1 (literal foo)))
17 | (first 1 (literal foo))
2218 ? uncaught exception: (expected-list foo)
2319
24 | (display
25 | (first 0 (literal foo)))
20 | (first 0 (literal foo))
2621 = ()
2722
2823 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with List)"
2 -> Tests for functionality "Evaluate Robin Expression (with List)"
33
44 `flatten` evaluates its first argument to obtain a list, then evaluates
55 to the list obtained by interpolating all elements into a single list.
99 is applied recursively to any elements in sublists which are themselves
1010 sublists.
1111
12 | (display
13 | (flatten ()))
12 | (flatten ())
1413 = ()
1514
16 | (display
17 | (flatten (list 1 2 3)))
15 | (flatten (list 1 2 3))
1816 = (1 2 3)
1917
20 | (display
21 | (flatten (list 1 (list 2 3 4) 5)))
18 | (flatten (list 1 (list 2 3 4) 5))
2219 = (1 2 3 4 5)
2320
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))
2622 = (1 2 3 4 4 4 5)
2723
28 | (display
29 | (flatten (list 1 () 5)))
24 | (flatten (list 1 () 5))
3025 = (1 5)
3126
3227 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with List)"
2 -> Tests for functionality "Evaluate Robin Expression (with List)"
33
44 `fold` evaluates its first argument to obtain a macro, generally assumed to
55 be a two-argument function, its second argument to obtain an initial value,
1111 the second argument. `fold` evaluates to the result of the the final
1212 application of the function.
1313
14 | (display
15 | (fold (fun (x a) x) () (literal (three dog night))))
14 | (fold (fun (x a) x) () (literal (three dog night)))
1615 = night
1716
18 | (display
19 | (fold (fun (x a) a) 541 (literal (archie moffam))))
17 | (fold (fun (x a) a) 541 (literal (archie moffam)))
2018 = 541
2119
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)))
2421 = (((() three) dog) night)
2522
26 | (display
27 | (fold 99 (fun (x a) a) (literal (three dog night))))
23 | (fold 99 (fun (x a) a) (literal (three dog night)))
2824 ? uncaught exception: (inapplicable-object 99)
2925
3026 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Small)"
2 -> Tests for functionality "Evaluate Robin Expression (with Small)"
33
44 You can define functions with `fun`. They can be anonymous.
55
6 | (display
7 | ((fun (a) a) (literal whee)))
6 | ((fun (a) a) (literal whee))
87 = whee
98
109 Function have "closure" behavior; that is, bindings in force when a
1110 function is defined will still be in force when the function is applied,
1211 even if they are no longer lexically in scope.
1312
14 | (display
1513 | ((let
1614 | ((a (literal (hi)))
1715 | (f (fun (x) (list x a))))
18 | f) (literal oh)))
16 | f) (literal oh))
1917 = (oh (hi))
2018
2119 Functions can take functions.
2220
23 | (display
2421 | (let
2522 | ((apply (fun (x) (x (literal a)))))
26 | (apply (fun (r) (list r)))))
23 | (apply (fun (r) (list r))))
2724 = (a)
2825
2926 Functions can return functions.
3027
31 | (display
3228 | (let
3329 | ((mk (fun (x) (fun (y) (prepend y x))))
3430 | (mk2 (mk (literal (vindaloo)))))
35 | (mk2 (literal chicken))))
31 | (mk2 (literal chicken)))
3632 = (chicken vindaloo)
3733
3834 Arguments to functions shadow any other bindings in effect.
3935
40 | (display
4136 | (let
4237 | ((a (literal a))
4338 | (b (fun (a) (list a a))))
44 | (b 7)))
39 | (b 7))
4540 = (7 7)
4641
4742 A function may have no arguments at all.
4843
49 | (display
50 | ((fun () 7)))
44 | ((fun () 7))
5145 = 7
5246
5347 But, a function must have exactly both a body and a list of formal arguments.
5448 Otherwise, an exception will be raised.
5549
56 | (display
57 | ((fun ())))
50 | ((fun ()))
5851 ? uncaught exception
5952
60 | (display
61 | ((fun)))
53 | ((fun))
6254 ? uncaught exception
6355
64 | (display
65 | ((fun (a) a a)))
56 | ((fun (a) a a))
6657 ? uncaught exception
6758
6859 An `illegal-arguments` exception will be raised if not enough arguments are
6960 supplied to a function call.
7061
71 | (display
7262 | ((fun (a b) (list b a))
73 | (prepend 1 ())))
63 | (prepend 1 ()))
7464 ? uncaught exception: (illegal-arguments
7565
7666 An `illegal-arguments` exception will be raised if too many arguments are
7767 supplied to a function call.
7868
79 | (display
8069 | ((fun (a b) (list b a))
81 | 1 (prepend 2 ()) 3))
70 | 1 (prepend 2 ()) 3)
8271 ? uncaught exception: (illegal-arguments
8372
8473 `fun` is basically equivalent to Scheme's `lambda`.
11
22 ### `head` ###
33
4 -> Tests for functionality "Interpret core Robin Program"
4 -> Tests for functionality "Evaluate core Robin Expression"
55
66 `head` evaluates its argument to a list, and evaluates to the first element
77 of that list.
88
9 | (display
10 | (head (prepend #t ())))
9 | (head (prepend #t ()))
1110 = #t
1211
1312 `head` expects its argument to be a list.
1413
15 | (display
16 | (head #f))
14 | (head #f)
1715 ? uncaught exception: (expected-list #f)
1816
1917 `head` expects exactly one argument.
2018
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 ())))
2421
25 | (display
26 | (head))
22 | (head)
2723 ? uncaught exception: (illegal-arguments ())
2824
2925 `head` is basically equivalent to Scheme's `car`.
3026
3127 '<<SPEC'
28
29 (require head)
11
22 ### `if` ###
33
4 -> Tests for functionality "Interpret core Robin Program"
4 -> Tests for functionality "Evaluate core Robin Expression"
55
66 `if` evaluates its first argument to a boolean value. If that value is
77 `#t`, it evaluates, and evaluates to, its second argument; or if that value
88 is `#f` it evaluates, and evaluates to, its third argument. In all cases,
99 at most two arguments are evaluated.
1010
11 | (display
12 | (if #t 7 9))
11 | (if #t 7 9)
1312 = 7
1413
15 | (display
16 | (if #f 7 9))
14 | (if #f 7 9)
1715 = 9
1816
1917 The identifiers named in the branch which is not evaluated need not be
2018 properly bound to values in the environment.
2119
22 | (display
23 | (if #t 1 (prepend fred ethel)))
20 | (if #t 1 (prepend fred ethel))
2421 = 1
2522
2623 The second and third arguments can be arbitrary expressions, but `if`
2724 expects its first argument to be a boolean.
2825
29 | (display
30 | (if 5 7 9))
26 | (if 5 7 9)
3127 ? uncaught exception: (expected-boolean 5)
3228
3329 `if` expects exactly three arguments.
3430
35 | (display
36 | (if #t 7))
31 | (if #t 7)
3732 ? uncaught exception: (illegal-arguments (#t 7))
3833
39 | (display
40 | (if #t 7 8 9))
34 | (if #t 7 8 9)
4135 ? uncaught exception: (illegal-arguments (#t 7 8 9))
4236
4337 `if` is basically equivalent to Scheme's `if`.
4438
4539 '<<SPEC'
40
41 (require if)
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with List)"
2 -> Tests for functionality "Evaluate Robin Expression (with List)"
33
44 `index` evaluates its first argument to a natural number, and its
55 second argument to a list. It then evaluates to the element of the
66 list at the index given by the natural number. The index is 0-based;
77 0 refers to the element at the head of the list.
88
9 | (display
10 | (index 0 (literal (the girl from ipanema))))
9 | (index 0 (literal (the girl from ipanema)))
1110 = the
1211
13 | (display
14 | (index 2 (literal (the girl from ipanema))))
12 | (index 2 (literal (the girl from ipanema)))
1513 = from
1614
17 | (display
1815 | (bind last (fun (li) (index (subtract (length li) 1) li))
19 | (last (literal (the girl from ipanema)))))
16 | (last (literal (the girl from ipanema))))
2017 = ipanema
2118
2219 Attempting to index beyond the end of the list will raise an exception.
2320
24 | (display
25 | (index 7 (literal (the girl from ipanema))))
21 | (index 7 (literal (the girl from ipanema)))
2622 ? uncaught exception: (expected-list ())
2723
2824 `index` expects its first argument to be a number.
2925
30 | (display
31 | (index (literal goofy) (list 1 2 3 4 5)))
26 | (index (literal goofy) (list 1 2 3 4 5))
3227 ? uncaught exception: (expected-number goofy)
3328
3429 `index` expects its second argument to be a list.
3530
36 | (display
37 | (index 8 (literal whatnot)))
31 | (index 8 (literal whatnot))
3832 ? uncaught exception: (expected-list whatnot)
3933
4034 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Stdlib)"
2 -> Tests for functionality "Evaluate Robin Expression (with Stdlib)"
33
44 `itoa` evaluates its sole argument to an integer, then evaluates to
55 a string representing that integer in decimal.
66
7 | (display
8 | (itoa 100))
7 | (itoa 100)
98 = (49 48 48)
109
11 | (display
12 | (itoa 99))
10 | (itoa 99)
1311 = (57 57)
1412
15 | (display
16 | (itoa 0))
13 | (itoa 0)
1714 = (48)
1815
19 | (display
20 | (itoa (subtract 0 1)))
16 | (itoa (subtract 0 1))
2117 = (45 49)
2218
23 | (display
24 | (itoa (subtract 0 765)))
19 | (itoa (subtract 0 765))
2520 = (45 55 54 53)
2621
27 | (display
28 | (itoa (literal m)))
22 | (itoa (literal m))
2923 ? uncaught exception: (expected-number m)
3024
3125 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with List)"
2 -> Tests for functionality "Evaluate Robin Expression (with List)"
33
44 `last` evaluates its first argument to obtain a non-negative integer,
55 considered to be a desired length, and its second argument to obtain a
66 list. It then evaluates to the suffix of the given list of the desired
77 length.
88
9 | (display
10 | (last 0 (list 1 2 3 4 5)))
9 | (last 0 (list 1 2 3 4 5))
1110 = ()
1211
13 | (display
14 | (head (last 1 (list 1 2 3 4 5))))
12 | (head (last 1 (list 1 2 3 4 5)))
1513 = 5
1614
17 | (display
18 | (last 3 (list 1 2 3 4 5)))
15 | (last 3 (list 1 2 3 4 5))
1916 = (3 4 5)
2017
21 | (display
22 | (last 6 (list 1 2 3 4 5)))
18 | (last 6 (list 1 2 3 4 5))
2319 ? uncaught exception: (expected-list ())
2420
25 | (display
26 | (last 1 (literal foo)))
21 | (last 1 (literal foo))
2722 ? uncaught exception: (expected-list foo)
2823
2924 Unlike `first`, `last` does care if it's not a list, even when the count
3025 is zero.
3126
32 | (display
3327 | (last 0 (literal foo)))
3428 ? uncaught exception: (expected-list foo)
3529
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with List)"
2 -> Tests for functionality "Evaluate Robin Expression (with List)"
33
44 `length` evaluates its single argument to obtain a proper list, then
55 evaluates to a non-negative integer which is the length of the list
66 (the number of cells, not counting nested cells and not counting the
77 empty list at the very tail.)
88
9 | (display
10 | (length ()))
9 | (length ())
1110 = 0
1211
13 | (display
14 | (length (list 1 2 #t #f 3)))
12 | (length (list 1 2 #t #f 3))
1513 = 5
1614
17 | (display
18 | (length (literal whatnot)))
15 | (length (literal whatnot))
1916 ? uncaught exception: (expected-list whatnot)
2017
2118 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Small)"
2 -> Tests for functionality "Evaluate Robin Expression (with Small)"
33
44 `let` lets you bind multiple identifiers to multiple values.
55
66 An identifier can be bound to a symbol.
77
8 | (display
9 | (let ((a (literal hello))) a))
8 | (let ((a (literal hello))) a)
109 = hello
1110
1211 `let` can appear in the binding expression in a `let`.
1312
14 | (display
15 | (let ((a (let ((b (literal c))) b))) a))
13 | (let ((a (let ((b (literal c))) b))) a)
1614 = c
1715
1816 `let` can bind a symbol to a macro.
1917
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)))
2623 = (foo)
2724
2825 Bindings established in a `let` remain in effect when evaluating
2926 the arguments things in the body of the `let`.
3027
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))))
3632 = ((g g) (g g))
3733
3834 Bindings established in a binding in a `let` can be seen in
3935 subsequent bindings in the same `let`.
4036
41 | (display
42 | (let ((a (literal hello)) (b (list a))) b))
37 | (let ((a (literal hello)) (b (list a))) b)
4338 = (hello)
4439
4540 Shadowing happens.
4641
47 | (display
48 | (let ((a (literal hello))) (let ((a (literal goodbye))) a)))
42 | (let ((a (literal hello))) (let ((a (literal goodbye))) a))
4943 = goodbye
5044
5145 `let` can have an empty list of bindings.
5246
53 | (display
54 | (let () (literal hi)))
47 | (let () (literal hi))
5548 = hi
5649
5750 The list of bindings must be a list, or else an exception will be raised.
5851
59 | (display
60 | (let 999 (literal hi)))
52 | (let 999 (literal hi))
6153 ? uncaught exception
6254
6355 Each binding in a list must be a list, or else an exception will be raised.
6456
65 | (display
66 | (let (999) (literal hi)))
57 | (let (999) (literal hi))
6758 ? uncaught exception
6859
6960 Both the body and the list of bindings are required, or else an exception
7061 will be raised.
7162
72 | (display
73 | (let ()))
63 | (let ()))
7464 ? uncaught exception
7565
76 | (display
77 | (let))
66 | (let)
7867 ? uncaught exception
7968
8069 Any arguments given beyond the body and list of bindings will be ignored
8170 and discarded, without being evaluated.
8271
83 | (display
84 | (let ((a 1)) a foo))
72 | (let ((a 1)) a foo)
8573 = 1
8674
8775 Each binding must have at least a name and a value, or else an exception
8876 will be raised.
89
90 | (display
91 | (let ((a)) a))
77
78 | (let ((a)) a)
9279 ? uncaught exception
9380
94 | (display
95 | (let (()) 7))
81 | (let (()) 7)
9682 ? uncaught exception
9783
9884 Anything given in a binding beyond the name and the value will simply be
9985 ignored and discarded, without being evaluated or otherwise examined.
10086
101 | (display
102 | (let ((a 1 foo)) a))
87 | (let ((a 1 foo)) a)
10388 = 1
10489
10590 The identifier in a binding must be a symbol.
10691
107 | (display
108 | (let ((3 1)) 3))
92 | (let ((3 1)) 3)
10993 ? uncaught exception: (illegal-binding (3 1))
11094
11195 `let` is basically equivalent to Scheme's `let*` or Haskell's `let`.
11
22 ### `list?` ###
33
4 -> Tests for functionality "Interpret core Robin Program"
4 -> Tests for functionality "Evaluate core Robin Expression"
55
66 `list?` evaluates its argument, then evaluates to `#t` if it is a list,
77 `#f` otherwise.
88
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)))
1210 = #t
1311
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)))
1713 = #t
1814
19 | (display
20 | (list? (prepend 4 (prepend 5 ()))))
15 | (list? (prepend 4 (prepend 5 ())))
2116 = #t
2217
2318 The empty list is a list.
2419
25 | (display
26 | (list? ()))
20 | (list? ())
2721 = #t
2822
2923 Symbols are not lists.
3024
31 | (define literal (macro (s a e) (head a)))
32 | (display
33 | (list? (literal a)))
25 | (list? ((macro (s a e) (head a)) a))
3426 = #f
3527
3628 The argument to `list?` may (naturally) be any type, but there must be
3729 exactly one argument.
3830
39 | (display
40 | (list? (prepend 4 ()) (prepend 6 ())))
31 | (list? (prepend 4 ()) (prepend 6 ()))
4132 ? uncaught exception: (illegal-arguments ((prepend 4 ()) (prepend 6 ())))
4233
43 | (display
44 | (list?))
34 | (list?)
4535 ? uncaught exception: (illegal-arguments ())
4636
4737 '<<SPEC'
38
39 (require list?)
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Small)"
2 -> Tests for functionality "Evaluate Robin Expression (with Small)"
33
44 `list` is a macro which evaluates each of its arguments, and evaluates to a
55 (proper) list containing each of the results, in the same order.
66
7 | (display
8 | (list 1 2 3 4 5))
7 | (list 1 2 3 4 5)
98 = (1 2 3 4 5)
109
11 | (display
12 | (list (list 2 3) (list 6 7)))
10 | (list (list 2 3) (list 6 7))
1311 = ((2 3) (6 7))
1412
1513 `list` need not have any arguments at all; the result is the empty list.
1614
17 | (display
18 | (list))
15 | (list)
1916 = ()
2017
2118 Unlike `literal`, `list` does evaluate its arguments, all of them.
2219
23 | (display
24 | (list (literal x) (literal y)))
20 | (list (literal x) (literal y))
2521 = (x y)
26
27 `list` does not require any arguments.
28
29 | (display
30 | (list))
31 = ()
3222
3323 '<<SPEC'
3424
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Small)"
2 -> Tests for functionality "Evaluate Robin Expression (with Small)"
33
44 One of the most basic identifiers available in `small` is `literal`,
55 which evaluates to the literal content of its sole argument, which can be
66 any S-expression.
77
8 | (display
9 | (literal symbol))
8 | (literal symbol))
109 = symbol
1110
12 | (display
13 | (literal (hello (there) world)))
11 | (literal (hello (there) world)))
1412 = (hello (there) world)
1513
1614 `literal` requires at least one argument; otherwise, an exception will
1715 be raised.
1816
19 | (display
20 | (literal))
17 | (literal)
2118 ? uncaught exception
22
23 TODO Unlike other things in `stdlib`, this does not use builtin wrappers
2419
2520 Any arguments beyond the first argument are simply ignored and discarded.
2621
27 | (display
28 | (literal a b c))
22 | (literal a b c))
2923 = a
3024
3125 `literal` is basically equivalent to Scheme's `quote`.
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with List)"
2 -> Tests for functionality "Evaluate Robin Expression (with List)"
33
4 | (display
5 | (lookup (literal b) (literal ((a 1) (b 2) (c 3)))))
4 | (lookup (literal b) (literal ((a 1) (b 2) (c 3))))
65 = (2)
76
8 | (display
9 | (lookup (literal a) (literal ((a 1) (a 2) (a 3)))))
7 | (lookup (literal a) (literal ((a 1) (a 2) (a 3))))
108 = (1)
119
12 | (display
13 | (lookup (literal r) (literal ((a 1) (b 2) (c 3)))))
10 | (lookup (literal r) (literal ((a 1) (b 2) (c 3))))
1411 = ()
1512
16 | (display
17 | (lookup (literal q) ()))
13 | (lookup (literal q) ())
1814 = ()
1915
20 | (display
21 | (lookup (literal q) 55))
16 | (lookup (literal q) 55)
2217 ? uncaught exception: (expected-list 55)
2318
24 | (display
25 | (lookup (literal q) (literal ((a 7) 99 (q 4)))))
19 | (lookup (literal q) (literal ((a 7) 99 (q 4))))
2620 ? uncaught exception: (expected-list 99)
2721
2822 '<<SPEC'
11
22 ### `macro?` ###
33
4 -> Tests for functionality "Interpret core Robin Program"
4 -> Tests for functionality "Evaluate core Robin Expression"
55
66 `macro?` evaluates its argument, then evaluates to `#t` if it is a macro,
77 or `#f` if it is not.
88
9 | (display
10 | (macro? (macro (self args env) args)))
9 | (macro? (macro (self args env) args))
1110 = #t
1211
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.
1613
17 | (display
18 | (macro? macro))
14 | (macro? macro)
1915 = #t
2016
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))
2320 = #f
2421
25 | (display
26 | (macro? 5))
22 Numbers are not macros.
23
24 | (macro? 5)
2725 = #f
2826
2927 The argument to `macro?` may (naturally) be any type, but there must be
3028 exactly one argument.
3129
32 | (display
33 | (macro? @macro @macro))
34 ? uncaught exception: (illegal-arguments (@macro @macro))
30 | (macro? macro macro)
31 ? uncaught exception: (illegal-arguments (macro macro))
3532
36 | (display
37 | (macro?))
33 | (macro?)
3834 ? uncaught exception: (illegal-arguments ())
3935
4036 '<<SPEC'
37
38 (require macro?)
11
22 ### `macro` ###
33
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"
58
69 `macro` takes its first argument to be a list of three formal
710 parameters, and its second argument to be an arbitrary expression,
204207 ? uncaught exception: (illegal-arguments ((self args 99) prepend))
205208
206209 '<<SPEC'
210
211 (require macro)
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with List)"
2 -> Tests for functionality "Evaluate Robin Expression (with List)"
33
44 `map` evaluates its first argument to obtain a macro, and its second argument
55 to obtain a list. It then evaluates to a list which is obtained by applying
66 the macro to each element of the given list. The macro is generally assumed
77 to be a one-argument function.
88
9 | (display
10 | (map (fun (x) (list x)) (literal (three dog night))))
9 | (map (fun (x) (list x)) (literal (three dog night)))
1110 = ((three) (dog) (night))
1211
1312 While it is possible to pass a macro that is not a function, it is not
1413 very productive. (Also, it exposes the implementation of `map`, so this
1514 is not a very good test.)
1615
17 | (display
18 | (map (macro (self args env) args) (literal (three dog night))))
16 | (map (macro (self args env) args) (literal (three dog night)))
1917 = (((head li)) ((head li)) ((head li)))
2018
2119 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Arith)"
2 -> Tests for functionality "Evaluate Robin Expression (with Arith)"
33
44 `multiply` evaluates both of its arguments to numbers and evaluates to the product
55 of those two numbers.
66
7 | (display
8 | (multiply 6 7))
7 | (multiply 6 7)
98 = 42
109
11 | (display
12 | (multiply (subtract 0 6) 7))
10 | (multiply (subtract 0 6) 7)
1311 = -42
1412
15 | (display
16 | (multiply 6 (subtract 0 7)))
13 | (multiply 6 (subtract 0 7))
1714 = -42
1815
19 | (display
20 | (multiply (subtract 0 6) (subtract 0 7)))
16 | (multiply (subtract 0 6) (subtract 0 7))
2117 = -42
2218
2319 `multiply` expects exactly two arguments.
2420
25 | (display
26 | (multiply 14))
21 | (multiply 14)
2722 ? uncaught exception: (illegal-arguments (14))
2823
29 | (display
30 | (multiply 6 7 7))
24 | (multiply 6 7 7)
3125 ? uncaught exception: (illegal-arguments (6 7 7))
3226
3327 Both of the arguments to `multiply` must be numbers.
3428
35 | (display
36 | (multiply 14 #t))
29 | (multiply 14 #t)
3730 ? uncaught exception: (expected-number #t)
3831
39 | (display
40 | (multiply #t 51))
32 | (multiply #t 51)
4133 ? uncaught exception: (expected-number #t)
4234
4335 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Boolean)"
2 -> Tests for functionality "Evaluate Robin Expression (with Boolean)"
33
44 `not` evaluates its single argument to a boolean, then evaluates to
55 the logical negation of that boolean.
66
7 | (display
8 | (not #t))
7 | (not #t)
98 = #f
109
11 | (display
12 | (not #f))
10 | (not #f)
1311 = #t
1412
1513 `not` expects exactly one argument.
1614
17 | (display
18 | (not))
15 | (not)
1916 ? uncaught exception: (illegal-arguments ())
2017
21 | (display
22 | (not #t #f))
18 | (not #t #f)
2319 ? uncaught exception: (illegal-arguments (#t #f))
2420
2521 `not` expects its single argument to be a boolean.
2622
27 | (display
28 | (not 33))
23 | (not 33)
2924 ? uncaught exception: (expected-boolean 33)
3025
3126 '<<SPEC'
11
22 ### `number?` ###
33
4 -> Tests for functionality "Interpret core Robin Program"
4 -> Tests for functionality "Evaluate core Robin Expression"
55
66 `number?` evaluates its argument, then evaluates to `#t` if it is a
77 number, `#f` otherwise.
88
9 | (display
10 | (number? 7))
9 | (number? 7)
1110 = #t
1211
13 | (display
14 | (number? 0))
12 | (number? 0)
1513 = #t
1614
17 | (display
18 | (number? ()))
15 | (number? ())
1916 = #f
2017
21 | (display
22 | (number? #t))
18 | (number? #t)
2319 = #f
2420
25 | (define literal (macro (s a e) (head a)))
26 | (display
27 | (number? (literal seven)))
21 | (number? ((macro (s a e) (head a)) seven))
2822 = #f
2923
3024 That's a good question...
3125
32 | (define literal (macro (s a e) (head a)))
33 | (display
34 | (number? (literal 7)))
26 | (number? ((macro (s a e) (head a)) 7))
3527 = #t
3628
3729 The argument to `number?` may (naturally) be any type, but there must be
3830 exactly one argument.
3931
40 | (display
41 | (number? 6 4))
32 | (number? 6 4)
4233 ? uncaught exception: (illegal-arguments (6 4))
4334
44 | (display
45 | (number?))
35 | (number?)
4636 ? uncaught exception: (illegal-arguments ())
4737
4838 '<<SPEC'
39
40 (require number?)
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Boolean)"
2 -> Tests for functionality "Evaluate Robin Expression (with Boolean)"
33
44 `or` evaluates both of its arguments to booleans, and evaluates to the
55 logical disjunction (boolean "or") of these two values.
66
7 | (display
8 | (or #t #t))
7 | (or #t #t)
98 = #t
109
11 | (display
12 | (or #t #f))
10 | (or #t #f)
1311 = #t
1412
15 | (display
16 | (or #f #t))
13 | (or #f #t)
1714 = #t
1815
19 | (display
20 | (or #f #f))
16 | (or #f #f)
2117 = #f
2218
2319 `or` expects exactly two arguments.
2420
25 (Hate to weaken this test, but I'm not a purist -- yet.)
26
27 | (display
28 | (or #f))
21 | (or #f)
2922 ? uncaught exception
3023
31 | (display
32 | (or #t #f #f))
24 | (or #t #f #f)
3325 ? uncaught exception: (illegal-arguments (#t #f #f))
3426
3527 `or` expects both of its arguments to be booleans.
3628
37 | (display
38 | (or 100 #f))
29 | (or 100 #f)
3930 ? uncaught exception: (expected-boolean 100)
4031
41 | (display
42 | (or #f 99))
32 | (or #f 99)
4333 ? uncaught exception: (expected-boolean 99)
4434
4535 `or` is short-circuiting in the sense that no arguments after the first
4636 `#t` argument will be evaluated. Fully testing this requires side-effects,
4737 but it can be demonstrated as follows.
4838
49 | (display
50 | (or #t 100))
39 | (or #t 100)
5140 = #t
5241
5342 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with List)"
2 -> Tests for functionality "Evaluate Robin Expression (with List)"
33
44 `prefix?` evaluates its first and second arguments to obtain lists.
55 It then evaluates to `#t` if the first list is a prefix of the second
77 or the head of A is `equal?` to the head of B and the tail of A is a
88 prefix of the tail of B.
99
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))
1211 = #t
1312
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))
1614 = #f
1715
18 | (display
19 | (prefix? () (list 1 2 3 4 5 6)))
16 | (prefix? () (list 1 2 3 4 5 6))
2017 = #t
2118
22 | (display
23 | (prefix? () (literal schpritz)))
19 | (prefix? () (literal schpritz))
2420 = #t
2521
26 | (display
27 | (prefix? (list 1 2 3) (list 1 2 3)))
22 | (prefix? (list 1 2 3) (list 1 2 3))
2823 = #t
2924
30 | (display
31 | (prefix? (list 1 2 3 4) (list 1 2 3)))
25 | (prefix? (list 1 2 3 4) (list 1 2 3))
3226 = #f
3327
3428 '<<SPEC'
11
22 ### `prepend` ###
33
4 -> Tests for functionality "Interpret core Robin Program"
4 -> Tests for functionality "Evaluate core Robin Expression"
55
66 `prepend` evaluates both of its arguments, then evaluates to a list cell
77 which contains the first value as its data and the second value as the
88 continuation of the list.
99
10 | (display
11 | (prepend () ()))
10 | (prepend () ())
1211 = (())
1312
14 | (display
15 | (prepend #t (prepend #f ())))
13 | (prepend #t (prepend #f ()))
1614 = (#t #f)
1715
1816 The second argument to `prepend` must be a list.
1917
20 | (display
21 | (prepend #t #f))
18 | (prepend #t #f)
2219 ? uncaught exception: (expected-list #f)
2320
2421 The first argument to `prepend` can be any type, but fewer than or more than
2522 two arguments will raise an exception.
2623
27 | (display
28 | (prepend #t))
24 | (prepend #t)
2925 ? uncaught exception: (illegal-arguments (#t))
3026
31 | (display
32 | (prepend #f #t #f))
27 | (prepend #f #t #f)
3328 ? uncaught exception: (illegal-arguments (#f #t #f))
3429
3530 `prepend` is basically equivalent to Scheme's `cons`, except for the
3631 requirement that the second argument be a list.
3732
3833 '<<SPEC'
34
35 (require prepend)
11
22 ### `raise` ###
33
4 -> Tests for functionality "Interpret core Robin Program"
4 -> Tests for functionality "Evaluate core Robin Expression"
55
66 `raise` evaluates its argument to obtain a value, then raises an
77 exception with that value.
1313 refers to the exception that triggered it, but this is not a strict
1414 requirement.
1515
16 | (display
17 | (raise 999999))
16 | (raise 999999)
1817 ? uncaught exception: 999999
1918
2019 `raise`'s single argument may be any kind of value, but `raise` expects
2120 exactly one argument.
2221
23 | (display
24 | (raise))
22 | (raise)
2523 ? uncaught exception: (illegal-arguments ())
2624
27 | (display
28 | (raise 2 3 4))
25 | (raise 2 3 4)
2926 ? uncaught exception: (illegal-arguments (2 3 4))
3027
3128 A Robin environment may install exception handlers which are not defined
3330 strict requirement.)
3431
3532 '<<SPEC'
33
34 (require raise)
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Arith)"
2 -> Tests for functionality "Evaluate Robin Expression (with Arith)"
33
44 `remainder` evaluates both of its arguments to numbers and evaluates to the
55 remainder of the division of the first number by the second.
66
7 | (display
8 | (remainder 12 3))
7 | (remainder 12 3)
98 = 0
109
11 | (display
12 | (remainder 11 3))
10 | (remainder 11 3)
1311 = 2
1412
15 | (display
16 | (remainder 10 3))
13 | (remainder 10 3)
1714 = 1
1815
19 | (display
20 | (remainder 9 3))
16 | (remainder 9 3)
2117 = 0
2218
23 The remainder is *always positive*.
19 The remainder is *always non-negative*.
2420
25 | (display
26 | (remainder (subtract 0 10) 3))
21 | (remainder (subtract 0 10) 3)
2722 = 2
2823
29 | (display
30 | (remainder 10 (subtract 0 3)))
24 | (remainder 10 (subtract 0 3))
3125 = 2
3226
3327 Trying to find the remainder of a division by zero is undefined, and an
3428 exception will be raised.
3529
36 | (display
37 | (remainder 10 0))
30 | (remainder 10 0)
3831 ? uncaught exception: (division-by-zero 10)
3932
4033 `remainder` expects exactly two arguments, both numbers.
4134
42 | (display
43 | (remainder 14))
35 | (remainder 14)
4436 ? uncaught exception: (illegal-arguments (14))
4537
46 | (display
47 | (remainder 14 23 57))
38 | (remainder 14 23 57)
4839 ? uncaught exception: (illegal-arguments (14 23 57))
4940
50 | (display
51 | (remainder 14 #t))
41 | (remainder 14 #t)
5242 ? uncaught exception: (expected-number #t)
5343
54 | (display
55 | (remainder #t 51))
44 | (remainder #t 51)
5645 ? uncaught exception: (expected-number #t)
5746
5847 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with List)"
2 -> Tests for functionality "Evaluate Robin Expression (with List)"
33
44 `rest` evaluates its first argument to obtain a non-negative integer,
55 considered to be a desired position, and its second argument to obtain a
66 list. It then evaluates to the suffix of the given list starting at the
77 desired position. The position 0 indicates the beginning of the list.
88
9 | (display
10 | (rest 0 (list 1 2 3 4 5)))
9 | (rest 0 (list 1 2 3 4 5))
1110 = (1 2 3 4 5)
1211
13 | (display
14 | (rest 3 (list 1 2 3 4 5)))
12 | (rest 3 (list 1 2 3 4 5))
1513 = (4 5)
1614
17 | (display
18 | (rest 5 (list 1 2 3 4 5)))
15 | (rest 5 (list 1 2 3 4 5))
1916 = ()
2017
21 | (display
22 | (rest 6 (list 1 2 3 4 5)))
18 | (rest 6 (list 1 2 3 4 5))
2319 ? uncaught exception: (expected-list ())
2420
25 | (display
26 | (rest 1 (literal foo)))
21 | (rest 1 (literal foo))
2722 ? uncaught exception: (expected-list foo)
2823
29 | (display
30 | (rest 0 (literal foo)))
24 | (rest 0 (literal foo))
3125 = foo
3226
3327 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with List)"
2 -> Tests for functionality "Evaluate Robin Expression (with List)"
33
44 `reverse` evaluates its argument to a list, then evaluates to a list which
55 is the same as the given list in every respect except that the order of
66 the elements is reversed.
77
8 | (display
9 | (reverse (literal (1 2 3 4 5))))
8 | (reverse (literal (1 2 3 4 5)))
109 = (5 4 3 2 1)
1110
12 | (display
13 | (reverse (literal fairies-wear-boots)))
11 | (reverse (literal fairies-wear-boots))
1412 ? uncaught exception: (expected-list fairies-wear-boots)
1513
1614 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Env)"
2 -> Tests for functionality "Evaluate Robin Expression (with Env)"
33
44 `sandbox` takes a list of identifiers as its first argument, and evaluates
55 its second argument in an environment where all bindings *except* those
66 for the listed identifiers have been unbound.
77
8 | (display
9 | (sandbox (prepend tail)
10 | (tail (prepend 8 (prepend 9 ())))))
8 | (sandbox (prepend tail)
9 | (tail (prepend 8 (prepend 9 ()))))
1110 = (9)
1211
13 | (display
14 | (sandbox (prepend tail)
15 | (head (prepend 8 (prepend 9 ())))))
12 | (sandbox (prepend tail)
13 | (head (prepend 8 (prepend 9 ()))))
1614 ? uncaught exception: (unbound-identifier head)
1715
1816 '<<SPEC'
11
22 ### `sign` ###
33
4 -> Tests for functionality "Interpret core Robin Program"
4 -> Tests for functionality "Evaluate core Robin Expression"
55
66 `sign` evaluates its sole argument to a number, then
77 evaluates to 0 if that number is 0, 1 if that number is positive, or
88 -1 if that number is negative.
99
10 | (display
11 | (sign 26))
10 | (sign 26)
1211 = 1
1312
14 | (display
15 | (sign 0))
13 | (sign 0)
1614 = 0
1715
18 | (display
19 | (sign (subtract 0 200)))
16 | (sign (subtract 0 200))
2017 = -1
2118
2219 `sign` expects a number.
2320
24 | (display
25 | (sign #f))
21 | (sign #f)
2622 ? uncaught exception: (expected-number #f)
2723
28 | (define literal (macro (s a e) (head a)))
29 | (display
30 | (sign (literal k)))
24 | (sign ((macro (s a e) (head a)) k))
3125 ? uncaught exception: (expected-number k)
3226
3327 `sign` expects exactly one argument.
3428
35 | (display
36 | (sign 100 200 300))
29 | (sign 100 200 300)
3730 ? uncaught exception: (illegal-arguments (100 200 300))
3831
39 | (display
40 | (sign))
32 | (sign)
4133 ? uncaught exception: (illegal-arguments ())
4234
4335 '<<SPEC'
36
37 (require sign)
11
22 ### `subtract` ###
33
4 -> Tests for functionality "Interpret core Robin Program"
4 -> Tests for functionality "Evaluate core Robin Expression"
55
66 `subtract` evaluates its first argument to a number, then
77 evaluates its second argument to a number, then evaluates
88 to the difference between the first and second numbers.
99
10 | (display
11 | (subtract 6 4))
10 | (subtract 6 4)
1211 = 2
1312
14 | (display
15 | (subtract 1000 8000))
13 | (subtract 1000 8000)
1614 = -7000
1715
1816 Addition may be accomplished by negating the second argument.
1917
20 | (display
21 | (subtract 999 (subtract 0 999)))
18 | (subtract 999 (subtract 0 999))
2219 = 1998
2320
2421 `subtract` expects both of its arguments to be numbers.
2522
26 | (display
27 | (subtract #f 100))
23 | (subtract #f 100)
2824 ? uncaught exception: (expected-number #f)
2925
30 | (display
31 | (subtract 100 ()))
26 | (subtract 100 ())
3227 ? uncaught exception: (expected-number ())
3328
3429 `subtract` expects exactly two arguments.
3530
36 | (display
37 | (subtract 100 200 300))
31 | (subtract 100 200 300)
3832 ? uncaught exception: (illegal-arguments (100 200 300))
3933
40 | (display
41 | (subtract))
34 | (subtract)
4235 ? uncaught exception: (illegal-arguments ())
4336
4437 '<<SPEC'
38
39 (require subtract)
11
22 ### `symbol?` ###
33
4 -> Tests for functionality "Interpret core Robin Program"
4 -> Tests for functionality "Evaluate core Robin Expression"
55
66 `symbol?` evaluates its argument, then evaluates to `#t` if it is a symbol,
77 `#f` otherwise.
88
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))
1210 = #t
1311
1412 Numbers are not symbols.
1513
16 | (display
17 | (symbol? 9))
14 | (symbol? 9)
1815 = #f
1916
2017 Lists are not symbols.
2118
22 | (display
23 | (symbol? (prepend 1 ())))
19 | (symbol? (prepend 1 ()))
2420 = #f
2521
2622 The argument to `symbol?` may (naturally) be any type, but there must be
2723 exactly one argument.
2824
29 | (display
30 | (symbol? 77 88))
25 | (symbol? 77 88)
3126 ? uncaught exception: (illegal-arguments (77 88))
3227
33 | (display
34 | (symbol?))
28 | (symbol?)
3529 ? uncaught exception: (illegal-arguments ())
3630
3731 '<<SPEC'
32
33 (require symbol?)
11
22 ### `tail` ###
33
4 -> Tests for functionality "Interpret core Robin Program"
4 -> Tests for functionality "Evaluate core Robin Expression"
55
66 `tail` evaluates its argument to a list, and evaluates to the tail of that
77 list (the sublist obtained by removing the first element.)
88
9 | (display
10 | (tail (prepend #t (prepend #f ()))))
9 | (tail (prepend #t (prepend #f ())))
1110 = (#f)
1211
1312 `tail` expects its argument to be a list.
1413
15 | (display
16 | (tail #f))
14 | (tail #f)
1715 ? uncaught exception: (expected-list #f)
1816
1917 `tail` expects exactly one argument.
2018
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 ())))
2421
25 | (display
26 | (tail))
22 | (tail)
2723 ? uncaught exception: (illegal-arguments ())
2824
2925 `tail` is basically equivalent to Scheme's `cdr`.
3026
3127 '<<SPEC'
28
29 (require tail)
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with List)"
2 -> Tests for functionality "Evaluate Robin Expression (with List)"
33
44 `take-while` evaluates its first argument to obtain a predicate and its
55 second argument to obtain a list. It then evaluates to the longest prefix
66 of the list whose elements all satisfy the predicate.
77
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)))
109 = (one two)
1110
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)))
1412 = ()
1513
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)))
1815 = (1 2 3 4 5 6)
1916
20 | (display
21 | (take-while (fun (x) (symbol? x)) ()))
17 | (take-while (fun (x) (symbol? x)) ())
2218 = ()
2319
24 | (display
25 | (take-while (fun (x) (symbol? x)) #f))
20 | (take-while (fun (x) (symbol? x)) #f)
2621 ? uncaught exception: (expected-list #f)
2722
2823 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Env)"
2 -> Tests for functionality "Evaluate Robin Expression (with Env)"
33
44 `unbind` removes the given identifier from the environment and evaluates its
55 second argument in that reduced environment.
66
7 | (display
8 | (unbind if (if #t (literal x) (literal y))))
7 | (unbind if (if #t (literal x) (literal y)))
98 ? uncaught exception: (unbound-identifier if)
109
1110 If the identifier doesn't exist in the environment, no change is made to
1211 the environment.
1312
14 | (display
15 | (unbind yog-sothoth (if #t (literal x) (literal y))))
13 | (unbind yog-sothoth (if #t (literal x) (literal y)))
1614 = x
1715
1816 `unbind` removes all trace of binding from the given identifier; if that
1917 identifier has several definitions that are shadowed, none of them will be
2018 in effect.
2119
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)))
2724 ? uncaught exception: (unbound-identifier x)
2825
2926 '<<SPEC'
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Env)"
2 -> Tests for functionality "Evaluate Robin Expression (with Env)"
33
44 `unshadow` is similar to `unbind`, but only removes the latest binding
55 for the given identifier; previously shadowed bindings, if any exist,
66 will be visible instead.
77
8 | (display
9 | (unshadow yog-sothoth (if #t (literal x) (literal y))))
8 | (unshadow yog-sothoth (if #t (literal x) (literal y)))
109 = x
1110
12 | (display
13 | (unshadow if (if #t (literal x) (literal y))))
11 | (unshadow if (if #t (literal x) (literal y)))
1412 ? uncaught exception: (unbound-identifier if)
1513
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))))
1916 = x
2017
21 | (display
18 | (bind q 400
19 | (unshadow q q))
20 ? uncaught exception: (unbound-identifier q)
21
22 | (bind q 200
2223 | (bind q 400
2324 | (unshadow q q)))
24 ? uncaught exception: (unbound-identifier q)
25 = 200
2526
26 | (display
27 | (bind q 100
2728 | (bind q 200
2829 | (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)))))
3731 = 100
3832
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)))
4437 = 100
4538
4639 `unshadow` is something of a gimmick that shows off Robin's ability
00 ;'<<SPEC'
11
2 -> Tests for functionality "Interpret Robin Program (with Boolean)"
2 -> Tests for functionality "Evaluate Robin Expression (with Boolean)"
33
44 `xor` evaluates both of its arguments to boolean, then evaluates to
55 the "exclusive-or" of those booleans.
66
7 | (display
8 | (xor #t #t))
7 | (xor #t #t)
98 = #f
109
11 | (display
12 | (xor #t #f))
10 | (xor #t #f)
1311 = #t
1412
15 | (display
16 | (xor #f #t))
13 | (xor #f #t)
1714 = #t
1815
19 | (display
20 | (xor #f #f))
16 | (xor #f #f)
2117 = #f
2218
2319 `xor` expects exactly two arguments.
2420
25 | (display
26 | (xor #f))
21 | (xor #f)
2722 ? uncaught exception: (illegal-arguments (#f))
2823
29 | (display
30 | (xor #t #f #f))
24 | (xor #t #f #f)
3125 ? uncaught exception: (illegal-arguments (#t #f #f))
3226
3327 `xor` expects both of its arguments to be booleans.
3428
35 | (display
36 | (xor 100 #t))
29 | (xor 100 #t)
3730 ? uncaught exception: (expected-boolean 100)
3831
39 | (display
40 | (xor #t 99))
32 | (xor #t 99)
4133 ? uncaught exception: (expected-boolean 99)
4234
4335 This test demonstrates that these functions really do evaluate their
4436 arguments.
4537
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)
4839 = #t
4940
5041 '<<SPEC'