1 -----------------------------------------------------------------------------
2 Polemy 0.1.0
3 by k.inaba (www.kmonos.net)
4 Nov 20, 2010
5 -----------------------------------------------------------------------------
6
7
8
9 <<How to Build>>
10
11 - Install DMD
12 http://www.digitalmars.com/d/2.0/changelog.html
13 Version 2.050 is recommended. Older or newer version may not work.
14
15 - Build
16 (for Windows) Run build.bat
17 (for Unix) Run build.sh
18 or use your favorite build tools upon main.d and polemy/*.d.
19
20 Then you will get the executable "polemy" in the "bin" directory.
21
22
23
24 <<License>>
25
26 d2stacktrace/*
27
28 is written by Benjamin Thaut and licensed under 2-clause BSD License.
29 See http://3d.benjamin-thaut.de/?p=15 for the detail.
30
31 (this package is used only for enabling stack-traces during printing exceptions;
32 it is not used for release builds.)
33
34 polemy/*
35 main.d
36
37 All the other parts are written by Kazuhiro Inaba and
38 licensed under NYSL 0.9982 ( http://www.kmonos.net/nysl/ ).
39
40
41
42 <<How to Use>>
43
44 > polemy
45 starts REPL
46
47 > polemy foo.pmy
48 executes foo.pmy
49
50 > polemy -l foo.pmy
51 after executing foo.pmy, starts REPL
52
53 > polemy -l foo.pmy -l bar.pmy buz.pmy
54 executes foo.pmy, bar.bmy, and then buz.pmy
55
56
57
58 <<Syntax>>
59
60 Comment is "# ... \n"
61
62 E ::=
63 // declaration
64 | ("var"|"let"|"def"|LAYER) ID "=" E (";"|"in") E
65 | ("var"|"let"|"def"|LAYER) ID "(" PARAMS ")" "{" E "}" (";"|"in") E
66 | ("var"|"let"|"def"|LAYER) ID "=" E
67 | ("var"|"let"|"def"|LAYER) ID "(" PARAMS ")" "{" E "}"
68 // literal
69 | INTEGER
70 | STRING
71 | "{" ENTRYS "}" // table
72 | "fun" "(" PARAMS ")" "{" E "}" // anonymous function
73 // function call
74 | E "(" ARGS")"
75 where ARGS ::= E "," ... "," E
76 PARAMS ::= ID LAYER* "," ... "," ID LAYER*
77 ENTRYS ::= ID ":" E "," ... "," ID ":" E
78 ID ::= 'a-zA-Z0-9_...'+
79 LAYER ::= "@" ID
80 // operators
81 | "(" E ")"
82 | E "." ID // table field access
83 | E ".?" ID // table field existence check
84 | E "{" ENTRYS "}" // table extend (pure functionally)
85 | E BINOP E
86 | "if" "(" E ")" "{" E "}"
87 | "if" "(" E ")" "{" E "}" "else "{" E "}"
88 // layered exec
89 | LAYER "(" E ")"
90
91 The following are actually rewritten to function calls:
92
93 - if (E) then{E} else{E} ==> if( E, fun(){E}, fun(){E} )
94 - E BINOP E ==> BINOP(E, E)
95 - E.ID ==> . (E, ID)
96 - E.?ID ==> .?(E, ID)
97 - {} ==> {}()
98 - { ENTRIES } ==> {}{ ENTRIES }
99 - E {ID:E, ...} ==> (.=(E, ID, E)) { ... }
100
101 Several styles of variable declaration can be used:
102
103 - fun(x){ fun(y){x} } # K-combinator
104 - fun(x){ let f = fun(y){x} in f } # let-in style
105 - fun(x){ var f = fun(y){x}; f } # var-; style
106 - fun(x){ def f = fun(y){x} in f } # you can use any combination of (let|var|def)-(;|in)
107 - fun(x){ def f(y){x} in f } # syntax sugar for function declaration
108 - fun(x){ let f(y){x}; f } # this is also ok
109 - fun(x){ var f(y){x} } # omitting (;|in) returns the last declared object directly
110 - fun(x,y){x} #< this is not equal to the above ones. functions are no curried.
111
112 NOTE: Theres no "let rec" syntax, but still recursive definition works
113 def f(x) { if(x==0){1}else{x*f(x-1)} } in f(10) #=> 3628800
114 yet still the code below also works
115 def x=21 in def x=x+x in x #=> 42.
116 The internal scoping mechanism is a little tricky (this is for coping with
117 the "layer" feature explained below), but I hope that it works as everyone
118 expects in most cases, as long as you don't use the same-name-variables heavily :).
119
120 (Experimental) pattern matching is also available. Here is an example.
121
122 def adjSum(lst)
123 {
124 case( lst )
125 when( {car:x, cdr:{car: y, cdr:z}} ) { {car: x+y, cdr: adjSum(z)} }
126 when( {car:x, cdr:{}} ) { {car: x, cdr: {}} }
127 when( {} ) { {} }
128 };
129
130 It is expanded to a sequence of if-then-elses prefering the first-match.
131 Note that {a: _} pattern matches all the tables that have the .a field.
132 It also matches to {a: 123, b: 456} having extra .b field. So, changing the
133 order of "when"s in the above code changes the behavior.
134
135
136
137
138 <<Basic Features>>
139
140 Polemy is an untyped functional programming language that has
141 - integers: 0, 123, 456666666666666666666666666666666666666789, ...
142 - strings: "hello, world!\n", ...
143 - tables: {car: 1, cdr: {car: 2, cdr: {}}}
144 - functions: fun(x){x+1}
145 as primitive datatypes. Functions capture lexical closures.
146 It is almost 'pure' (except the primitve function "print" and some
147 trick inside scoping mechanisms).
148
149
150 <<Layers :: Overview>>
151
152 Polemy's runtime environment has many "layer"s.
153 Usual execution run in the @value layer.
154
155 >> 1 + 2
156 3
157 >> @value( 1 + 2 )
158 3
159
160 Here you can see that @LayerName( Expression ) executes the inner Expression in
161 the @LayerName layer. Other than @value, one other predefined layer exists: @macro.
162
163 >> @macro( 1+2 )
164 {pos@value:{lineno@value:3, column@value:9, filename@value:<REPL>},
165 is@value:app,
166 args@value:{car@value:{pos@value:{lineno@value:3, column@value:9, filename@value:<REPL>},
167 is@value:int,
168 data@value:1},
169 cdr@value:{
170 car@value:{pos@value:{lineno@value:3, column@value:11, filename@value:<REPL>},
171 is@value:int,
172 data@value:2},
173 cdr@value:{}}},
174 fun@value:{pos@value:{lineno@value:3, column@value:10, filename@value:<REPL>},
175 is@value:var,
176 name@value:+}}
177
178 (Sorry, this pretty printing is not available on the actual interpreter...)
179 This evaluates the expression 1+2 in the @macro layer. In this layer, the meaning of
180 the program is its abstract syntax tree.
181
182 You can interleave layers.
183 The root node of the abstract syntax tree is function "app"lication.
184
185 >> @value(@macro( 1+2 ).is)
186 app
187
188
189
190 <<Layers :: Defining a new layer>>
191
192 To define a new layer, you should first tell how to "lift" existing values two the new layer.
193 Let us define the "@type" layer, where the meaning of programs is their static type.
194
195 >> @@type = fun(x) {
196 >> if( _isint(x) ) { "int" } else {
197 >> if( _isfun(x) ) { x } else { "unknown" } }
198 >> }
199 (Note: polemy REPL may warn some exception here but please ignore)
200
201 For simplicity, I here deal only with integers.
202 _isint is a primitive function of Polemy that checks the dynamic type of a value.
203 For function, leaving it untouched works well for almost all layers.
204
205 >> @type( 1 )
206 int
207 >> @type( 2 )
208 int
209 >> @type( "foo" )
210 unknown
211
212 Fine! Let's try to type 1+2.
213
214 >> @type( 1 + 2 )
215 ...\value.d(119): [<REPL>:6:8] only @value layer can call native function
216
217 Note that the behavior of this program is
218 - run 1+2 in the @type layer
219 and NOT
220 - run 1+2 in @value and obtain 3 and run 3 in the @type.
221 The problem is, the variable "+" is defined only in the @value layer.
222 To carry out computation in the @type layer. We need to define it also
223 in the @type layer.
224
225 To define some variable in a specific layer, use @LayerName in place of
226 (let|var|def)s.
227
228 >> let x = 2
229 >> @value x = 2
230 >> @type x = "int"
231 >> @hoge x = "fuga"
232
233 For "+", do it like this.
234
235 >> @type "+" = fun(x,y) {@value(
236 >> if( @type(x)=="int" && @type(y)=="int" ) { "int" } else { "typeerror" }
237 >> )}
238 polemy.value.native!(IntValue,IntValue,IntValue).native.__anonclass24
239
240 It is just computing the return type from the input type.
241 Not here that the intended "meaning" of if-then-else is the runtime-branching,
242 and the meaning of "==" is the value-comparison. These are the @value layer
243 behavior. So we have defined the function body inside @value layer.
244 But when we refer the variables x and y, we need its @type layer meaning.
245 Hence we use @type() there.
246
247 Now we get it.
248
249 >> @type( 1 + 2 )
250 int
251
252 Well, but do we have to define the @type layer meaning for every variables???
253 No. After you defined @type "+", you'll automatically get the following:
254
255 >> def double(x) { x + x }
256 (function:17e4740:1789720)
257
258 >> @type( double(123) )
259 int
260
261 Every user-defined functions are automatically "lift"ed to the appropriate layer.
262 Only primitive functions like "+" requires @yourNewLayer annotation.
263
264
265
266 <<Layers :: neutral-layer>>
267
268 let|var|def is to define a variable in the "current" layer.
269 Not necessary to the @value layer.
270
271 >> @value( let x = 1 in @value(x) )
272 1
273
274 >> @macro( let x = 1 in @value(x) )
275 polemy.failure.RuntimeException: [<REPL>:14:29] variable x not found
276
277 >> @macro( let x = 1 in @macro(x) )
278 {pos@value:{lineno@value:15, ...
279
280
281
282 <<Layers :: Layered-Parameters>>
283
284 >> def foo(x @macro @value) { {fst: x, snd: @macro(x)} }
285 (function:1730360:1789720)
286
287 If you annotate function parameters by @LayerNames, when you invoke the function...
288
289 >> foo(1+2)
290 {snd@value: {pos@value:{lineno@value:17, column@value:5, filename@value:<REPL>},
291 is@value:app, arg@value:{...
292 /fst@value:3
293 /}
294
295 its corresponding arguments are evaluated in the layer and passed to it.
296 If you specify multiple layers, the argument expression is run multiple times.
297 If you do not specify any layer for a parameter, it works in the neutral layer.
298
299
300
301 <<@macro layer>>
302
303 When function is invoked, it first run in the @macro layer, and after that,
304 it run in the neutral layer. Here is an example.
305
306 >> @macro twice(x) { x; x }
307 >> def f() { twice(print("Hello")); 999 }
308 (function:173b6a0:1789720)
309 >> f()
310 Hello
311 Hello
312 999
313
314 When the interpreter evaluates f(), it first executes
315 "twice(print("Hello")); 999"
316 in the @macro layer. Basically what it does is to just construct its syntax tree.
317 But, since we have defined the "twice" function in the @macro layer, it is
318 execute as a function. Resulting syntax tree is
319 "print("Hello"); print("Hello"); 999"
320 and this is executed on the neutral (in this example, @value) layer.
321 This is the reason why you see two "Hello"s.
322
323
324
325 [[limitations]]
326
327 This @macro layer is a very primitive one, and not a perfect macro language.
328 Two major limitations are seen in the following "it" example.
329
330 >> @macro LetItBe(x, y) { let it = x in y };
331
332 The variable name is not hygenic, and so without any effort, the syntax tree "y"
333 can access the outer variable "it".
334
335 >> def foo() { LetItBe( 1+2+3, it*it ) }
336 >> foo()
337 36
338
339 Of course, this is not just a limitation; it can sometimes allow us to write
340 many interesting macros.
341
342 The other problem is that the macro expansion is only done at function startup.
343 So
344
345 >> LetItBe( 1+2+3, it*it )
346 ...\value.d(173): [<REPL>:24:1] variable LetItBe is not set in layer @value
347
348 you cannot directly use the macro in the same scope as the definition.
349 You need to wrap it up in a function (like the foo() in the above example).
350
351
352
353 [[quote and unquote]]
354
355 Here is more involved example of code genration.
356 From "x", it generates "x*x*x*x*x*x*x*x*x*x".
357
358 @macro pow10(x) {
359 @value(
360 def pow(x, n) {
361 if( n == 1 ) { x }
362 else {
363 @macro( @value(x) * @value(pow(x,n-1)) )
364 }
365 }
366 in
367 pow(@macro(x),10)
368 )
369 };
370
371 Here, x is a syntax tree but n is an actual integer. If you read carefully,
372 you should get what is going on. Basically, @macro can be considered like
373 quasiquoting and @value to be an escape from it.
374
375
376
377 <<Primitives>>
378
379 {} 0-ary create-empty-table
380 . 2-ary table-get
381 .? 2-ary table-has?
382 .= 3-ary table-set
383
384 if 3-ary if-then-else
385
386 + - * / % || && 2-ary integer-operations (no short-circuit!)
387 < > <= >= == != 2-ary generic comparison
388
389 print 1-ary print-to-stdout
390
391 _isint _isstr _isfun _isundefined _istable 1-ary dynamic-type-test
392