/
initial.opa
488 lines (399 loc) · 12.1 KB
/
initial.opa
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
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
/*
Copyright © 2011 MLstate
This file is part of OPA.
OPA is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License, version 3, as published by
the Free Software Foundation.
OPA is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
more details.
You should have received a copy of the GNU Affero General Public License
along with OPA. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Basic definitions of common types and values.
*
* These definitions are accessible by all programs and are used pervasively by the standard library itself.
*
* @author David-Rajchenbach-Teller, 2010 (documentation)
* @destination public
* @stability stable
* @category standard
*/
/**
* {1 Elementary types}
*/
/**
* {2 Booleans}
*
* Booleans represent truth values. There are exactly two booleans: [{false}] and [{true}]. Booleans
* are used pervasively in OPA, in particular for control flow. Booleans can be manipulated using
* [if...then...else...] or pattern-matching.
*
* More functions on booleans are defined in module [Bool].
*/
@opacapi
type bool = {false} / {true}
/**
* A shortcut for [{true}].
*/
true ={true}:bool
/**
* A shortcut for [{false}].
*/
false={false}:bool
/**
* Boolean negation
*/
not(b) = if b then false else true
/**
* {2 Void}
*/
/**
* The value of nothing.
*/
@opacapi
type void = {}
void={}:void
/**
* {1 Comparison functions}
*
* All comparison functions return a boolean.
*/
/**
* The basic monomorphic comparison functions
*/
compare_int = Int.compare
compare_string = String.compare
compare_float = Float.compare
/**
* The structural polymorphic comparison
*/
@specialize(compare_int,compare_string,compare_float)
compare(a,b) = OpaValue.compare(a,b)
/**
* Structural equality.
*
* [a == b] returns [true] if and only if [a] and [b] are structurally equal.
* For instance, ["blue" == "blue"] or [{field = "blue"} == {field = "blue"}]
* will return [true].
*
* Note that structural equality does not work on functions.
*/
@opacapi
@specialize(String.equals,Int.equals,Float.equals)
`==`(a:'a,b:'a):bool = match compare(a,b) with {eq} -> true | _ -> false
/**
* Universal comparison functions
*/
@specialize(String.`lt`,Int.`<`,Float.`<`)
`<`(a:'a,b:'a) = match compare(a,b) with {lt} -> true | _ -> false
@specialize(String.`gt`,Int.`>`,Float.`>`)
`>`(a:'a,b:'a) = match compare(a,b) with {gt} -> true | _ -> false
@opacapi
@specialize(String.`ne`,Int.`!=`,Float.`!=`)
`!=`(a:'a,b:'a) = match compare(a,b) with {eq} -> false | _ -> true
@specialize(String.`ge`,Int.`>=`,Float.`>=`)
`>=`(a:'a,b:'a) = match compare(a,b) with {lt} | {neq} -> false | _ -> true
@specialize(String.`le`,Int.`<=`,Float.`<=`)
`<=`(a:'a,b:'a) = match compare(a,b) with {gt} | {neq} -> false | _ -> true
@specialize(Int.min,Float.min)
min(a:'a,b:'a) = match compare(a,b) with {lt} -> a | _ -> b
@specialize(Int.max,Float.max)
max(a:'a,b:'a) = match compare(a,b) with {gt} -> a | _ -> b
eq(a,b)= a==b
/**
* Physical equality.
*
* This operator is for advanced uses, as it compares memory addresses of two objects.
* Unless you know what you are doing, chances are that you should rather use operator [`==`].
*
* [a===b] produces [true] if and only if [a] and [b] are the same object -- not just if
* they are equal. For instance, [a = {blue}; a === a] returns [true], while
* ["blue" === "blue"] will probably return [false]. Note that circumstances beyond the control
* of the developer may alter the physical equality of two objects: compiler optimizations may
* transform two immutable identical objects into one, while concurrency or distribution may
* cause an object to be duplicated transparently.
*/
`===` = %% Bslpervasives.areSameObject %% : 'a, 'a -> bool
/**
* {1 Elementary combinators}
*/
/**
A function that discards its argument
*/
ignore(_:'a) : void = void
/**
* The identity function
*
* For every [x], [identity(x)] is equal to [x]
*/
@opacapi identity(x:'a) = x : 'a
/**
* The pipe.
*
* The pipe is a reversed composition operator. That is, [a |> f] is equivalent to [f(a)].
* Use this operator to chain function calls. For instance, [5 |> _ + 10 |> _ + 20] produces
* result [35].
*
* Use it to chain function calls.
*/
// CAUTION, |> is currently inlined by the compiler using `|>`(a,f) = f(a)
`|>`(a,f) = f(a)
/**
* Composition
*
* This is the forward composition operator. That is, [f@g] is equivalent to [x -> f(g(x))].
* Use this operator to compose functions for a later call. For instance, [_ + 10 @ _ + 20]
* is the function [x -> x + 30].
*/
`@`(f,g) =
new_f(x)=f(g(x))
new_f
`@>`(g, f) = f @ g
compose = `@`
reverse_compose = `@>`
/**
* {1 Numbers}
*/
/**
* {2 Operations on integers}
*
* More operations on integers are defined in module [Int].
*/
/**
* A shortcut for [_ + 1]
*/
succ(x : int) = x + 1
/**
* A shortcut for [_ - 1]
*/
pred(x : int) = x - 1
/**
* Arithmetic operations
*
*/
unary_minus_int = %% Bslpervasives.int_neg %%
mod = %% Bslpervasives.int_mod %%
rem = %% Bslpervasives.int_rem %%
/**
* Magic overloaded operator '+'
* This operator is defined only on [int], [float]
* The overloading of this operator is resolved at compile time, which means that
* if the context does not give enough type information for inferring one of the supported type,
* this will raise an error during the compilation, inviting the user to add a type annotation.
**/
@specialize_strict(Int.`+`, Float.`+`)
`+`(_ : 'number, _ : 'number) : 'number = @fail
/**
* Magic operator '-'
* Cf documentation of {!+}.
* Defined only on [int] and [float]
**/
@specialize_strict(Int.`-`, Float.`-`)
`-`(_ : 'number, _ : 'number) : 'number = @fail
/**
* Magic operator '-'
* Cf documentation of '+'.
* Defined only on [int] and [float]
**/
@specialize_strict(Int.`*`, Float.`*`)
`*`(_ : 'number, _ : 'number) : 'number = @fail
/**
* Magic operator '-'
* Cf documentation of '+'.
* Defined only on [int] and [float]
**/
@specialize_strict(Int.`/`, Float.`/`)
`/`(_ : 'number, _ : 'number) : 'number = @fail
/**
* {2 Operations on floating-point numbers}
*
* More operations on floating-point numbers are defined in module [Float].
*/
@deprecated({hint="use undotted operator"}) `+.` = Float.`+`
@deprecated({hint="use undotted operator"}) `-.` = Float.`-`
@deprecated({hint="use undotted operator"}) `*.` = Float.`*`
@deprecated({hint="use undotted operator"}) `/.` = Float.`/`
/**
* Arithmetic operations
*
* The following functions are equivalent to arithmetic operators. They are provided essentially to simplify the life of developers who
* wish to generate OPA code from specification languages.
*/
/**
* Still introduced by the parser. Should be removed before the release,
* once applications are patched, and do not use the [-.] syntax.
* @opacapi: introduced by the parser as resolution of unary [-.]
**/
@opacapi
@deprecated({hint="use undotted operator"})
unary_minus_dot = %% Bslpervasives.float_neg %% : float -> float
/**
* Magic overloaded operator unary '-'
* This operator is defined only on [int], [float]
* @opacapi: introduced by the parser as resolution of unary [-]
**/
@opacapi
@specialize_strict(unary_minus_int, %% Bslpervasives.float_neg %%)
unary_minus(_:'number) : 'number = @fail
/**
* {1 Debugging utilities}
*
* More utilities are provided in module [Debug].
*/
/**
* This is just an inline of the directive [@fail],
* which will report the position of the error in the source code.
**/
@expand
error(s) = @fail(s)
/**
* Print a warning.
*/
warning = Debug.warning
/**
* Print a message and continue.
*
* If this function is executed on the client, the message will be displayed in a side window.
* On the server, the message will be displayed on the console.
*/
jlog = %% Bslpervasives.jlog %%
`^` = %% BslString.concat %%
/**
* A shortcut for ^
*/
sc = `^`
print = %% Bslpervasives.print_string %% : string -> void
prerr = %% Bslpervasives.prerr_string %% : string -> void
/**
* Same than print, prerr but add a newline char, and flush.
**/
println = %% Bslpervasives.print_endline %% : string -> void
prerrln = %% Bslpervasives.prerr_endline %% : string -> void
print_int = %% Bslpervasives.print_int %% : int -> void
/**
* {1 Loops}
*
* In OPA, loops are functions. If you come from a different language, this may come as a surprise.
* The most general looping function is called [for]. You can easily define other looping functions
* from [for] or from more primitive constructions.
*/
/**
* General loop
*
* This function implements a general-purpose looping construction.
*
* Example: [for(0, (i -> do jlog("{i}"); i + 1), _ < 50)] will print
* [0], then [1], then [2], ..., until [49].
*
* Alternative syntax: [for(0, _ , _<50)(i -> do jlog("{i}"; i+1))].
*
*
* @param init An initial value.
* @param next The body of the loop. It takes as argument the latest state of the loop and returns the next state.
* @param cond A function determining whether the loop should continue. If it returns [true], the loop continues.
* Otherwise, it stops.
*/
for(init:'state, next:'state -> 'state, cond:'state -> bool) : 'state =
if cond(init) then
for(next(init),next,cond)
else
init
/**
* A loop on integers.
*
* Example: [inrange(0, 10, do jlog("{_}"))] will print [0], [1], ... until [10]
*
* @param first The start of the loop (included).
* @param last The end of the loop (included).
* @param action The body of the loop. It takes as argument the latest state of the loop and returns nothing.
*/
inrange(first:int, last:int, action: int -> void) =
_ = for(first, (i -> do action(i); i+1), _ <= last)
void
/**
* Calls a function a given number of times
*
* NB: only useful if f has a side-effect
*
* @param n The number of times to call f
* @param f The function to be called n times
*/
repeat(n : int, f : -> void) : void =
ignore(for(0, (i -> do f(); i+1), _ < n))
/**
* Calls a function while it returns true, while accumulating a state
*
* @param f The function to be called as long as it returns true
*/
while(state:'state, f : 'state -> ('state, bool)) : 'state =
(state, continue) = f(state)
if continue then while(state,f) else state
/**
* An alias for [for]
*/
unfold = for
/**
*
* Short cut operators : &&, ||
*
*/
/* They are implemented as macro in S3 (see the @expand) */
@expand
`&&`(a,b) =
if a then b
else
/*
JS imp backend may simplify pattern matching generated
from && in opa code. We can easily match the form of
the generated code if the following bool is syntactic.
Do not replace it with the identifier [false]
*/
{false} : bool
@expand
`||`(a,b) =
if a
then
/*
cf &&, same remark about js optimization
*/
{true} : bool
else b
xor(left, right) = if left then not(right) else right
/**
* {6 Conversion function}
*/
/**
* The basic monomorphic conversion
*/
string_of_int = String.of_int
int_to_string = string_of_int
string_of_float = String.of_float
float_to_string = string_of_float
//int_of_string = Parser.int
//int_of_string = %% BslNumber.Int.of_string %%
//string_to_int = int_of_string
int_of_float = Int.of_float // %% BslNumber.Int.of_float %% : float -> int
float_to_int = Int.of_float
float_of_int = Float.of_int
int_to_float = Float.of_int
//float_of_string = Float.of_string
string_to_int = Int.of_string
string_to_float = Float.of_string
string_of_void() = String.of_void
void_to_string = String.of_void
int_of_first_char = Int.of_utf8
voidToString = Void.to_string
intToString = Int.to_string
stringToString = String.to_string
boolToString = Bool.to_string
floatToString = Float.to_string
/**
* The polymorphic conversion to string.
* Used for the string insertion syntax, e.g. ["{myOpaValueInAString}"]
*/
@opacapi
@specialize(voidToString,intToString,stringToString,boolToString,floatToString)
magicToString(s) = OpaValue.to_string(s)