git @ Cat's Eye Technologies Castile / 598b216
Split out tests, design from README; rewrite what remains in it. Chris Pressey 3 years ago
5 changed file(s) with 1434 addition(s) and 1330 deletion(s). Raw diff Collapse all Expand all
00 Castile
11 =======
22
3 This is the reference distribution for Castile, an unremarkable programming
4 language.
3 Version 0.4 | _Entry_ [@ catseye.tc](https://catseye.tc/node/Castile)
4 | _See also:_ [Eightebed](https://github.com/catseye/Eightebed#readme)
55
6 The current version of Castile is 0.3. It is not only subject to change,
7 it is pretty much *guaranteed* to change.
6 - - - -
87
9 Unlike most of my programming languages, there is nothing that could really
10 be described as innovative or experimental or even particularly unusual
11 about Castile. It is not a particularly comfortable programming experience,
12 often forcing the programmer to be explicit and verbose.
8 This is the reference distribution for **Castile**, a simple imperative
9 language with union types.
1310
14 The reference implementation is slightly less unremarkable than the language
15 itself, if only for the fact that it compiles to four different target
16 languages: Javascript, Ruby, a hypothetical stack machine called
17 "stackmac" (a stackmac emulator ships with this distribution,) and (coming
18 soon) C.
11 The design of Castile was influenced (in varying degrees) by C, Rust,
12 Eightebed, Python, Ruby, and Erlang. More information on its roots can
13 be found in [doc/Design.md](doc/Design.md).
1914
20 Castile's influences might include:
15 The reference implementation can both interpret Castile programs and
16 compile them to a variety of targets — JavaScript, Ruby, C, and a generic
17 stack-based VM (included in this distribution).
2118
22 * **C**: Most of Castile's syntax follows C, but it is generally more
23 permissive (semicolons are optional, types of local variables and return
24 types for functions do not have to be declared, etc.) It has a type
25 system (where `struct`s are the only types with name equivalence) which
26 can be applied statically. It has function values, but not closures.
19 A rich test suite in [Falderal][] format, which describes the language
20 with many examples, can be found in [tests/Castile.md](tests/Castile.md).
2721
28 * **Rust**: There is a union type, to which values must be explicitly
29 promoted (with `as`) and extracted (with `typecase ... is`.) This is
30 like Rust's `Enum`, which is (to quote its tutorial) "much like the
31 'tagged union' pattern in C, but with better static guarantees." Along
32 with structs, this provides something similar to algebraic data typing,
33 as seen in languages such as Haskell, Scala, etc.
22 Quick Start
23 -----------
3424
35 * **Eightebed**: A few years back I realized that pointers that can
36 assume a null value are really a variant type, like Haskell's `Maybe`.
37 Of course, in most languages with pointers, the property of being null
38 isn't captured by the type; you can go ahead and dereference a pointer
39 in C or Java, whether it's valid or not. In Castile, this is captured
40 with a union type which includes `void`, and `typecase` generalizes
41 Eightebed's `ifvalid`.
25 Clone this repository, `cd` into the repo directory and run
4226
43 * **Python**: The first time a local variable is assigned counts as its
44 declaration as a local.
27 bin/castile eg/hello.castile
4528
46 * **Ruby**: The last expression in a function body is the return value
47 of that function; no explicit `return` is needed there. (But unlike
48 Ruby, and more like Pascal or linted C, all *other* expressions in
49 statement position within a block must have void type.)
29 Alternately, put the `bin` subdirectory on your executable search path, so
30 that you can run `castile` from any directory on your system. `castile`
31 has no dependencies besides Python (either Python 2 or Python 3.)
5032
51 * **Erlang** (or any other purely functional language): There are no
52 language-level pointers; sharing, if it happens at all, must be
53 orchestrated by the implementation. Global variables and function
54 arguments are not mutable, and neither are the fields of structs.
55 (But unlike Erlang, local variables *are* mutable.)
33 Motivating Example
34 ------------------
5635
57 Some lines of research underneath all this are, if all we have is a relatively
58 crude language, but we make it typesafe and give it a slightly nicer type
59 system, does it suffice to make programming tolerable? Do tolerable ways of
60 managing memory without a full garbage collector present themselves? Does
61 having a simple compiler which can be target many backends provide any
62 advantages?
36 Here are some functions for creating linked lists, written in Castile:
6337
64 Also unlike most of my programming languages (with the exceptions of ILLGOL
65 and Bhuna), Castile was largely "designed by building" -- I wrote an
66 interpreter, and the language it happens to accept, I called Castile.
67 I wrote the interpreter in a very short span of time; most of it was done
68 within 24 hours of starting (but consider that I ripped off some of the
69 scanning/parsing code from ALPACA.) A few days later, I extended the
70 implementation to also allow compiling to Javascript, and a few days after
71 that, I added a Ruby backend (why not, eh?), and over the next few days,
72 the stackmac backend and emulator.
38 struct list {
39 value: integer;
40 next: list|void;
41 }
7342
74 This document contains what is as close as there is to a specification of
75 the language, in the form of a Falderal test suite. The interpreter and all
76 compilers pass all the tests, but there are known shortcomings in at least
77 the compilers (no name mangling, etc.)
43 fun empty() {
44 return null as list|void
45 }
7846
79 The `eg` directory contains a few example Castile programs, including a
80 string tokenizer.
47 fun cons(v: integer, l: list|void) {
48 make list(value:v, next:l) as list|void
49 }
8150
82 One area where the Castile implementation is not entirely unremarkable is
83 that the typechecker is not required to be run. Unchecked Castile is
84 technically a different language from Castile; there are Castile programs
85 which would result in an error, where the Unchecked Castile program would
86 *not* (because it never executes the part of the program with a bad type.)
87 However, Unchecked Castile programs should be otherwise well-behaved;
88 any attempt to execute code which would have resulted in a type failure,
89 will result in a crash. Note, however, that this only applies to the
90 evaluator, not any of the compiler backends. Compiling Unchecked Castile
91 will simply not work (the backend will crash when it can't see any types.)
51 In this, `list|void` is a union type, which is the moral equivalent of
52 saying that the value is "nullable". In order to access any of the
53 concrete types of the union, one must use `typecase`:
9254
93 Grammar
94 -------
55 fun max(l: list|void) {
56 u = l;
57 v = u;
58 n = 0;
59 while true {
60 typecase u is void {
61 break;
62 }
63 typecase u is list {
64 if u.value > n {
65 n = u.value
66 }
67 v = u.next;
68 }
69 u = v;
70 }
71 return n
72 }
9573
96 Program ::= {Defn [";"]}.
97 Defn ::= "fun" ident "(" [Arg {"," Arg}] ")" Body
98 | "struct" ident "{" {ident ":" TExpr [";"]} "}"
99 | ident (":" TExpr0 | "=" Literal).
100 Arg ::= ident [":" TExpr1].
101 Body ::= "{" {Stmt [";"]} "}".
102 Stmt ::= "while" Expr0 Block
103 | "typecase" ident "is" TExpr0 Block
104 | "do" Expr0
105 | "return" Expr0
106 | If
107 | Expr0.
108 Block ::= "{" {Stmt [";"]} "}".
109 If ::= "if" Expr0 Block ["else" (Block | If)].
110 Expr0 ::= Expr1 {("and" | "or") Expr1} ["as" TExpr0].
111 Expr1 ::= Expr2 {(">" | ">=" | "<" | "<=" | "==" | "!=") Expr2}.
112 Expr2 ::= Expr3 {("+" | "-") Expr3}.
113 Expr3 ::= Expr4 {("*" | "/") Expr4}.
114 Expr4 ::= Expr5 {"(" [Expr0 {"," Expr0}] ")" | "." ident}.
115 Expr5 ::= "make" ident "(" [ident ":" Expr0 {"," ident ":" Expr0}] ")"
116 | "(" Expr0 ")"
117 | "not" Expr1
118 | Literal
119 | ident ["=" Expr0].
120 Literal ::= strlit
121 | ["-"] intlit
122 | "true" | "false" | "null"
123 | "fun" "(" [Arg {"," Arg}] ")" Body.
124 TExpr0 ::= TExpr1 [{"," TExpr1} "->" TExpr1].
125 TExpr1 ::= TExpr2 {"|" TExpr2}.
126 TExpr2 ::= "integer"
127 | "boolean"
128 | "void"
129 | "(" TExpr0 ")"
130 | ident.
74 This retains type-safety; the code will never unexpectedly be presented
75 with a null value.
13176
132 Examples
133 --------
77 Union types can also encourage the programmer follow a [Parse, don't validate][]
78 approach. In the above, `cons` will never return a `void`, and `max` is not
79 defined on empty lists. So ideally, we'd like to tighten their types to exclude
80 those. And we can:
13481
135 -> Tests for functionality "Run Castile Program"
82 ...
13683
137 ### Rudiments ###
84 fun cons(v: integer, l: list) {
85 make list(value:v, next:l as list|void)
86 }
13887
139 Minimal correct program.
88 fun singleton(v: integer) {
89 make list(value:v, next:null as list|void)
90 }
14091
141 | fun main() {}
142 =
92 fun max(l: list) {
93 u = l as list|void;
94 v = u;
95 ...
96 }
14397
144 A program may evaluate to a value.
98 Many more examples of Castile programs can be found in
99 [tests/Castile.md](tests/Castile.md).
145100
146 | fun main() { 160 }
147 = 160
148
149 The function named `main` is the one that is evaluated when the
150 program is run.
151
152 | fun foobar(a, b, c) { 100 }
153 | fun main() { 120 }
154 | fun f() { 140 }
155 = 120
156
157 `main` should have no formal arguments.
158
159 | fun main(a, b, c) {
160 | 120
161 | }
162 ? type mismatch
163
164 But other functions may.
165
166 | fun foobar(a, b) { b }
167 | fun main() { foobar(100, 200) }
168 = 200
169
170 Defined function names must be unique.
171
172 | fun dup() { 1 }
173 | fun dup() { 2 }
174 ? duplicate
175
176 Formal argument names must be unique.
177
178 | fun f(g, g) {}
179 | fun main() { 1 }
180 ? defined
181
182 Functions must be defined before they are referenced.
183
184 | fun main() { f(7) }
185 | fun f(g) { g }
186 ? undefined
187
188 Either that, or forward-declared.
189
190 | f : integer -> integer
191 | fun main() { f(7) }
192 | fun f(g) { g * 2 }
193 = 14
194
195 If forward-declared, types must match.
196
197 | f : integer -> string
198 | fun main() { f(7) }
199 | fun f(g) { g * 2 }
200 ? type mismatch
201
202 Arguments must match...
203
204 | fun f(g, h) { g * 2 + h * 2 }
205 | fun main() { f(7) }
206 ? argument mismatch
207
208 | fun f(g, h) { g * 2 + h * 2 }
209 | fun main() { f(7,8,9) }
210 ? argument mismatch
211
212 ### Statements ###
213
214 Statements are commands that have the type void and are executed for their
215 side-effects. So, in general, statements may not be expressions. The
216 exception is that the last statement in a block may be an expression; the
217 result of that expression is the value of the block.
218
219 | fun main() {
220 | 20 * 8
221 | }
222 = 160
223
224 | fun main() {
225 | 20 + 3 * 8;
226 | 20 * 8
227 | }
228 ? type mismatch
229
230 An `if`/`else` lets you make decisions.
231
232 | fun main() {
233 | a = 0;
234 | if 3 > 2 {
235 | a = 70
236 | } else {
237 | a = 80
238 | }
239 | a
240 | }
241 = 70
242
243 An `if` need not have an `else`.
244
245 | fun main() {
246 | a = 60
247 | if 3 > 2 {
248 | a = 70
249 | }
250 | a
251 | }
252 = 70
253
254 `if` always typechecks to void, one branch or two.
255
256 | fun main() {
257 | a = 60
258 | if 3 > 2 {
259 | a = 70
260 | }
261 | }
262 =
263
264 | fun main() {
265 | a = 60
266 | if 3 > 2 {
267 | a = 70
268 | } else {
269 | a = 90
270 | }
271 | }
272 =
273
274 If an `if` does have an `else`, the part after `else` must be either a block
275 (already shown) or another `if`.
276
277 | fun main() {
278 | if 2 > 3 {
279 | return 60
280 | } else if 4 > 5 {
281 | return 0
282 | } else {
283 | return 1
284 | }
285 | }
286 = 1
287
288 No dangling else problem.
289
290 | fun main() {
291 | if 2 > 3 {
292 | return 60
293 | } else if 4 < 5 {
294 | return 99
295 | } else {
296 | return 1
297 | }
298 | }
299 = 99
300
301 `while` loops.
302
303 | fun main() {
304 | a = 0 b = 4
305 | while b > 0 {
306 | a = a + b
307 | b = b - 1
308 | }
309 | a
310 | }
311 = 10
312
313 A `while` itself has void type.
314
315 | fun main() {
316 | a = 0; b = 4;
317 | while b > 0 {
318 | a = a + b;
319 | b = b - 1;
320 | }
321 | }
322 =
323
324 `break` may be used to prematurely exit a `while`.
325
326 | fun main() {
327 | a = 0; b = 0;
328 | while true {
329 | a = a + b;
330 | b = b + 1;
331 | if (b > 4) { break; }
332 | }
333 | a
334 | }
335 = 10
336
337 ### Expressions ###
338
339 Precedence.
340
341 | fun main() {
342 | 2 + 3 * 4 /* not 20 */
343 | }
344 = 14
345
346 Unary negation.
347
348 | fun main() {
349 | -3
350 | }
351 = -3
352
353 | fun main() {
354 | 2+-5
355 | }
356 = -3
357
358 Minus sign must be right in front of a number.
359
360 | fun main() {
361 | -(4)
362 | }
363 ? Expected
364
365 Unary not.
366
367 | fun main() {
368 | not (4 > 3)
369 | }
370 = False
371
372 Precedence of unary not.
373
374 | fun main() {
375 | not true or true
376 | }
377 = True
378
379 | fun main() {
380 | not 3 > 4
381 | }
382 = True
383
384 ### Local Variables ###
385
386 Local variables.
387
388 | fun main() {
389 | a = 6;
390 | b = 7;
391 | a + b
392 | }
393 = 13
394
395 Local variables can be assigned functions.
396
397 | fun ancillary(x) { x * 2 }
398 | fun main() {
399 | a = ancillary;
400 | a(7)
401 | }
402 = 14
403
404 Local variables can be assigned.
405
406 | fun main() {
407 | a = 6;
408 | a = a + 12;
409 | a
410 | }
411 = 18
412
413 | fun main() {
414 | a = 6;
415 | z = 99;
416 | a
417 | }
418 = 6
419
420 | fun main() {
421 | z = 6;
422 | a
423 | }
424 ? undefined
425
426 Local variables cannot occur in expressions until they are defined by an
427 initial assignment.
428
429 | fun main() {
430 | z = a * 10;
431 | a = 10;
432 | z
433 | }
434 ? undefined
435
436 A local variables may not be defined inside an `if` or `while` or `typecase`
437 block, as it might not be executed.
438
439 | fun main() {
440 | if (4 > 5) {
441 | a = 10;
442 | } else {
443 | b = 11;
444 | }
445 | b
446 | }
447 ? within control
448
449 | fun main() {
450 | b = false;
451 | while b {
452 | a = 10;
453 | }
454 | a
455 | }
456 ? within control
457
458 | fun main() {
459 | a = 55 as integer|string;
460 | typecase a is string {
461 | b = 7
462 | }
463 | a
464 | }
465 ? within control
466
467 Assignment, though it syntactically may occur in expressions, has a type of
468 void, so it can only really happen at the statement level.
469
470 | fun main() {
471 | a = 0; b = 0;
472 | a = b = 9;
473 | }
474 ? type mismatch
475
476 Variables in upper scopes may be modified.
477
478 | fun main() {
479 | a = 0
480 | if 3 > 2 {
481 | a = 4;
482 | }
483 | a
484 | }
485 = 4
486
487 ### Non-local Values ###
488
489 Literals may appear at the toplevel. Semicolons are optional at toplevel.
490
491 | factor = 5;
492 | fun main() {
493 | 6 * factor
494 | }
495 = 30
496
497 Toplevel literals may not be updated. (And thus
498
499 | factor = 5
500 | fun main() {
501 | factor = 7
502 | }
503 ? shadows
504
505 Toplevel literals may be function literals (the syntax we've been using is just sugar.)
506
507 | main = fun() {
508 | 7
509 | }
510 = 7
511
512 Truth and falsehood are builtin toplevels.
513
514 | fun main() {
515 | true or false
516 | }
517 = True
518
519 | fun main() {
520 | false and true
521 | }
522 = False
523
524 So is `null`, which is the single value of `void` type.
525
526 | fun wat(x: void) { 3 }
527 | fun main() {
528 | wat(null)
529 | }
530 = 3
531
532 ### More on Functions ###
533
534 Function arguments may not be updated.
535
536 | fun foo(x) {
537 | x = x + 14;
538 | x
539 | }
540 | fun main() {
541 | foo(7)
542 | }
543 ? shadows
544
545 Factorial can be computed.
546
547 | factorial : integer -> integer
548 | fun factorial(a) {
549 | if a == 0 {
550 | return 1
551 | } else {
552 | return a * factorial(a - 1)
553 | }
554 | }
555 | fun main() {
556 | factorial(6)
557 | }
558 = 720
559
560 Literal functions.
561
562 | fun main() {
563 | inc = fun(x) { x + 1 };
564 | inc(7)
565 | }
566 = 8
567
568 | fun main() {
569 | fun(x){ x + 1 }(9)
570 | }
571 = 10
572
573 | fun main() {
574 | a = 99;
575 | a = fun(x){ x + 1 }(9);
576 | a
577 | }
578 = 10
579
580 Literal functions can have local variables, loops, etc.
581
582 | fun main() {
583 | z = 99;
584 | z = fun(x) {
585 | a = x; b = x;
586 | while a > 0 {
587 | b = b + a; a = a - 1;
588 | }
589 | return b
590 | }(9);
591 | z
592 | }
593 = 54
594
595 Literal functions can define other literal functions...
596
597 | fun main() {
598 | fun(x){ fun(y){ fun(z){ z + 1 } } }(4)(4)(10)
599 | }
600 = 11
601
602 Literal functions can access globals.
603
604 | oid = 19
605 | fun main() {
606 | fun(x){ x + oid }(11);
607 | }
608 = 30
609
610 Literal functions cannot access variables declared in enclosing scopes.
611
612 | fun main() {
613 | oid = 19;
614 | fun(x){ x + oid }(11);
615 | }
616 ? undefined
617
618 Literal functions cannot access arguments declared in enclosing scopes.
619
620 | fun main() {
621 | fun(x){ fun(y){ fun(z){ y + 1 } } }(4)(4)(10)
622 | }
623 ? undefined
624
625 Functions can be passed to functions and returned from functions.
626
627 | fun doubble(x) { x * 2 }
628 | fun triple(x) { x * 3 }
629 | fun apply_and_add_one(f: (integer -> integer), x) { f(x) + 1 }
630 | fun sellect(a) { if a > 10 { return doubble } else { return triple } }
631 | fun main() {
632 | t = sellect(5);
633 | d = sellect(15);
634 | p = t(10);
635 | apply_and_add_one(d, p)
636 | }
637 = 61
638
639 To overcome the syntactic ambiguity with commas, function types
640 in function definitions must be in parens.
641
642 | fun add(x, y) { x + y }
643 | fun mul(x, y) { x * y }
644 | fun do_it(f: (integer, integer -> integer), g) {
645 | f(3, g)
646 | }
647 | fun main() {
648 | do_it(mul, 4) - do_it(add, 4)
649 | }
650 = 5
651
652 `return` may be used to prematurely return a value from a function.
653
654 | fun foo(y) {
655 | x = y
656 | while x > 0 {
657 | if x < 5 {
658 | return x;
659 | }
660 | x = x - 1;
661 | }
662 | 17
663 | }
664 | fun main() {
665 | foo(10) + foo(0)
666 | }
667 = 21
668
669 Type of value returned must jibe with value of function's block.
670
671 | fun foo(x) {
672 | return "string";
673 | 17
674 | }
675 | fun main() {
676 | foo(10) + foo(0)
677 | }
678 ? type mismatch
679
680 Type of value returned must jibe with other return statements.
681
682 | fun foo(x) {
683 | if x > 0 {
684 | return "string";
685 | } else {
686 | return 17
687 | }
688 | }
689 | fun main() {
690 | foo(10) + foo(0)
691 | }
692 ? type mismatch
693
694 ### Equality ###
695
696 Equality, inequality, boolean operators.
697
698 | fun main() {
699 | if 15 == 15 and ((15 != 14) or false) {
700 | print("struth")
701 | }
702 | }
703 = struth
704
705 | fun main() {
706 | if "five" == "five" and (("six" != "seven") or false) {
707 | print("struth")
708 | }
709 | }
710 = struth
711
712 Equality cannot be checked between two values of different types.
713
714 | fun main() {
715 | if 15 == "fifteen" {
716 | print("wat")
717 | }
718 | }
719 ? mismatch
720
721 | fun main() {
722 | if 15 != "fifteen" {
723 | print("wat")
724 | }
725 | }
726 ? mismatch
727
728 Equality can be checked between unions. (TODO)
729
730 /| fun main() {
731 /| a = 40 as string|integer
732 /| b = 40 as string|integer
733 /| if a == b {
734 /| print("it is")
735 /| }
736 /| }
737 /= ok
738
739 | fun main() {
740 | a = 40 as string|integer
741 | b = "beep" as string|integer
742 | if a != b {
743 | print("correct")
744 | }
745 | }
746 = correct
747
748 Equality cannot be tested between two disjoint unions.
749
750 | fun main() {
751 | a = 40 as string|integer
752 | b = 40 as integer|void
753 | if a == b {
754 | print("correct")
755 | }
756 | }
757 ? mismatch
758
759 ### Builtins ###
760
761 The usual.
762
763 | fun main() {
764 | print("Hello, world!")
765 | }
766 = Hello, world!
767
768 Some standard functions are builtin and available as toplevels.
769
770 | fun main() {
771 | a = "hello";
772 | b = len(a);
773 | while b > 0 {
774 | print(a);
775 | b = b - 1;
776 | a = substr(a, 1, b)
777 | }
778 | }
779 = hello
780 = ello
781 = llo
782 = lo
783 = o
784
785 The `+` operator is not string concatenation. `concat` is.
786
787 | fun main() {
788 | print("hello " + "world")
789 | }
790 ? type mismatch
791
792 | fun main() {
793 | print(concat("hello ", "world"))
794 | }
795 = hello world
796
797 The builtin toplevels are functions and functions need parens.
798
799 | fun main() {
800 | print "hi"
801 | }
802 ? type mismatch
803
804 Note that the above was the motivation for requiring statements to have void
805 type; if non-void exprs could be used anywhere, that would just throw away
806 the function value `print` (b/c semicolons are optional) and return 'hi'.
807
808 ### Struct Types ###
809
810 Record types. You can define them:
811
812 | struct person { name: string; age: integer }
813 | main = fun() {}
814 =
815
816 And make them.
817
818 | struct person { name: string; age: integer }
819 | main = fun() {
820 | j = make person(name:"Jake", age:23);
821 | print("ok")
822 | }
823 = ok
824
825 And extract the fields from them.
826
827 | struct person { name: string; age: integer }
828 | main = fun() {
829 | j = make person(name:"Jake", age:23);
830 | print(j.name)
831 | if j.age > 20 {
832 | print("Older than twenty")
833 | } else {
834 | print("Underage")
835 | }
836 | }
837 = Jake
838 = Older than twenty
839
840 Structs must be defined somewhere.
841
842 | main = fun() {
843 | j = make person(name:"Jake", age:23);
844 | j
845 | }
846 ? undefined
847
848 Structs need not be defined before use.
849
850 | main = fun() {
851 | j = make person(name:"Jake", age:23);
852 | j.age
853 | }
854 | struct person { name: string; age: integer }
855 = 23
856
857 Structs may not contain structs which don't exist.
858
859 | struct person { name: string; age: foobar }
860 | main = fun() { 333 }
861 ? undefined
862
863 Types must match when making a struct.
864
865 | struct person { name: string; age: integer }
866 | main = fun() {
867 | j = make person(name:"Jake", age:"Old enough to know better");
868 | j.age
869 | }
870 ? type mismatch
871
872 | struct person { name: string; age: integer }
873 | main = fun() {
874 | j = make person(name:"Jake");
875 | j.age
876 | }
877 ? argument mismatch
878
879 | struct person { name: string }
880 | main = fun() {
881 | j = make person(name:"Jake", age:23);
882 | j.age
883 | }
884 ? argument mismatch
885
886 Order of field initialization when making a struct doesn't matter.
887
888 | struct person { name: string; age: integer }
889 | main = fun() {
890 | j = make person(age: 23, name:"Jake");
891 | j.age
892 | }
893 = 23
894
895 Structs can be tested for equality. (Since structs are immutable, it
896 doesn't matter if this is structural equality or identity.)
897
898 | struct person { name: string; age: integer }
899 | main = fun() {
900 | j = make person(age: 23, name:"Jake");
901 | k = make person(name:"Jake", age: 23);
902 | j == k
903 | }
904 = True
905
906 | struct person { age: integer; name: string }
907 | main = fun() {
908 | j = make person(age: 23, name:"Jake");
909 | k = make person(age: 23, name:"John");
910 | j == k
911 | }
912 = False
913
914 | struct person { age: integer; name: string }
915 | main = fun() {
916 | j = make person(age: 23, name:"Jake");
917 | k = make person(age: 21, name:"Jake");
918 | j != k
919 | }
920 = True
921
922 Structs of two different types cannot be tested for equality.
923
924 | struct person { age: integer; name: string }
925 | struct individual { age: integer; name: string }
926 | main = fun() {
927 | j = make person(age: 23, name:"Jake");
928 | k = make individual(age: 23, name:"Jake");
929 | j == k
930 | }
931 ? mismatch
932
933 Structs cannot be compared for ordering.
934
935 | struct person { age: integer; name: string }
936 | main = fun() {
937 | j = make person(age: 23, name:"Jake");
938 | k = make person(age: 21, name:"Jake");
939 | j > k
940 | }
941 ? structs cannot be compared for order
942
943 Structs can be passed to functions.
944
945 | struct person { name: string; age: integer }
946 | fun wat(bouncer: person) { bouncer.age }
947 | main = fun() {
948 | j = make person(name:"Jake", age:23);
949 | wat(j)
950 | }
951 = 23
952
953 Structs have name equivalence, not structural.
954
955 | struct person { name: string; age: integer }
956 | struct city { name: string; population: integer }
957 | fun wat(hometown: city) { hometown }
958 | main = fun() {
959 | j = make person(name:"Jake", age:23);
960 | wat(j)
961 | }
962 ? type mismatch
963
964 Struct fields must all be unique.
965
966 | struct person { name: string; name: string }
967 | main = fun() {
968 | j = make person(name:"Jake", name:"Smith");
969 | }
970 ? defined
971
972 Values can be retrieved from structs.
973
974 | struct person { name: string; age: integer }
975 | fun age(bouncer: person) { bouncer.age }
976 | main = fun() {
977 | j = make person(name:"Jake", age:23);
978 | age(j)
979 | }
980 = 23
981
982 | struct person { name: string }
983 | fun age(bouncer: person) { bouncer.age }
984 | main = fun() {
985 | j = make person(name:"Jake");
986 | age(j)
987 | }
988 ? undefined
989
990 Different structs may have the same field name in different positions.
991
992 | struct person { name: string; age: integer }
993 | struct city { population: integer; name: string }
994 | main = fun() {
995 | j = make person(name:"Jake", age:23);
996 | w = make city(population:600000, name:"Winnipeg");
997 | print(j.name)
998 | print(w.name)
999 | }
1000 = Jake
1001 = Winnipeg
1002
1003 Can't define the same struct multiple times.
1004
1005 | struct person { name: string; age: integer }
1006 | struct person { name: string; age: string }
1007 | fun main() { 333 }
1008 ? duplicate
1009
1010 Structs may refer to themselves.
1011
1012 | struct recursive {
1013 | next: recursive;
1014 | }
1015 | fun main() { 333 }
1016 = 333
1017
1018 | struct odd {
1019 | next: even;
1020 | }
1021 | struct even {
1022 | next: odd;
1023 | }
1024 | fun main() { 333 }
1025 = 333
1026
1027 But you can't actually make one of these infinite structs.
1028
1029 | struct recursive {
1030 | next: recursive;
1031 | }
1032 | fun main() { make recursive(next:make recursive(next:"nooo")) }
1033 ? type mismatch
1034
1035 ### Union Types ###
1036
1037 Values of union type are created with the type promotion operator,
1038 `as ...`. Type promotion has a very low precedence, and can be
1039 applied to any expression.
1040
1041 The type after the `as` must be a union.
1042
1043 | fun main() {
1044 | a = 20;
1045 | b = 30;
1046 | a + b as integer
1047 | }
1048 ? bad cast
1049
1050 The type of the value being cast by the `as` must be one of the types in the union.
1051
1052 | fun main() {
1053 | a = 20;
1054 | b = 30;
1055 | a + b as string|void
1056 | }
1057 ? bad cast
1058
1059 The type after the `as` must be the type of the expression.
1060
1061 | fun main() {
1062 | a = 20;
1063 | b = 30;
1064 | c = a + b as integer|string
1065 | print("ok")
1066 | }
1067 = ok
1068
1069 Each of the individual types named in the union type must be unique.
1070
1071 | fun foo(a, b: integer|string) {
1072 | print("ok")
1073 | }
1074 | fun main() {
1075 | a = 20;
1076 | b = 30;
1077 | c = a + b as integer|integer|string
1078 | foo(a, c)
1079 | }
1080 ? bad union type
1081
1082 One can, vacuously, promote a union type to itself.
1083
1084 | fun main() {
1085 | a = 20;
1086 | b = 30;
1087 | c = a + b as integer|string
1088 | d = c as integer|string
1089 | print("ok")
1090 | }
1091 = ok
1092
1093 One can promote a union type to another union type, so long as it is a superset.
1094
1095 | fun main() {
1096 | a = 20;
1097 | b = 30;
1098 | c = a + b as integer|string
1099 | d = c as integer|string|void
1100 | print("ok")
1101 | }
1102 = ok
1103
1104 One cannot promote a union type to a union type that is not a superset.
1105
1106 | fun main() {
1107 | a = 20;
1108 | b = 30;
1109 | c = a + b as integer|string
1110 | d = c as integer|void
1111 | print("ok")
1112 | }
1113 ? bad cast
1114
1115 Values of union type can be passed to functions.
1116
1117 | fun foo(a, b: integer|string) {
1118 | a + 1
1119 | }
1120 | main = fun() {
1121 | a = 0;
1122 | a = foo(a, 333 as integer|string);
1123 | a = foo(a, "hiya" as integer|string);
1124 | a
1125 | }
1126 = 2
1127
1128 Order of types in a union doesn't matter.
1129
1130 | fun foo(a, b: integer|string) {
1131 | a + 1
1132 | }
1133 | main = fun() {
1134 | a = 0;
1135 | a = foo(a, 333 as integer|string);
1136 | a = foo(a, "hiya" as string|integer);
1137 | a
1138 | }
1139 = 2
1140
1141 Trivial use of `typecase`.
1142
1143 | main = fun() {
1144 | a = 333 as integer|string;
1145 | typecase a is integer {
1146 | print("int")
1147 | };
1148 | }
1149 = int
1150
1151 Inside a `typecase` the variable can be used as a value of
1152 the determined type.
1153
1154 | main = fun() {
1155 | a = 333 as integer|string;
1156 | typecase a is integer {
1157 | print(str(a))
1158 | };
1159 | typecase a is string {
1160 | print(a)
1161 | };
1162 | }
1163 = 333
1164
1165 The `typecase` construct can operate on the "right" type of a union.
1166
1167 | fun foo(a, b: integer|string) {
1168 | r = a;
1169 | typecase b is integer {
1170 | r = r + b;
1171 | };
1172 | typecase b is string {
1173 | r = r + len(b);
1174 | };
1175 | r
1176 | }
1177 | main = fun() {
1178 | a = 0;
1179 | a = foo(a, 333 as integer|string);
1180 | a = foo(a, "hiya" as integer|string);
1181 | a
1182 | }
1183 = 337
1184
1185 The expression in a `typecase` must be a variable.
1186
1187 | main = fun() {
1188 | a = 333 as integer|string;
1189 | typecase 333 is integer {
1190 | print("what?")
1191 | };
1192 | }
1193 ? identifier
1194
1195 The expression in a `typecase` can be an argument to the function in
1196 which the `typecase` occurs.
1197
1198 | fun wat(j: integer|string) {
1199 | typecase j is integer {
1200 | print("integer")
1201 | };
1202 | }
1203 | main = fun() {
1204 | wat(444 as integer|string)
1205 | }
1206 = integer
1207
1208 The expression in a `typecase` cannot effectively be a global, as globals
1209 must be literals and there is no way (right now) to make a literal of union
1210 type.
1211
1212 Inside a `typecase` the variable cannot be updated.
1213
1214 | main = fun() {
1215 | a = 333 as integer|string;
1216 | typecase a is integer {
1217 | a = 700;
1218 | };
1219 | }
1220 ? cannot assign
1221
1222 The union can include void.
1223
1224 | main = fun() {
1225 | j = null as void|integer;
1226 | typecase j is void {
1227 | print("nothing there")
1228 | };
1229 | }
1230 = nothing there
1231
1232 ### Struct Types + Union Types ###
1233
1234 Union types may be used to make fields of a struct "nullable", so that
1235 you can in actuality create recursive, but finite, data structures.
1236
1237 | struct list {
1238 | value: string;
1239 | next: list|integer;
1240 | }
1241 | main = fun() {
1242 | l = make list(
1243 | value: "first",
1244 | next: make list(
1245 | value: "second",
1246 | next:0 as list|integer
1247 | ) as list|integer)
1248 | s = l.next
1249 | typecase s is list {
1250 | print(s.value)
1251 | }
1252 | }
1253 = second
1254
1255 You may want to use helper functions to hide this ugliness.
1256
1257 | struct list {
1258 | value: string;
1259 | next: list|void;
1260 | }
1261 |
1262 | fun empty() {
1263 | return null as list|void
1264 | }
1265 |
1266 | fun cons(v: string, l: list|void) {
1267 | make list(value:v, next:l) as list|void
1268 | }
1269 |
1270 | fun nth(n, l: list|void) {
1271 | u = l;
1272 | v = u;
1273 | k = n;
1274 | while k > 1 {
1275 | typecase u is void { break; }
1276 | typecase u is list { v = u.next; }
1277 | u = v;
1278 | k = k - 1;
1279 | }
1280 | return u
1281 | }
1282 |
1283 | main = fun() {
1284 | l = cons("first", cons("second", cons("third", empty())));
1285 | h = nth(2, l);
1286 | typecase h is list { print(h.value); }
1287 | }
1288 = second
1289
1290 And in fact, you can restrict the union types to smaller sets to
1291 better indicate the allowable types of the functions. For example,
1292 `cons` always returns a list, so that should be its return type,
1293 not `list|void`. Likewise, `nth` requires a list. In this way we
1294 can implement some of the "Parse, don't Validate" approach.
1295
1296 | struct list {
1297 | value: string;
1298 | next: list|void;
1299 | }
1300 |
1301 | fun cons(v: string, l: list) {
1302 | make list(value:v, next:l as list|void)
1303 | }
1304 |
1305 | fun singleton(v: string) {
1306 | make list(value:v, next:null as list|void)
1307 | }
1308 |
1309 | fun nth(n, l: list) {
1310 | u = l as list|void;
1311 | v = u;
1312 | k = n;
1313 | while k > 1 {
1314 | typecase u is void { break; }
1315 | typecase u is list { v = u.next; }
1316 | u = v;
1317 | k = k - 1;
1318 | }
1319 | return u
1320 | }
1321 |
1322 | main = fun() {
1323 | l = cons("first", cons("second", singleton("third")));
1324 | h = nth(2, l);
1325 | typecase h is list { print(h.value); }
1326 | }
1327 = second
1328
1329 Structs may be empty.
1330
1331 | struct red { }
1332 | fun show(color: red) {
1333 | print("hi")
1334 | }
1335 | main = fun() {
1336 | show(make red());
1337 | }
1338 = hi
1339
1340 In combination with unions, this lets us create "typed enums".
1341
1342 | struct red { }
1343 | struct green { }
1344 | struct blue { }
1345 | fun show(color: red|green|blue) {
1346 | typecase color is red { print("red"); }
1347 | typecase color is green { print("green"); }
1348 | typecase color is blue { print("blue"); }
1349 | }
1350 | main = fun() {
1351 | show(make red() as red|green|blue);
1352 | show(make blue() as red|green|blue);
1353 | }
1354 = red
1355 = blue
101 [Falderal]: https://catseye.tc/node/Falderal
102 [Parse, don't validate]: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
0 Castile Design Notes
1 ====================
2
3 (These are the original notes from the original README.)
4
5 Unlike most of my programming languages, there is nothing that could really
6 be described as innovative or experimental or even particularly unusual
7 about Castile. It is not a particularly comfortable programming experience,
8 often forcing the programmer to be explicit and verbose.
9
10 The reference implementation is slightly less unremarkable than the language
11 itself, if only for the fact that it compiles to four different target
12 languages: Javascript, Ruby, a hypothetical stack machine called
13 "stackmac" (a stackmac emulator ships with this distribution,) and (coming
14 soon) C.
15
16 Castile's influences might include:
17
18 * **C**: Most of Castile's syntax follows C, but it is generally more
19 permissive (semicolons are optional, types of local variables and return
20 types for functions do not have to be declared, etc.) It has a type
21 system (where `struct`s are the only types with name equivalence) which
22 can be applied statically. It has function values, but not closures.
23
24 * **Rust**: There is a union type, to which values must be explicitly
25 promoted (with `as`) and extracted (with `typecase ... is`.) This is
26 like Rust's `Enum`, which is (to quote its tutorial) "much like the
27 'tagged union' pattern in C, but with better static guarantees." Along
28 with structs, this provides something similar to algebraic data typing,
29 as seen in languages such as Haskell, Scala, etc.
30
31 * **Eightebed**: A few years back I realized that pointers that can
32 assume a null value are really a variant type, like Haskell's `Maybe`.
33 Of course, in most languages with pointers, the property of being null
34 isn't captured by the type; you can go ahead and dereference a pointer
35 in C or Java, whether it's valid or not. In Castile, this is captured
36 with a union type which includes `void`, and `typecase` generalizes
37 Eightebed's `ifvalid`.
38
39 * **Python**: The first time a local variable is assigned counts as its
40 declaration as a local.
41
42 * **Ruby**: The last expression in a function body is the return value
43 of that function; no explicit `return` is needed there. (But unlike
44 Ruby, and more like Pascal or linted C, all *other* expressions in
45 statement position within a block must have void type.)
46
47 * **Erlang** (or any other purely functional language): There are no
48 language-level pointers; sharing, if it happens at all, must be
49 orchestrated by the implementation. Global variables and function
50 arguments are not mutable, and neither are the fields of structs.
51 (But unlike Erlang, local variables *are* mutable.)
52
53 Some lines of research underneath all this are, if all we have is a relatively
54 crude language, but we make it typesafe and give it a slightly nicer type
55 system, does it suffice to make programming tolerable? Do tolerable ways of
56 managing memory without a full garbage collector present themselves? Does
57 having a simple compiler which can be target many backends provide any
58 advantages?
59
60 Also unlike most of my programming languages (with the exceptions of ILLGOL
61 and Bhuna), Castile was largely "designed by building" -- I wrote an
62 interpreter, and the language it happens to accept, I called Castile.
63 I wrote the interpreter in a very short span of time; most of it was done
64 within 24 hours of starting (but consider that I ripped off some of the
65 scanning/parsing code from ALPACA.) A few days later, I extended the
66 implementation to also allow compiling to Javascript, and a few days after
67 that, I added a Ruby backend (why not, eh?), and over the next few days,
68 the stackmac backend and emulator.
69
70 This document contains what is as close as there is to a specification of
71 the language, in the form of a Falderal test suite. The interpreter and all
72 compilers pass all the tests, but there are known shortcomings in at least
73 the compilers (no name mangling, etc.)
74
75 The `eg` directory contains a few example Castile programs, including a
76 string tokenizer.
77
78 One area where the Castile implementation is not entirely unremarkable is
79 that the typechecker is not required to be run. Unchecked Castile is
80 technically a different language from Castile; there are Castile programs
81 which would result in an error, where the Unchecked Castile program would
82 *not* (because it never executes the part of the program with a bad type.)
83 However, Unchecked Castile programs should be otherwise well-behaved;
84 any attempt to execute code which would have resulted in a type failure,
85 will result in a crash. Note, however, that this only applies to the
86 evaluator, not any of the compiler backends. Compiling Unchecked Castile
87 will simply not work (the backend will crash when it can't see any types.)
0 Grammar of Castile
1 ==================
2
3 This is an EBNF grammar for Castile.
4
5 Program ::= {Defn [";"]}.
6 Defn ::= "fun" ident "(" [Arg {"," Arg}] ")" Body
7 | "struct" ident "{" {ident ":" TExpr [";"]} "}"
8 | ident (":" TExpr0 | "=" Literal).
9 Arg ::= ident [":" TExpr1].
10 Body ::= "{" {Stmt [";"]} "}".
11 Stmt ::= "while" Expr0 Block
12 | "typecase" ident "is" TExpr0 Block
13 | "do" Expr0
14 | "return" Expr0
15 | If
16 | Expr0.
17 Block ::= "{" {Stmt [";"]} "}".
18 If ::= "if" Expr0 Block ["else" (Block | If)].
19 Expr0 ::= Expr1 {("and" | "or") Expr1} ["as" TExpr0].
20 Expr1 ::= Expr2 {(">" | ">=" | "<" | "<=" | "==" | "!=") Expr2}.
21 Expr2 ::= Expr3 {("+" | "-") Expr3}.
22 Expr3 ::= Expr4 {("*" | "/") Expr4}.
23 Expr4 ::= Expr5 {"(" [Expr0 {"," Expr0}] ")" | "." ident}.
24 Expr5 ::= "make" ident "(" [ident ":" Expr0 {"," ident ":" Expr0}] ")"
25 | "(" Expr0 ")"
26 | "not" Expr1
27 | Literal
28 | ident ["=" Expr0].
29 Literal ::= strlit
30 | ["-"] intlit
31 | "true" | "false" | "null"
32 | "fun" "(" [Arg {"," Arg}] ")" Body.
33 TExpr0 ::= TExpr1 [{"," TExpr1} "->" TExpr1].
34 TExpr1 ::= TExpr2 {"|" TExpr2}.
35 TExpr2 ::= "integer"
36 | "boolean"
37 | "void"
38 | "(" TExpr0 ")"
39 | ident.
2323 APPLIANCES="$APPLIANCES tests/appliances/castile-c-c.md"
2424 fi
2525
26 falderal $APPLIANCES README.md
26 falderal $APPLIANCES tests/Castile.md
2727 RESULT=$?
2828 rm -f foo.* a.out
2929 exit $RESULT
0 Castile
1 =======
2
3 This is a test suite for Castile, written in Falderal format.
4
5 While it cannot serve as a complete specification, it comes as
6 close as anything currently does to a specification for Castilde.
7
8 -> Tests for functionality "Run Castile Program"
9
10 ### Rudiments ###
11
12 Minimal correct program.
13
14 | fun main() {}
15 =
16
17 A program may evaluate to a value.
18
19 | fun main() { 160 }
20 = 160
21
22 The function named `main` is the one that is evaluated when the
23 program is run.
24
25 | fun foobar(a, b, c) { 100 }
26 | fun main() { 120 }
27 | fun f() { 140 }
28 = 120
29
30 `main` should have no formal arguments.
31
32 | fun main(a, b, c) {
33 | 120
34 | }
35 ? type mismatch
36
37 But other functions may.
38
39 | fun foobar(a, b) { b }
40 | fun main() { foobar(100, 200) }
41 = 200
42
43 Defined function names must be unique.
44
45 | fun dup() { 1 }
46 | fun dup() { 2 }
47 ? duplicate
48
49 Formal argument names must be unique.
50
51 | fun f(g, g) {}
52 | fun main() { 1 }
53 ? defined
54
55 Functions must be defined before they are referenced.
56
57 | fun main() { f(7) }
58 | fun f(g) { g }
59 ? undefined
60
61 Either that, or forward-declared.
62
63 | f : integer -> integer
64 | fun main() { f(7) }
65 | fun f(g) { g * 2 }
66 = 14
67
68 If forward-declared, types must match.
69
70 | f : integer -> string
71 | fun main() { f(7) }
72 | fun f(g) { g * 2 }
73 ? type mismatch
74
75 Arguments must match...
76
77 | fun f(g, h) { g * 2 + h * 2 }
78 | fun main() { f(7) }
79 ? argument mismatch
80
81 | fun f(g, h) { g * 2 + h * 2 }
82 | fun main() { f(7,8,9) }
83 ? argument mismatch
84
85 ### Statements ###
86
87 Statements are commands that have the type void and are executed for their
88 side-effects. So, in general, statements may not be expressions. The
89 exception is that the last statement in a block may be an expression; the
90 result of that expression is the value of the block.
91
92 | fun main() {
93 | 20 * 8
94 | }
95 = 160
96
97 | fun main() {
98 | 20 + 3 * 8;
99 | 20 * 8
100 | }
101 ? type mismatch
102
103 An `if`/`else` lets you make decisions.
104
105 | fun main() {
106 | a = 0;
107 | if 3 > 2 {
108 | a = 70
109 | } else {
110 | a = 80
111 | }
112 | a
113 | }
114 = 70
115
116 An `if` need not have an `else`.
117
118 | fun main() {
119 | a = 60
120 | if 3 > 2 {
121 | a = 70
122 | }
123 | a
124 | }
125 = 70
126
127 `if` always typechecks to void, one branch or two.
128
129 | fun main() {
130 | a = 60
131 | if 3 > 2 {
132 | a = 70
133 | }
134 | }
135 =
136
137 | fun main() {
138 | a = 60
139 | if 3 > 2 {
140 | a = 70
141 | } else {
142 | a = 90
143 | }
144 | }
145 =
146
147 If an `if` does have an `else`, the part after `else` must be either a block
148 (already shown) or another `if`.
149
150 | fun main() {
151 | if 2 > 3 {
152 | return 60
153 | } else if 4 > 5 {
154 | return 0
155 | } else {
156 | return 1
157 | }
158 | }
159 = 1
160
161 No dangling else problem.
162
163 | fun main() {
164 | if 2 > 3 {
165 | return 60
166 | } else if 4 < 5 {
167 | return 99
168 | } else {
169 | return 1
170 | }
171 | }
172 = 99
173
174 `while` loops.
175
176 | fun main() {
177 | a = 0 b = 4
178 | while b > 0 {
179 | a = a + b
180 | b = b - 1
181 | }
182 | a
183 | }
184 = 10
185
186 A `while` itself has void type.
187
188 | fun main() {
189 | a = 0; b = 4;
190 | while b > 0 {
191 | a = a + b;
192 | b = b - 1;
193 | }
194 | }
195 =
196
197 `break` may be used to prematurely exit a `while`.
198
199 | fun main() {
200 | a = 0; b = 0;
201 | while true {
202 | a = a + b;
203 | b = b + 1;
204 | if (b > 4) { break; }
205 | }
206 | a
207 | }
208 = 10
209
210 ### Expressions ###
211
212 Precedence.
213
214 | fun main() {
215 | 2 + 3 * 4 /* not 20 */
216 | }
217 = 14
218
219 Unary negation.
220
221 | fun main() {
222 | -3
223 | }
224 = -3
225
226 | fun main() {
227 | 2+-5
228 | }
229 = -3
230
231 Minus sign must be right in front of a number.
232
233 | fun main() {
234 | -(4)
235 | }
236 ? Expected
237
238 Unary not.
239
240 | fun main() {
241 | not (4 > 3)
242 | }
243 = False
244
245 Precedence of unary not.
246
247 | fun main() {
248 | not true or true
249 | }
250 = True
251
252 | fun main() {
253 | not 3 > 4
254 | }
255 = True
256
257 ### Local Variables ###
258
259 Local variables.
260
261 | fun main() {
262 | a = 6;
263 | b = 7;
264 | a + b
265 | }
266 = 13
267
268 Local variables can be assigned functions.
269
270 | fun ancillary(x) { x * 2 }
271 | fun main() {
272 | a = ancillary;
273 | a(7)
274 | }
275 = 14
276
277 Local variables can be assigned.
278
279 | fun main() {
280 | a = 6;
281 | a = a + 12;
282 | a
283 | }
284 = 18
285
286 | fun main() {
287 | a = 6;
288 | z = 99;
289 | a
290 | }
291 = 6
292
293 | fun main() {
294 | z = 6;
295 | a
296 | }
297 ? undefined
298
299 Local variables cannot occur in expressions until they are defined by an
300 initial assignment.
301
302 | fun main() {
303 | z = a * 10;
304 | a = 10;
305 | z
306 | }
307 ? undefined
308
309 A local variables may not be defined inside an `if` or `while` or `typecase`
310 block, as it might not be executed.
311
312 | fun main() {
313 | if (4 > 5) {
314 | a = 10;
315 | } else {
316 | b = 11;
317 | }
318 | b
319 | }
320 ? within control
321
322 | fun main() {
323 | b = false;
324 | while b {
325 | a = 10;
326 | }
327 | a
328 | }
329 ? within control
330
331 | fun main() {
332 | a = 55 as integer|string;
333 | typecase a is string {
334 | b = 7
335 | }
336 | a
337 | }
338 ? within control
339
340 Assignment, though it syntactically may occur in expressions, has a type of
341 void, so it can only really happen at the statement level.
342
343 | fun main() {
344 | a = 0; b = 0;
345 | a = b = 9;
346 | }
347 ? type mismatch
348
349 Variables in upper scopes may be modified.
350
351 | fun main() {
352 | a = 0
353 | if 3 > 2 {
354 | a = 4;
355 | }
356 | a
357 | }
358 = 4
359
360 ### Non-local Values ###
361
362 Literals may appear at the toplevel. Semicolons are optional at toplevel.
363
364 | factor = 5;
365 | fun main() {
366 | 6 * factor
367 | }
368 = 30
369
370 Toplevel literals may not be updated. (And thus
371
372 | factor = 5
373 | fun main() {
374 | factor = 7
375 | }
376 ? shadows
377
378 Toplevel literals may be function literals (the syntax we've been using is just sugar.)
379
380 | main = fun() {
381 | 7
382 | }
383 = 7
384
385 Truth and falsehood are builtin toplevels.
386
387 | fun main() {
388 | true or false
389 | }
390 = True
391
392 | fun main() {
393 | false and true
394 | }
395 = False
396
397 So is `null`, which is the single value of `void` type.
398
399 | fun wat(x: void) { 3 }
400 | fun main() {
401 | wat(null)
402 | }
403 = 3
404
405 ### More on Functions ###
406
407 Function arguments may not be updated.
408
409 | fun foo(x) {
410 | x = x + 14;
411 | x
412 | }
413 | fun main() {
414 | foo(7)
415 | }
416 ? shadows
417
418 Factorial can be computed.
419
420 | factorial : integer -> integer
421 | fun factorial(a) {
422 | if a == 0 {
423 | return 1
424 | } else {
425 | return a * factorial(a - 1)
426 | }
427 | }
428 | fun main() {
429 | factorial(6)
430 | }
431 = 720
432
433 Literal functions.
434
435 | fun main() {
436 | inc = fun(x) { x + 1 };
437 | inc(7)
438 | }
439 = 8
440
441 | fun main() {
442 | fun(x){ x + 1 }(9)
443 | }
444 = 10
445
446 | fun main() {
447 | a = 99;
448 | a = fun(x){ x + 1 }(9);
449 | a
450 | }
451 = 10
452
453 Literal functions can have local variables, loops, etc.
454
455 | fun main() {
456 | z = 99;
457 | z = fun(x) {
458 | a = x; b = x;
459 | while a > 0 {
460 | b = b + a; a = a - 1;
461 | }
462 | return b
463 | }(9);
464 | z
465 | }
466 = 54
467
468 Literal functions can define other literal functions...
469
470 | fun main() {
471 | fun(x){ fun(y){ fun(z){ z + 1 } } }(4)(4)(10)
472 | }
473 = 11
474
475 Literal functions can access globals.
476
477 | oid = 19
478 | fun main() {
479 | fun(x){ x + oid }(11);
480 | }
481 = 30
482
483 Literal functions cannot access variables declared in enclosing scopes.
484
485 | fun main() {
486 | oid = 19;
487 | fun(x){ x + oid }(11);
488 | }
489 ? undefined
490
491 Literal functions cannot access arguments declared in enclosing scopes.
492
493 | fun main() {
494 | fun(x){ fun(y){ fun(z){ y + 1 } } }(4)(4)(10)
495 | }
496 ? undefined
497
498 Functions can be passed to functions and returned from functions.
499
500 | fun doubble(x) { x * 2 }
501 | fun triple(x) { x * 3 }
502 | fun apply_and_add_one(f: (integer -> integer), x) { f(x) + 1 }
503 | fun sellect(a) { if a > 10 { return doubble } else { return triple } }
504 | fun main() {
505 | t = sellect(5);
506 | d = sellect(15);
507 | p = t(10);
508 | apply_and_add_one(d, p)
509 | }
510 = 61
511
512 To overcome the syntactic ambiguity with commas, function types
513 in function definitions must be in parens.
514
515 | fun add(x, y) { x + y }
516 | fun mul(x, y) { x * y }
517 | fun do_it(f: (integer, integer -> integer), g) {
518 | f(3, g)
519 | }
520 | fun main() {
521 | do_it(mul, 4) - do_it(add, 4)
522 | }
523 = 5
524
525 `return` may be used to prematurely return a value from a function.
526
527 | fun foo(y) {
528 | x = y
529 | while x > 0 {
530 | if x < 5 {
531 | return x;
532 | }
533 | x = x - 1;
534 | }
535 | 17
536 | }
537 | fun main() {
538 | foo(10) + foo(0)
539 | }
540 = 21
541
542 Type of value returned must jibe with value of function's block.
543
544 | fun foo(x) {
545 | return "string";
546 | 17
547 | }
548 | fun main() {
549 | foo(10) + foo(0)
550 | }
551 ? type mismatch
552
553 Type of value returned must jibe with other return statements.
554
555 | fun foo(x) {
556 | if x > 0 {
557 | return "string";
558 | } else {
559 | return 17
560 | }
561 | }
562 | fun main() {
563 | foo(10) + foo(0)
564 | }
565 ? type mismatch
566
567 ### Equality ###
568
569 Equality, inequality, boolean operators.
570
571 | fun main() {
572 | if 15 == 15 and ((15 != 14) or false) {
573 | print("struth")
574 | }
575 | }
576 = struth
577
578 | fun main() {
579 | if "five" == "five" and (("six" != "seven") or false) {
580 | print("struth")
581 | }
582 | }
583 = struth
584
585 Equality cannot be checked between two values of different types.
586
587 | fun main() {
588 | if 15 == "fifteen" {
589 | print("wat")
590 | }
591 | }
592 ? mismatch
593
594 | fun main() {
595 | if 15 != "fifteen" {
596 | print("wat")
597 | }
598 | }
599 ? mismatch
600
601 Equality can be checked between unions. (TODO)
602
603 /| fun main() {
604 /| a = 40 as string|integer
605 /| b = 40 as string|integer
606 /| if a == b {
607 /| print("it is")
608 /| }
609 /| }
610 /= ok
611
612 | fun main() {
613 | a = 40 as string|integer
614 | b = "beep" as string|integer
615 | if a != b {
616 | print("correct")
617 | }
618 | }
619 = correct
620
621 Equality cannot be tested between two disjoint unions.
622
623 | fun main() {
624 | a = 40 as string|integer
625 | b = 40 as integer|void
626 | if a == b {
627 | print("correct")
628 | }
629 | }
630 ? mismatch
631
632 ### Builtins ###
633
634 The usual.
635
636 | fun main() {
637 | print("Hello, world!")
638 | }
639 = Hello, world!
640
641 Some standard functions are builtin and available as toplevels.
642
643 | fun main() {
644 | a = "hello";
645 | b = len(a);
646 | while b > 0 {
647 | print(a);
648 | b = b - 1;
649 | a = substr(a, 1, b)
650 | }
651 | }
652 = hello
653 = ello
654 = llo
655 = lo
656 = o
657
658 The `+` operator is not string concatenation. `concat` is.
659
660 | fun main() {
661 | print("hello " + "world")
662 | }
663 ? type mismatch
664
665 | fun main() {
666 | print(concat("hello ", "world"))
667 | }
668 = hello world
669
670 The builtin toplevels are functions and functions need parens.
671
672 | fun main() {
673 | print "hi"
674 | }
675 ? type mismatch
676
677 Note that the above was the motivation for requiring statements to have void
678 type; if non-void exprs could be used anywhere, that would just throw away
679 the function value `print` (b/c semicolons are optional) and return 'hi'.
680
681 ### Struct Types ###
682
683 Record types. You can define them:
684
685 | struct person { name: string; age: integer }
686 | main = fun() {}
687 =
688
689 And make them.
690
691 | struct person { name: string; age: integer }
692 | main = fun() {
693 | j = make person(name:"Jake", age:23);
694 | print("ok")
695 | }
696 = ok
697
698 And extract the fields from them.
699
700 | struct person { name: string; age: integer }
701 | main = fun() {
702 | j = make person(name:"Jake", age:23);
703 | print(j.name)
704 | if j.age > 20 {
705 | print("Older than twenty")
706 | } else {
707 | print("Underage")
708 | }
709 | }
710 = Jake
711 = Older than twenty
712
713 Structs must be defined somewhere.
714
715 | main = fun() {
716 | j = make person(name:"Jake", age:23);
717 | j
718 | }
719 ? undefined
720
721 Structs need not be defined before use.
722
723 | main = fun() {
724 | j = make person(name:"Jake", age:23);
725 | j.age
726 | }
727 | struct person { name: string; age: integer }
728 = 23
729
730 Structs may not contain structs which don't exist.
731
732 | struct person { name: string; age: foobar }
733 | main = fun() { 333 }
734 ? undefined
735
736 Types must match when making a struct.
737
738 | struct person { name: string; age: integer }
739 | main = fun() {
740 | j = make person(name:"Jake", age:"Old enough to know better");
741 | j.age
742 | }
743 ? type mismatch
744
745 | struct person { name: string; age: integer }
746 | main = fun() {
747 | j = make person(name:"Jake");
748 | j.age
749 | }
750 ? argument mismatch
751
752 | struct person { name: string }
753 | main = fun() {
754 | j = make person(name:"Jake", age:23);
755 | j.age
756 | }
757 ? argument mismatch
758
759 Order of field initialization when making a struct doesn't matter.
760
761 | struct person { name: string; age: integer }
762 | main = fun() {
763 | j = make person(age: 23, name:"Jake");
764 | j.age
765 | }
766 = 23
767
768 Structs can be tested for equality. (Since structs are immutable, it
769 doesn't matter if this is structural equality or identity.)
770
771 | struct person { name: string; age: integer }
772 | main = fun() {
773 | j = make person(age: 23, name:"Jake");
774 | k = make person(name:"Jake", age: 23);
775 | j == k
776 | }
777 = True
778
779 | struct person { age: integer; name: string }
780 | main = fun() {
781 | j = make person(age: 23, name:"Jake");
782 | k = make person(age: 23, name:"John");
783 | j == k
784 | }
785 = False
786
787 | struct person { age: integer; name: string }
788 | main = fun() {
789 | j = make person(age: 23, name:"Jake");
790 | k = make person(age: 21, name:"Jake");
791 | j != k
792 | }
793 = True
794
795 Structs of two different types cannot be tested for equality.
796
797 | struct person { age: integer; name: string }
798 | struct individual { age: integer; name: string }
799 | main = fun() {
800 | j = make person(age: 23, name:"Jake");
801 | k = make individual(age: 23, name:"Jake");
802 | j == k
803 | }
804 ? mismatch
805
806 Structs cannot be compared for ordering.
807
808 | struct person { age: integer; name: string }
809 | main = fun() {
810 | j = make person(age: 23, name:"Jake");
811 | k = make person(age: 21, name:"Jake");
812 | j > k
813 | }
814 ? structs cannot be compared for order
815
816 Structs can be passed to functions.
817
818 | struct person { name: string; age: integer }
819 | fun wat(bouncer: person) { bouncer.age }
820 | main = fun() {
821 | j = make person(name:"Jake", age:23);
822 | wat(j)
823 | }
824 = 23
825
826 Structs have name equivalence, not structural.
827
828 | struct person { name: string; age: integer }
829 | struct city { name: string; population: integer }
830 | fun wat(hometown: city) { hometown }
831 | main = fun() {
832 | j = make person(name:"Jake", age:23);
833 | wat(j)
834 | }
835 ? type mismatch
836
837 Struct fields must all be unique.
838
839 | struct person { name: string; name: string }
840 | main = fun() {
841 | j = make person(name:"Jake", name:"Smith");
842 | }
843 ? defined
844
845 Values can be retrieved from structs.
846
847 | struct person { name: string; age: integer }
848 | fun age(bouncer: person) { bouncer.age }
849 | main = fun() {
850 | j = make person(name:"Jake", age:23);
851 | age(j)
852 | }
853 = 23
854
855 | struct person { name: string }
856 | fun age(bouncer: person) { bouncer.age }
857 | main = fun() {
858 | j = make person(name:"Jake");
859 | age(j)
860 | }
861 ? undefined
862
863 Different structs may have the same field name in different positions.
864
865 | struct person { name: string; age: integer }
866 | struct city { population: integer; name: string }
867 | main = fun() {
868 | j = make person(name:"Jake", age:23);
869 | w = make city(population:600000, name:"Winnipeg");
870 | print(j.name)
871 | print(w.name)
872 | }
873 = Jake
874 = Winnipeg
875
876 Can't define the same struct multiple times.
877
878 | struct person { name: string; age: integer }
879 | struct person { name: string; age: string }
880 | fun main() { 333 }
881 ? duplicate
882
883 Structs may refer to themselves.
884
885 | struct recursive {
886 | next: recursive;
887 | }
888 | fun main() { 333 }
889 = 333
890
891 | struct odd {
892 | next: even;
893 | }
894 | struct even {
895 | next: odd;
896 | }
897 | fun main() { 333 }
898 = 333
899
900 But you can't actually make one of these infinite structs.
901
902 | struct recursive {
903 | next: recursive;
904 | }
905 | fun main() { make recursive(next:make recursive(next:"nooo")) }
906 ? type mismatch
907
908 ### Union Types ###
909
910 Values of union type are created with the type promotion operator,
911 `as ...`. Type promotion has a very low precedence, and can be
912 applied to any expression.
913
914 The type after the `as` must be a union.
915
916 | fun main() {
917 | a = 20;
918 | b = 30;
919 | a + b as integer
920 | }
921 ? bad cast
922
923 The type of the value being cast by the `as` must be one of the types in the union.
924
925 | fun main() {
926 | a = 20;
927 | b = 30;
928 | a + b as string|void
929 | }
930 ? bad cast
931
932 The type after the `as` must be the type of the expression.
933
934 | fun main() {
935 | a = 20;
936 | b = 30;
937 | c = a + b as integer|string
938 | print("ok")
939 | }
940 = ok
941
942 Each of the individual types named in the union type must be unique.
943
944 | fun foo(a, b: integer|string) {
945 | print("ok")
946 | }
947 | fun main() {
948 | a = 20;
949 | b = 30;
950 | c = a + b as integer|integer|string
951 | foo(a, c)
952 | }
953 ? bad union type
954
955 One can, vacuously, promote a union type to itself.
956
957 | fun main() {
958 | a = 20;
959 | b = 30;
960 | c = a + b as integer|string
961 | d = c as integer|string
962 | print("ok")
963 | }
964 = ok
965
966 One can promote a union type to another union type, so long as it is a superset.
967
968 | fun main() {
969 | a = 20;
970 | b = 30;
971 | c = a + b as integer|string
972 | d = c as integer|string|void
973 | print("ok")
974 | }
975 = ok
976
977 One cannot promote a union type to a union type that is not a superset.
978
979 | fun main() {
980 | a = 20;
981 | b = 30;
982 | c = a + b as integer|string
983 | d = c as integer|void
984 | print("ok")
985 | }
986 ? bad cast
987
988 Values of union type can be passed to functions.
989
990 | fun foo(a, b: integer|string) {
991 | a + 1
992 | }
993 | main = fun() {
994 | a = 0;
995 | a = foo(a, 333 as integer|string);
996 | a = foo(a, "hiya" as integer|string);
997 | a
998 | }
999 = 2
1000
1001 Order of types in a union doesn't matter.
1002
1003 | fun foo(a, b: integer|string) {
1004 | a + 1
1005 | }
1006 | main = fun() {
1007 | a = 0;
1008 | a = foo(a, 333 as integer|string);
1009 | a = foo(a, "hiya" as string|integer);
1010 | a
1011 | }
1012 = 2
1013
1014 Trivial use of `typecase`.
1015
1016 | main = fun() {
1017 | a = 333 as integer|string;
1018 | typecase a is integer {
1019 | print("int")
1020 | };
1021 | }
1022 = int
1023
1024 Inside a `typecase` the variable can be used as a value of
1025 the determined type.
1026
1027 | main = fun() {
1028 | a = 333 as integer|string;
1029 | typecase a is integer {
1030 | print(str(a))
1031 | };
1032 | typecase a is string {
1033 | print(a)
1034 | };
1035 | }
1036 = 333
1037
1038 The `typecase` construct can operate on the "right" type of a union.
1039
1040 | fun foo(a, b: integer|string) {
1041 | r = a;
1042 | typecase b is integer {
1043 | r = r + b;
1044 | };
1045 | typecase b is string {
1046 | r = r + len(b);
1047 | };
1048 | r
1049 | }
1050 | main = fun() {
1051 | a = 0;
1052 | a = foo(a, 333 as integer|string);
1053 | a = foo(a, "hiya" as integer|string);
1054 | a
1055 | }
1056 = 337
1057
1058 The expression in a `typecase` must be a variable.
1059
1060 | main = fun() {
1061 | a = 333 as integer|string;
1062 | typecase 333 is integer {
1063 | print("what?")
1064 | };
1065 | }
1066 ? identifier
1067
1068 The expression in a `typecase` can be an argument to the function in
1069 which the `typecase` occurs.
1070
1071 | fun wat(j: integer|string) {
1072 | typecase j is integer {
1073 | print("integer")
1074 | };
1075 | }
1076 | main = fun() {
1077 | wat(444 as integer|string)
1078 | }
1079 = integer
1080
1081 The expression in a `typecase` cannot effectively be a global, as globals
1082 must be literals and there is no way (right now) to make a literal of union
1083 type.
1084
1085 Inside a `typecase` the variable cannot be updated.
1086
1087 | main = fun() {
1088 | a = 333 as integer|string;
1089 | typecase a is integer {
1090 | a = 700;
1091 | };
1092 | }
1093 ? cannot assign
1094
1095 The union can include void.
1096
1097 | main = fun() {
1098 | j = null as void|integer;
1099 | typecase j is void {
1100 | print("nothing there")
1101 | };
1102 | }
1103 = nothing there
1104
1105 ### Struct Types + Union Types ###
1106
1107 Union types may be used to make fields of a struct "nullable", so that
1108 you can in actuality create recursive, but finite, data structures.
1109
1110 | struct list {
1111 | value: string;
1112 | next: list|integer;
1113 | }
1114 | main = fun() {
1115 | l = make list(
1116 | value: "first",
1117 | next: make list(
1118 | value: "second",
1119 | next:0 as list|integer
1120 | ) as list|integer)
1121 | s = l.next
1122 | typecase s is list {
1123 | print(s.value)
1124 | }
1125 | }
1126 = second
1127
1128 You may want to use helper functions to hide this ugliness.
1129
1130 | struct list {
1131 | value: string;
1132 | next: list|void;
1133 | }
1134 |
1135 | fun empty() {
1136 | return null as list|void
1137 | }
1138 |
1139 | fun cons(v: string, l: list|void) {
1140 | make list(value:v, next:l) as list|void
1141 | }
1142 |
1143 | fun nth(n, l: list|void) {
1144 | u = l;
1145 | v = u;
1146 | k = n;
1147 | while k > 1 {
1148 | typecase u is void { break; }
1149 | typecase u is list { v = u.next; }
1150 | u = v;
1151 | k = k - 1;
1152 | }
1153 | return u
1154 | }
1155 |
1156 | main = fun() {
1157 | l = cons("first", cons("second", cons("third", empty())));
1158 | h = nth(2, l);
1159 | typecase h is list { print(h.value); }
1160 | }
1161 = second
1162
1163 And in fact, you can restrict the union types to smaller sets to
1164 better indicate the allowable types of the functions. For example,
1165 `cons` always returns a list, so that should be its return type,
1166 not `list|void`. Likewise, `nth` requires a list. In this way we
1167 can implement some of the "Parse, don't Validate" approach.
1168
1169 | struct list {
1170 | value: string;
1171 | next: list|void;
1172 | }
1173 |
1174 | fun cons(v: string, l: list) {
1175 | make list(value:v, next:l as list|void)
1176 | }
1177 |
1178 | fun singleton(v: string) {
1179 | make list(value:v, next:null as list|void)
1180 | }
1181 |
1182 | fun nth(n, l: list) {
1183 | u = l as list|void;
1184 | v = u;
1185 | k = n;
1186 | while k > 1 {
1187 | typecase u is void { break; }
1188 | typecase u is list { v = u.next; }
1189 | u = v;
1190 | k = k - 1;
1191 | }
1192 | return u
1193 | }
1194 |
1195 | main = fun() {
1196 | l = cons("first", cons("second", singleton("third")));
1197 | h = nth(2, l);
1198 | typecase h is list { print(h.value); }
1199 | }
1200 = second
1201
1202 Structs may be empty.
1203
1204 | struct red { }
1205 | fun show(color: red) {
1206 | print("hi")
1207 | }
1208 | main = fun() {
1209 | show(make red());
1210 | }
1211 = hi
1212
1213 In combination with unions, this lets us create "typed enums".
1214
1215 | struct red { }
1216 | struct green { }
1217 | struct blue { }
1218 | fun show(color: red|green|blue) {
1219 | typecase color is red { print("red"); }
1220 | typecase color is green { print("green"); }
1221 | typecase color is blue { print("blue"); }
1222 | }
1223 | main = fun() {
1224 | show(make red() as red|green|blue);
1225 | show(make blue() as red|green|blue);
1226 | }
1227 = red
1228 = blue