1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
package com.seaofnodes.simple.codegen;
/***
Modules!?!?!?
Goal: under no circumstances can you load from an uninitialized variable
(although you can see default inits for fields that have defaults).
Goal: nested tree-structured name spaces, to control complexity.
Goal: field decl order within a file is fairly flexible.
A Module a root-level SMP file, and the full file path must be provided on
the command line. All other Simple structs are relative to this file.
Example: "simple /user/Dave/hello.smp"
Defines a module "hello" in directory "/user/Dave", compiles and produces
a `hello` or `hello.exe` output file, and runs it.
A SMP file has some <finit> file-init code which is static-global.
This code is executed on "first touch" of the file (defined below).
Example: hello.smp contains:
sys.io.p("Hello World!");
On first-touch, will print "Hello World!".
A SMP file is a lexical scope, and code inside it can reference those names.
Example: hello.smp contains:
val greet = "Hello World!"; // Declare file-scoped variable
sys.io.p(greet); // Reference file-scoped variable
Static code is NOT allowed to have forward-references AND be executed
before the <finit> finishes initializing all variables. After var
initialization, a singleton instance of the implied file struct is made,
and then <finit> code can call anything.
Example:
sys.io.p(greet); // Illegal forward reference
val greet = "Oops!";
Example:
val delay = { -> greet }; // Legal forward ref, but cannot execute (yet)
val greet = "Hello World!"; // Ref is resolved
sys.io.p(delay()); // Allowed to call delay()
val nprimes = { N ->
// compute number of primes < N
}
// Can otherwise execute any code in a <finit>
sys.io.p(nprimes(100));
Inside a directory, you can have sibling SMP files:
File hello.smp contains:
sys.io.p(I9A.GREETING); // File hello references sibling file I9A static globals
File I9A.smp contains:
val GREETING = translate("Hello World!");
val translate = { str msg -> ... };
<finit> code cannot reference sibling files (nor child files, see below)
until all static variables are initialized. This prevents cyclic
initialization bugs, where file A uses vars from file B and vice-versa.
File Chicken.smp contains:
val lay = Egg.hatch; // Illegal, defining var 'lay' after calling sibling Egg
File Egg.smp contains:
val hatch = Chicken.lay; // Illegal
You can make cross references, if they get delayed until after all fields
are declared.
File Chicken.smp contains:
int lay = 2;
lay = Egg.egg;
File Egg.smp contains:
int egg = 3;
egg = Chicken.lay;
The problem here, of course, is that you need to touch one of Chicken or
Egg first.
Inside a SMP file, you can define structs; structs are also a lexical
scope. Bare code in the struct is part of the struct's <init> and only
runs when an instance is created. Structs do not have static <clinit> (nor
<finit>). Structs <init> cannot have any forward references, and locally
declared functions cannot have both forward references and be executed (or
passed along to a possibly-executing function).
In the same directory as a SMP file, Simple allows other directories with
names matching SMP files. Inside these dirs can be more SMP files that are
lexically nested, similar to embedded structs.
*/
public abstract class Module {
}
|