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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
|
# Modules for Simple
Goals:
- 1-liner valid program (for easy tutorial/newbie starts)
- - Compile and run with minimal effort
- Separate compilation and namespaces; external libs
- - Compile to ELF or other linkable/executable format
- Easy private fields & classes
- Output files are compatible with existing .o/.exe/lib/dll infrastructure
- No `#include` or `import` files
- Structure / file system heirarchy per module
- - Perhaps many files make a module
- - easy namespaces within a module
- - Nested names in parent file or nested file
- Class initializers guarenteed:
- - execute exactly once
- - lazily on first touch
- - complete on or before first instances are made
- - - (an exception is allowed for a golden instance in `<clinit>`)
---
### Goal: Valid 1-liner, minimal effort
This makes a module and a class `hello` out of the current working directory
name. Compiling without more arguments also links and executes.
File: `hello.smp`
```
sys.io.p("Hello World!");
```
Compile and run:
```
> smp hello.smp
Hello World!
```
Module and class name is `hello` extracted from file name, compiled in memory
and `exec'd`. No files made, this is strictly a run and gun mode.
---
### Goal: separate compilation
Name a build directory target during compilation to get `.o` files.
The build target directory structure will match the source, and
can be placed anywhere.
As before, since no module name is mentioned on the compile line, `Pet` becomes
both the module and a class.
File: Pet.smp
```
// Class initializer <clinit> is any code outside a struct declaration
// and is guaranteed to run-once on first touch.
// static fields listed here:
int default_leggies = 4;
// A default (no argument) <init> is code inside struct with same name as the
// file; this is the same behavior as field initializers for Java, although
// any code is allowed, not just expressions.
struct Pet {
str name;
int leggies = default_leggies;
}
float yet_another_static_field;
```
Compile to `Pet.o` in the `build/` directory:
```
> smp Pet.smp -o build/
> ls build
Pet.o
```
---
### Goal: nested namespaces
This makes a module and class `Pet` based on the file name in `myRepo`. It
also makes a nested class `Pet.Vet`. These are used from a different module
`Owner`.
Directory: `~/myRepo/`
```
~/myRepo/
Pet.smp
build/
~/otherRepo/
Owner.smp
```
File: `myRepo/Pet.smp`
```
int default_leggies = 4;
struct Pet {
str name;
int leggies = default_leggies;
}
// Nested class Vet inside of file Pet
struct Vet {
str name;
str address;
long credit_card_num;
}
```
File: `otherRepo/Owner.smp`
```
Pet dog = Pet{name="Fido"};
Pet snake = Pet{name="slither", leggies = 0; };
Pet.Vet vet = Pet.Vet{name="Linda"; address="101 Main St"; credit_card_num=1234};
sys.io.p("I took "+dog.name+" to "+vet.name+"\n");
```
Compile to `Pet.o` in the `build/` directory; since the compile line does not
mention some other module root, `Pet` must be the module (and is also a class).
Since `Pet` is the module, `Vet` is a class within the module `Pet`.
```
> smp Pet.smp -o build/
> ls build
Pet.o
```
The code for both `Pet` and `Pet.Vet` are in the same `Pet.o` file.
Compile and run `Owner.smp`, specifying the `Pet` repo to satisfy the free
variables `Pet` and `Pet.Vet`.
```
> smp Owner.smp -lib ~/myRepo/build
I took Fido to Linda
```
---
### Goal: nested namespace in a nested file
Same as the last example but with nested files:
```
~/myRepo
Pet.smp
Pet/
Vet.smp
build/
Pet.o
Pet/
Vet.o
~/otherRepo
Owner.smp
```
File: Pet.smp
```
int default_leggies = 4;
struct Pet {
str name;
int leggies = default_leggies;
}
```
File: Pet/Vet.smp
```
// Nested class Vet inside of directory Pet
struct Vet {
str name;
str address;
long credit_card_num;
}
```
Compile and run `Owner.smp`:
```
> smp Owner.smp -lib ~/myRepo/build
I took Fido to Linda
```
---
### Goal: Class Initializers are Lazy
```
Parent.smp
Parent/
ChildA.smp
ChildB.smp
```
File: `Parent.smp`
```
sys.io.p("In Parent <clinit>");
sys.io.p(ChildB.str);
```
File: `ChildA.smp`
```
// <clinit> for ChildA
sys.io.p("In ChildA <clinit>");
str str = "A";
```
File: `ChildB.smp`
```
// <clinit> for ChildB
sys.io.p("In ChildB <clinit>");
str str = "B";
```
Compile and run:
```
> smp Parent.smp
In Parent <clinit>
In ChildB <clinit>
B
```
### Goal: Easy private fields
Leading underscore makes field or class private.
### Goal: similar to C compiler driver syntax
```
Simple --cpu x86_64_v2 --abi Win64 --norun -o lib/lib/sys_x86_64_v2_win64.o src/main/smp/sys.smp
```
Module not mentioned, so top-level file is module: `sys`
Module directory not mentioned, so is prefix of file name `src/main/smp`.
Build dir not mentioned, so is prefix of output file: `lib/`
Output file is default `smp.o`, here renamed.
One <clinit> can refer to another, and will be executed as first touched.
Parents must be touched before children by design.
However a child A can refer to a child B.
Touching Parent.B means
- Parent is touched first, and begins <clinit>
- Anything it touches, gets recursively <clinit> including e.g. A or B
- Parent completes.
- Nothing in a <clinit> can refer to another *module*.
- Static functions *can* refer to another module, but then can be executed...
Goal: deterministic <clinit> within a module.
History: plenty of times I've had complex <clinit> orders, and then discovered
some referred static-final-fields are not yet init'd when child uses.
SubGoal: deterministic <clinit> *order*, Parent before Child, etc.
Issue: mid-Parent <clinit>, refers to Child, so Child <clinit> before Parent completes.
SubAnswer: Since parent first, and "parent knows child", this is OK.
SubIssue: this does not work across modules; one module is not a parent of another
Issue: Parent fcns called during <clinit> can non-determistic init child.
"fcn = { -> rand ? A.fld : B.fld }"
Answer?: Parent <clinit> *may not* direct or indirect refer to a child.
Example: `B b = new B; // Illegal ref to child in Parent.<clinit>`
Example: `B? b = fcn(); fcn = { -> rand ? null : new B; } // Illegal call to fcn referring to B inside Parent <clinit>`
Example: `B? b; fcn = { -> new B; } // OK, no B.<clinit> during Parent.<clinit>`
Goal: external linkage also enjoys the 1-shot init feature.
Answer?: extern call to 'Foo.foo()' first checks Foo.<clinit>
has run, before calling `Foo.foo()`. Optional: Caller is
modified to bypass <clinit> check after success.
|