blob: 2746ad1eb679540464167a6df1220375b7a95e51 (
plain)
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
|
# Simple のモジュール
[English](module.md) | 日本語
この文書は [Modules for Simple](module.md) の日本語訳です。原文はモジュールの設計案です。
目標:
- 有効なプログラムを1行で書けるようにする(チュートリアルや初心者の導入を簡単にする)。
- 最小限の手間でコンパイルして実行する。
- 分割コンパイル、名前空間、外部ライブラリを扱う。
- ELF など、リンク可能・実行可能な形式へコンパイルする。
- フィールドやクラスを簡単に非公開にする。
- 出力ファイルを、既存の .o/.exe/lib/dll の仕組みと互換にする。
- `#include` や `import` 用のファイルを不要にする。
- モジュールごとに構造・ファイルシステムの階層を持たせる。
- 複数のファイルで1つのモジュールを構成することも考えられる。
- モジュール内で簡単に名前空間を使う。
- 親ファイルまたは入れ子のファイルに、入れ子の名前を置く。
- クラス初期化子について次を保証する。
- 正確に1回だけ実行する。
- 最初にアクセスされた時点で遅延実行する。
- 最初のインスタンスが作られる前、またはその時点までに完了する。
- `<clinit>` 内の特別な原型インスタンス(golden instance)は例外として認める。
---
### 目標:最小限の手間で有効な1行プログラムを書く
現在の作業ディレクトリの名前から、モジュールとクラス `hello` を作ります。
追加の引数なしでコンパイルすると、リンクと実行も行います。
ファイル:`hello.smp`
```
sys.io.p("Hello World!");
```
コンパイルして実行します。
```
> smp hello.smp
Hello World!
```
モジュール名とクラス名は、ファイル名から取り出した `hello` です。
メモリ内でコンパイルし、`exec` で実行します。ファイルは作りません。
これは、その場でコンパイルして実行するためだけのモードです。
---
### 目標:分割コンパイル
コンパイル時に出力先のビルドディレクトリを指定すると、`.o` ファイルを得られます。
出力先ディレクトリの構造はソースと一致し、場所は自由に選べます。
前と同様に、コンパイルのコマンドでモジュール名を指定していないため、
`Pet` がモジュールにもクラスにもなります。
ファイル: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;
```
`build/` ディレクトリ内の `Pet.o` へコンパイルします。
```
> smp Pet.smp -o build/
> ls build
Pet.o
```
---
### 目標:入れ子の名前空間
`myRepo` 内のファイル名から、モジュールとクラス `Pet` を作ります。
入れ子のクラス `Pet.Vet` も作ります。これらを別のモジュール `Owner` から使います。
ディレクトリ:`~/myRepo/`
```
~/myRepo/
Pet.smp
build/
~/otherRepo/
Owner.smp
```
ファイル:`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;
}
```
ファイル:`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");
```
`build/` ディレクトリ内の `Pet.o` へコンパイルします。
コマンドで別のモジュールのルートを指定していないため、`Pet` がモジュールであり、クラスでもあります。
`Pet` がモジュールなので、`Vet` はモジュール `Pet` 内のクラスです。
```
> smp Pet.smp -o build/
> ls build
Pet.o
```
`Pet` と `Pet.Vet` のコードは、同じ `Pet.o` ファイルに入ります。
自由変数 `Pet` と `Pet.Vet` を解決するために `Pet` のリポジトリを指定して、
`Owner.smp` をコンパイルし、実行します。
```
> smp Owner.smp -lib ~/myRepo/build
I took Fido to Linda
```
---
### 目標:入れ子のファイル内の入れ子の名前空間
前の例と同じですが、ファイルを入れ子にします。
```
~/myRepo
Pet.smp
Pet/
Vet.smp
build/
Pet.o
Pet/
Vet.o
~/otherRepo
Owner.smp
```
ファイル:Pet.smp
```
int default_leggies = 4;
struct Pet {
str name;
int leggies = default_leggies;
}
```
ファイル:Pet/Vet.smp
```
// Nested class Vet inside of directory Pet
struct Vet {
str name;
str address;
long credit_card_num;
}
```
`Owner.smp` をコンパイルし、実行します。
```
> smp Owner.smp -lib ~/myRepo/build
I took Fido to Linda
```
---
### 目標:クラス初期化子の遅延実行
```
Parent.smp
Parent/
ChildA.smp
ChildB.smp
```
ファイル:`Parent.smp`
```
sys.io.p("In Parent <clinit>");
sys.io.p(ChildB.str);
```
ファイル:`ChildA.smp`
```
// <clinit> for ChildA
sys.io.p("In ChildA <clinit>");
str str = "A";
```
ファイル:`ChildB.smp`
```
// <clinit> for ChildB
sys.io.p("In ChildB <clinit>");
str str = "B";
```
コンパイルして実行します。
```
> smp Parent.smp
In Parent <clinit>
In ChildB <clinit>
B
```
### 目標:簡単な非公開フィールド
名前の先頭にアンダースコアを付けると、フィールドやクラスが非公開になります。
### 目標:C コンパイラのドライバに似た構文
```
Simple --cpu x86_64_v2 --abi Win64 --norun -o lib/lib/sys_x86_64_v2_win64.o src/main/smp/sys.smp
```
モジュールを指定していないため、最上位のファイルがモジュール `sys` になります。
モジュールディレクトリも指定していないため、ファイル名のディレクトリ部分 `src/main/smp` になります。
ビルドディレクトリを指定していないため、出力ファイルのディレクトリ部分 `lib/` になります。
出力ファイル名の既定値は `smp.o` ですが、ここでは別名を指定しています。
ある `<clinit>` は別の `<clinit>` を参照でき、最初のアクセス時に実行されます。
設計上、子より先に親へアクセスする必要があります。ただし、子 A は子 B を参照できます。
Parent.B へのアクセスは、次を意味します。
- 最初に Parent にアクセスし、`<clinit>` を開始する。
- そこからアクセスするものにも、A や B を含め、再帰的に `<clinit>` を実行する。
- Parent の処理が完了する。
- `<clinit>` 内では別の*モジュール*を参照できない。
- 静的関数は別のモジュールを参照*できる*が、その場合、実行される可能性がある……。
目標:モジュール内の `<clinit>` を決定的にする。
- 背景:複雑な `<clinit>` の順序のせいで、子が使う時点で参照先の static final フィールドがまだ初期化されていないことが、何度もあった。
- 副目標:親が子より先になるなど、`<clinit>` の*順序*を決定的にする。
- 問題:Parent の `<clinit>` の途中で Child を参照すると、Parent が完了する前に Child の `<clinit>` が走る。
- 部分的な回答:親が先で「親は子を知っている」ため、これはよい。
- 追加の問題:モジュールをまたぐと成り立たない。あるモジュールは別のモジュールの親ではない。
- 問題:`<clinit>` 中に呼び出す Parent の関数が、非決定的に子を初期化できる。
- `fcn = { -> rand ? A.fld : B.fld }`
- 回答案:Parent の `<clinit>` は、直接にも間接にも子を参照しては*ならない*。
- 例:`B b = new B; // Illegal ref to child in Parent.<clinit>`
— Parent の初期化子から子への不正な参照。
- 例:`B? b = fcn(); fcn = { -> rand ? null : new B; } // Illegal call to fcn referring to B inside Parent <clinit>`
— Parent の初期化子内で B を参照する関数への不正な呼び出し。
- 例:`B? b; fcn = { -> new B; } // OK, no B.<clinit> during Parent.<clinit>`
— Parent の初期化中に B の初期化子を実行しないため、これは許される。
目標:外部とのリンクでも、1回限りの初期化機能を使えるようにする。
回答案:外部から `Foo.foo()` を呼ぶときは、まず `Foo.<clinit>` が実行済みであることを確認してから、
`Foo.foo()` を呼び出す。任意の最適化として、成功後は呼び出し元を変更し、
`<clinit>` の確認を省略する。
|