Initial import of Xoomonk spec.
Cat's Eye Technologies
12 years ago
0 | -> encoding: UTF-8 | |
1 | ||
2 | The Xoomonk Programming Language | |
3 | ================================ | |
4 | ||
5 | Language version 0.1 Pretty Much Complete But Maybe Not Totally Finalized | |
6 | ||
7 | Abstract | |
8 | -------- | |
9 | ||
10 | _Xoomonk_ is a programming language in which _malingering updatable stores_ | |
11 | are first-class objects. Malingering updatable stores unify several language | |
12 | constructs, including procedure activations, named parameters, and object-like | |
13 | data structures. | |
14 | ||
15 | Description | |
16 | ----------- | |
17 | ||
18 | Overall, Xoomonk looks like a "typical" imperative language. The result | |
19 | of evaluating an expression can be assigned to a variable, and the contents | |
20 | of a variable can be used in a further expression. | |
21 | ||
22 | | a := 1 | |
23 | | b := a | |
24 | | print b | |
25 | = 1 | |
26 | ||
27 | However, blocks of these imperative statements can appear as terms in | |
28 | expressions; such blocks evaluate to entire updatable stores. | |
29 | ||
30 | | a := { | |
31 | | c := 5 | |
32 | | d := c | |
33 | | } | |
34 | | print a | |
35 | = [c=5,d=5] | |
36 | ||
37 | Once created, a store can be updated and accessed. | |
38 | ||
39 | | a := { | |
40 | | c := 5 | |
41 | | d := c | |
42 | | } | |
43 | | print a | |
44 | | a.d := 7 | |
45 | | print a | |
46 | | print a.c | |
47 | = [c=5,d=5] | |
48 | = [c=5,d=7] | |
49 | = 5 | |
50 | ||
51 | A store can also be assigned to a variable after creation. Stores are | |
52 | accessed by reference, so this creates two aliases to the same store. | |
53 | ||
54 | | a := { | |
55 | | c := 5 | |
56 | | d := c | |
57 | | } | |
58 | | b := a | |
59 | | b.c := 17 | |
60 | | print a | |
61 | | print b | |
62 | = [c=17,d=5] | |
63 | = [c=17,d=5] | |
64 | ||
65 | To create an independent copy of the store, the postfix `*` operator is | |
66 | used. | |
67 | ||
68 | | a := { | |
69 | | c := 5 | |
70 | | d := c | |
71 | | } | |
72 | | b := a* | |
73 | | b.c := 17 | |
74 | | print a | |
75 | | print b | |
76 | = [c=5,d=5] | |
77 | = [c=17,d=5] | |
78 | ||
79 | Empty blocks are permissible. | |
80 | ||
81 | | a := {} | |
82 | | print a | |
83 | = [] | |
84 | ||
85 | Once a store has been created, only those variables defined in the store | |
86 | can be updated and accessed -- new variables cannot be added. | |
87 | ||
88 | | a := { b := 6 } | |
89 | | print a.c | |
90 | ? Attempt to access an undefined variable | |
91 | ||
92 | | a := { b := 6 } | |
93 | | a.c := 12 | |
94 | ? Attempt to assign an undefined variable | |
95 | ||
96 | Stores and integers are the only two data types in Xoomonk. However, there | |
97 | are some special forms of the print statement, demonstrated here, which | |
98 | allow for textual output. | |
99 | ||
100 | | a := 65 | |
101 | | print char a | |
102 | | print string "Hello, world!" | |
103 | | print string "The value of a is "; | |
104 | | print a; | |
105 | | print "!" | |
106 | = A | |
107 | = Hello, world! | |
108 | = The value of a is 65! | |
109 | ||
110 | Xoomonk enforces strict block scope. Variables can be shadowed in an | |
111 | inner block. | |
112 | ||
113 | | a := 14 | |
114 | | b := { | |
115 | | a := 12 | |
116 | | print a | |
117 | | } | |
118 | | print a | |
119 | = 12 | |
120 | = 14 | |
121 | ||
122 | The special identifier `^` refers to the lexically enclosing store, that is, | |
123 | the store which was in effect when the this store was defined. | |
124 | ||
125 | | a := 14 | |
126 | | b := { | |
127 | | a := 12 | |
128 | | print ^.a | |
129 | | } | |
130 | | print b.a | |
131 | = 14 | |
132 | = 12 | |
133 | ||
134 | As long as a variable is assigned somewhere in a block, it can be accessed | |
135 | before it is first assigned. In this case, it will hold the integer value 0. | |
136 | It need not stay an integer, for typing is dynamic. | |
137 | ||
138 | | b := { | |
139 | | print a | |
140 | | a := 12 | |
141 | | print a | |
142 | | a := { c := 13 } | |
143 | | print a | |
144 | | } | |
145 | | print b | |
146 | = 0 | |
147 | = 12 | |
148 | = [c=13] | |
149 | = [a=[c=13]] | |
150 | ||
151 | We now present today's main feature. | |
152 | ||
153 | It's important to understand that a block need not define all the | |
154 | variables used in it. Such blocks do not immediately evaluate to a store. | |
155 | Instead, they evaluate to an object called an _unsaturated store_. | |
156 | ||
157 | Or, to put it another way: | |
158 | ||
159 | If, in a block, you refer to a variable which has not yet been given | |
160 | a value in that updatable store, the computations within the block are not | |
161 | performed until that variable is given a value. Such a store is called an | |
162 | _unsaturated store_. | |
163 | ||
164 | | a := { | |
165 | | d := c | |
166 | | } | |
167 | | print a | |
168 | = [c=?,d=*] | |
169 | ||
170 | An unsaturated store behaves similarly to a saturated store in certain | |
171 | respects. In particular, unsaturated stores can be updated. If doing | |
172 | so means that all of the undefined variables in the store are now defined, | |
173 | the block associated with that store is evaluated, and the store becomes | |
174 | saturated. In this sense, an unsaturated store is like a promise, and | |
175 | this bears some resemblance to lazy evaluation (thus the term _malingering_). | |
176 | ||
177 | | a := { | |
178 | | print string "executing block" | |
179 | | d := c | |
180 | | } | |
181 | | print a | |
182 | | a.c := 7 | |
183 | | print a | |
184 | = [c=?,d=*] | |
185 | = executing block | |
186 | = [c=7,d=7] | |
187 | ||
188 | Once a store has become saturated, the block associated with it is not | |
189 | executed again. | |
190 | ||
191 | | a := { | |
192 | | d := c | |
193 | | } | |
194 | | a.c := 7 | |
195 | | print a | |
196 | | a.c := 4 | |
197 | | print a | |
198 | = [c=7,d=7] | |
199 | = [c=4,d=7] | |
200 | ||
201 | The main program is a block. If it is unsaturated, no execution occurs | |
202 | at all. | |
203 | ||
204 | | print "hello" | |
205 | | a := 14 | |
206 | | b := c | |
207 | = | |
208 | ||
209 | Variables cannot generally be accessed from an unsaturated store. | |
210 | ||
211 | | a := { | |
212 | | d := c | |
213 | | } | |
214 | | x := a.c | |
215 | ? Attempt to access an unassigned variable | |
216 | ||
217 | | a := { | |
218 | | d := c | |
219 | | } | |
220 | | x := a.d | |
221 | ? Attempt to access an unresolved variable | |
222 | ||
223 | This is true, even if the variable is assigned a constant expression | |
224 | inside the block. | |
225 | ||
226 | | a := { | |
227 | | b := 7 | |
228 | | d := c | |
229 | | } | |
230 | | x := a.b | |
231 | ? Attempt to access an unresolved variable | |
232 | ||
233 | If, however, the unsaturated store contains some variables that have | |
234 | been updated since the store was created, those variable may be accessed. | |
235 | ||
236 | | a := { | |
237 | | print "executing block" | |
238 | | p := q | |
239 | | d := c | |
240 | | } | |
241 | | a.q = 7 | |
242 | | print a.q | |
243 | = 7 | |
244 | ||
245 | Nor is it possible to assign a variable in an unsaturated store which is | |
246 | defined somewhere in the block. | |
247 | ||
248 | | a := { | |
249 | | b := 7 | |
250 | | d := c | |
251 | | } | |
252 | | a.b := 4 | |
253 | ? Attempt to assign an unresolved variable | |
254 | ||
255 | A variable is considered unresolved even if it is assigned within the block, | |
256 | if that assignment takes place during or after its first use in an expresion. | |
257 | ||
258 | | a := { | |
259 | | b := b | |
260 | | } | |
261 | | a.b := 5 | |
262 | | print a | |
263 | = [b=5] | |
264 | ||
265 | | a := { | |
266 | | print string "executing block" | |
267 | | l := b | |
268 | | b := 3 | |
269 | | l := 3 | |
270 | | } | |
271 | | print string "saturating store" | |
272 | | a.b := 5 | |
273 | | print a | |
274 | = saturating store | |
275 | = executing block | |
276 | = [b=3,l=3] | |
277 | ||
278 | We now describe how this language is (we reasonably assume) Turing-complete. | |
279 | ||
280 | Operations are accomplished with certain built-in unsaturated stores. For | |
281 | example, there is a store called `add`, which can be used for addition. These | |
282 | built-in stores are globally available; they do not exist in any particular | |
283 | store themselves. One uses the `$` prefix operator to access this global | |
284 | namespace. | |
285 | ||
286 | | print $add | |
287 | | $add.x := 3 | |
288 | | $add.y := 5 | |
289 | | print $add.result | |
290 | | print $add | |
291 | = [x=?,y=?,result=*] | |
292 | = 8 | |
293 | = [x=3,y=5,result=8] | |
294 | ||
295 | Because using a built-in operation store in this way saturates it, it cannot | |
296 | be used again. Typically you want to make a copy of the store first, and use | |
297 | that, leaving the built-in store unmodified. | |
298 | ||
299 | | o1 := $add* | |
300 | | o1.x := 4 | |
301 | | o1.y := 7 | |
302 | | o2 := $add* | |
303 | | o2.x := o1.result | |
304 | | o2.y := 9 | |
305 | | print o2.result | |
306 | = 20 | |
307 | ||
308 | Since Xoomonk is not a strictly minimalist language, there is a selection | |
309 | of built-in stores which provide useful operations: `$add`, `$sub`, `$mul`, | |
310 | `$div`, `$gt`, and `$not`. | |
311 | ||
312 | Decision-making is also accomplished with a built-in store, `if`. This store | |
313 | contains variables caled `cond`, `then`, and `else`. `cond` should | |
314 | be an integer, and `then` and `else` should be unsaturated stores where `x` is | |
315 | unassigned. When the first three are assigned values, if `cond` is nonzero, | |
316 | it is assigned to `x` in the `then` store; otherwise, if it is zero, it is | |
317 | assigned to `x` in the `else` store. | |
318 | ||
319 | | o1 := $if* | |
320 | | o1.then := { | |
321 | | y := x | |
322 | | print "condition is true" | |
323 | | } | |
324 | | o1.else := { | |
325 | | y := x | |
326 | | print "condition is false" | |
327 | | } | |
328 | | o1.cond := 0 | |
329 | = condition is false | |
330 | ||
331 | | o1 := $if* | |
332 | | o1.then := { | |
333 | | y := x | |
334 | | print "condition is true" | |
335 | | } | |
336 | | o1.else := { | |
337 | | y := x | |
338 | | print "condition is false" | |
339 | | } | |
340 | | o1.cond := 1 | |
341 | = condition is true | |
342 | ||
343 | Repetition is also accomplished with a built-in store, `loop`. This store | |
344 | contains an unassigned variable called `do`. When it is assigned a value, | |
345 | assumed to be an unsaturated store, a copy of it is made. The variable | |
346 | `x` inside that copy is assigned the value 0. This is supposed to saturate | |
347 | the store. The variable `continue` is then accessed from the store. If | |
348 | it is nonzero, the process repeats, with another copy of the `do` store | |
349 | getting 0 assigned to its `x`, and so forth. | |
350 | ||
351 | | l := $loop* | |
352 | | counter := 5 | |
353 | | l.do := { | |
354 | | y := x | |
355 | | print ^.counter | |
356 | | o := $sub* | |
357 | | o.x := ^.counter | |
358 | | o.y := 1 | |
359 | | ^.counter := o.result | |
360 | | continue := o.result | |
361 | | } | |
362 | | print string "done!" | |
363 | = 5 | |
364 | = 4 | |
365 | = 5 | |
366 | = 2 | |
367 | = 1 | |
368 | = done! | |
369 | ||
370 | Because the `loop` construct will always execute the `do` store at least once | |
371 | (even assuming its only unassigned variable is `x`), it acts like a so-called | |
372 | `repeat` loop. It can be used in conjunction with `if` to simulate a | |
373 | so-called `while` loop. With this loop, the built-in operations provided, | |
374 | and variables which may contain unbounded integer values, Xoomonk should | |
375 | be uncontroversially Turing-complete. | |
376 | ||
377 | Finally, there is no provision for defining functions or procedures, because | |
378 | malingering stores can act as these constructs. | |
379 | ||
380 | | perimeter := { | |
381 | | o1 := $mul* | |
382 | | o1.x := x | |
383 | | o1.y := 2 | |
384 | | o2 := $mul* | |
385 | | o2.x := y | |
386 | | o2.y := 2 | |
387 | | o3 := $add* | |
388 | | o3.x := o1.result | |
389 | | o3.y := o2.result | |
390 | | result := o3.result | |
391 | | } | |
392 | | p1 := perimeter* | |
393 | | p1.x := 13 | |
394 | | p1.y := 6 | |
395 | | print p1.result | |
396 | | p2 := perimeter* | |
397 | | p2.x := 4 | |
398 | | p2.y := 1 | |
399 | | print p2.result | |
400 | = 38 | |
401 | = 10 | |
402 | ||
403 | Grammar | |
404 | ------- | |
405 | ||
406 | Xoomonk ::= { Stmt }. | |
407 | Stmt ::= Assign | Print. | |
408 | Assign ::= Ref ":=" Expr. | |
409 | Print ::= "print" ("string" <string> | "char" Expr | Expr) [";"]. | |
410 | Expr ::= (Block | Ref | Const) ["*"]. | |
411 | Block ::= "{" { Stmt } "}". | |
412 | Ref ::= Name {"." Name}. | |
413 | Name ::= "^" | "$" <alphanumeric> | <alphanumeric>. | |
414 | Const ::= <integer-literal>. | |
415 | ||
416 | Discussion | |
417 | ---------- | |
418 | ||
419 | There is some similarity with Wouter van Oortmerssen's language Bla, in that | |
420 | function environments are very close cousins of updatable stores. But | |
421 | Xoomonk, quite unlike Bla, is an imperative language; once created, a store | |
422 | may be updated at any point. And, of course, this property is exploited in | |
423 | the introduction of malingering stores. | |
424 | ||
425 | Xoomonk originally had an infix operator `&`, which takes two stores as its | |
426 | arguments, at least one of which must be saturated, and evaluates to a third | |
427 | store which is the result of merging the two argument stores. This result store | |
428 | may be saturated even if only one of the argument stores was saturated, if | |
429 | the saturated store gave all the values that the unsaturated store needed. | |
430 | This operator was dropped because it is mostly syntactic sugar for assigning | |
431 | each of the desired variables from one store to the other. However, it does | |
432 | admittedly provide a very natural syntax, which orthogonally includes "named | |
433 | arguments", when using unsaturated stores as procedures: | |
434 | ||
435 | perimeter = { | |
436 | # see example above | |
437 | } | |
438 | g := perimeter* & { x := 13 y := 6 } | |
439 | print g.result | |
440 | ||
441 | Xoomonk, as a project, is also an experiment in _test-driven language | |
442 | design_. As you can see, I've described the language with a series of | |
443 | examples, written in (something close to) Falderal format, each of which | |
444 | could be used as a test case. This should make implementing an interpreter | |
445 | or compiler for the language much easier, when I get around to that. | |
446 | ||
447 | Happy malingering! | |
448 | ||
449 | -Chris Pressey | |
450 | Cat's Eye Technologies | |
451 | August 7, 2011 | |
452 | Evanston, IL |