forked from mwerezak/sphinx-lang
-
Notifications
You must be signed in to change notification settings - Fork 0
/
syntax_examples.sph
584 lines (414 loc) · 12.2 KB
/
syntax_examples.sph
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
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
# Sphinx syntax examples
# If you use Sublime Text you can read this with syntax highlighting!
# (highlighter for sublime text provided by Sphinx.sublime-syntax)
#{
Block Comment
#{ Nesting! }#
}#
# Your first Sphinx program!
echo "Hello, world!" # print() function will be available later once there is a builtin library
# Semicolons are optional. The syntax has been designed so that the end of a statement can always be inferred.
"One Stmt"; "Two Stmt"
# Like Lua, the syntax has been chosen to ensure that
# the end of a statement can always be inferred without
# needing to be whitespace-sensitive.
# The language will support type annotations and have structural type inference built in
# Any expression can be annotated, though outside of certain positions you may need parentheses
# This is still WIP
("Type Annotations" : String)
# Booleans
true # Not false.
false # Not *not* false.
# Numbers
1234 # An integer.
12.34 # A decimal number.
0xFACE # Hexadecimal integer.
# Strings
"I am a string"
"" # The empty string.
"123" # self is a string, not a number.
'And then she said, "woah."' # Can be single or double quoted.
"\ttab\nnewline, literal\\backslash" # Escape sequences
r"/[\w._%+-]+@[\w.-]+\.[a-zA-Z]{2,4}/" # Literal with escape sequences disabled
let two = 2
$"numbers: 1 {two} 3 {2*2}" # interpolated string
# Nil
nil
# Tuple
"abc", 123
# parens are only required for these special cases
() # the empty tuple
("solo",) # tuple with one element
# Objects
{ value = 0xA } # anonymous object
Person { value = 0xC } # classy object
get_person_type() { name = "Bob", age = 23 } # type can be an expression
{ name: String = "Bob", age: Int = 23 } # with annotations
# Arithmetic expressions
add + me
subtract - me
multiply * me
divide / me
modulo % me
# (x^2) + (y^2) # TODO exponentiation?
# Bitwise and shifts
(17 ^ 73) << 3
2412 & 0xff | ~0xcc00
# unary
-negate_me
~invert
# Comparison and equality
less < than
less_than <= or_equal
greater > than
greater_than >= or_equal
1 == 2 # false.
"cat" != "dog" # true.
314 == "pi" # false.
123 == "123" # false.
# booleans are not implicitly integers (but all expressions do have a "truth" value)
true == 1 # false
true == bool(1) # true
# Logical operators
not true # false.
not false # true.
# these will short circuit
true and false # false.
true and true # true.
false or false # false.
true or false # true.
# Precendence and grouping
average = (min + max) / 2
# Blocks
begin
echo "One statement."
echo "Two statements."
"Result value."
end
begin
if condition then
break "Early exit" # can break out of blocks early if you want
end
"Fallback value."
end
# Variable declaration and assignment
# Declarations are used to create new variables.
# They are written just like assignments except the
# variable name expression (lvalue) is preceeded by
# either "let" or "var":
let im_a_variable = "here is my value" # immutable variable
let i_am_nil = nil # initializer is required
var i_can_be_mutated = "initial_value"
i_can_be_mutated = "another_value"
let im_a_float: Float = 3.14159 # with type annotation
# destructure tuples
var first, second = "abc", "def"
second, first = first, second
# destructuring assignment with type annotations
let (index: Int, val: String) = 0, "example"
# assignment expressions are expressions, and so can be chained
var b = 0
var c = 1
let a = b += c = let d = 1 # chaining assignment (right associative)
let a = (b += (c = (let d = 1))) # previous expression is equivalent to this
# assignment versions of all binary operators are supported for assignment expressions only
inc = 0
inc -= 1 # arithmetic assignment
# variables can be freely redeclared (shadowed)
let breakfast = "bagels"
echo breakfast # "bagels".
var breakfast = breakfast
breakfast = "beignets"
echo breakfast # "beignets".
del a # drop a variable from the current scope
del collection[key] # mostly useful for removing values from objects
# assigment expressions bind weaker than operator expressions
3 * b += c # Error, assignment target is not an lvalue
3 * (b += c) # Ok
# Control Flow
# branching constructs are expressions
# looping constructs are statements
cond_expr =
if condition1 then
"first"
elif condition2 then
"second"
else
"default"
end
# If no branch is entered in an if-expression (which can happen if there is no "else")
# the whole expression evaluates to the last value that was tested. So for example:
# If-expressions evaluate to the last item of the branch that they enter
if (if a then true end) then
echo "if-ception"
end
# If-expressions evaluate to their condition if they don't enter a branch
if (if false then true end) then
assert false
end
# loops support both break and continue statements
var a = 1
while a < 10 do
if exitcond then
break
end
if skipcond then
continue
end
echo a
a += 1
end
# break and continue can use labels to target an outer loop
::outer while example_cond() do
some_work()
while inner_cond() do
if exit_inner() then
break
end
if continue_outer() then
continue ::outer
end
if exit_outer() then
break ::outer
end
end
end
do
var cond = example()
while cond
# for loops using iterators
# here, create_iter() returns an "iterator"
for item in create_iter() do
echo item
end
#{
An iterator is one of the following
A 3-tuple: (iterfunc, invariant, i0)
A callable that when invoked with no arguments creates such a 3-tuple
An object that has the __iter metamethod which returns such a 3-tuple
An iterator 3-tuple must do the following:
iterfunc must accept a (i, invariant) and return (i_next, ...) or nil
invariant is a value that is passed to each invocation of iterfunc
i0 is a value that is passed to iterfunc as the initial value of i
Before each execution of the for-loop body, iterfunc is invoked and the first value of the
returned tuple is saved to be used as the next value of i.
Then, the entire tuple is assigned to item, which may be a tuple-destructuring expression.
After the for-loop body is executed, the process is repeated until iterfunc returns nil.
}#
# Functions
make_breakfast(bacon, eggs, toast)
make_breakfast()
# implicitly returns nil
fun echo_sum(a, b)
echo a + b
end
# function parameters can be let or var, just like other variables (let can be omitted)
fun mutable_params(a, var b)
b = a
end
# note: unlike Python, default arguments are re-evaluated with each invocation
fun echo_default(thing = "default")
echo thing
end
# optional type annotations
fun return_sum(a: Float, b: Float = 0.0) -> Float:
return a + b
end
# Closures
fun add_pair(a, b)
return a + b
end
fun identity(a)
return a
end
echo identity(add_pair)(1, 2) # echos "3".
fun outer_function()
fun local_function()
echo "I'm local!"
end
local_function()
end
fun return_functions()
var outside = "outside"
fun print_value()
echo outside
end
fun hide_outside()
let outside = "local"
echo outside
end
fun set_outside()
outside = "inside"
end
return print_value, hide_outside, set_outside
end
let print, hide, set = return_functions()
print() # prints "outside"
hide() # prints "local"
print() # prints "outside"
set()
print() # prints "inside"
let anon_func = fun(x) x + 1 end
let a = anon_func(3)
# Decorator Syntax
# Using "@" syntax you can apply a function to any value being assigned in a declaration, essentially:
@decorator let name = value
# becomes:
let name = decorator(value)
# Since named function and class definitions are just syntactic sugar over a variable declaration,
# You can use this to wrap functions and classes like you might see in other languages like Python.
fun trace(wrapped)
return fun(args...)
echo "trace"
wrapped(args...)
end
end
@trace
fun increment(x)
return x + 1
end
# The decorator can be any expression that evaluates to a callable, as long as the callable takes a single argument:
@some["strange"].decorator(true)
fun example() end
# A contrived example to show that the mechanism is not specific to functions.
fun super_int(n) return 9001 * n end
@super_int
let super_seven = 7
echo super_seven # 63,007
# It could even work with regular assignment, although the usefulness of this is dubious
# I'm debating whether or not this should even be allowed.
@super_int
super_seven /= 1 # makes it un-super thanks to division
echo super_seven # 7
# Classes
something.field = 1
something.method()
class Breakfast
# declare class-level variables
var classvar = 0
# type annotate possible member variables
# unlike function parameters, mutability is the default for object members
var self.field # member named "field" is mutable
let self.name: String # member named "name" is immutable and has type "String"
# functions whose first parameter is the "self" symbol
# are automatically converted into builtin method descripter objects
fun cook(self)
echo "Eggs a-fryin'!"
end
fun mutates_self(var self, value)
self.field = value
end
# even when created using an anonymous function
var serve = fun(self, who)
echo "Enjoy your breakfast, " + who + "."
end
# use classmethods to define constructor functions
fun new(name)
return Breakfast { field = 1, name = "Eggs and Bacon" }
end
end
# Maybe. TBD
Breakfast.cook = fun(self)
echo "Eggs a-fryin'!"
end
# Store it in variables.
let someVariable = Breakfast
# Pass it to functions.
someFunction(Breakfast)
let breakfast = Breakfast {} # object initializer
echo breakfast # "Breakfast instance".
# Instantiation and initialization
# originally initializers used { name: value }, but I want to reserve colons for future type hinting
var breakfast = Breakfast { meat = "sausage", bread = "sourdough" }
breakfast.drink = "coffee"
del breakfast.bread # remove an object member
let a = 0
let anonymous = { some = a = 1, data = 2 }
class Breakfast
fun serve(self, who)
echo "Enjoy your " + self.meat + " and " +
self.bread + ", " + who + "."
end
# ...
end
class Breakfast
# constructor function
fun new(meat, bread)
return Breakfast { meat = meat, bread = bread }
end
end
var baconAndToast = Breakfast.new("bacon", "toast")
baconAndToast.serve("Dear Reader")
# "Enjoy your bacon and toast, Dear Reader."
# Inheritance
class Brunch: Breakfast
fun drink()
echo "How about a Bloody Mary?"
end
end
# Variadic arguments
fun variadic_fun(normal_arg, variadic...)
echo normal_arg
for variadic_arg in variadic do
echo variadic_arg
end
end
fun variadic_default(sequence... = ())
for item in sequence do
echo item
end
end
variadic_fun("red", "blue", "green") # prints "red" then "blue" then "green"
var mylist = list.new("red", "green", "blue") # important use case: constructor functions for collection types
# Argument unpacking
var things = ("second", "third")
variadic_fun("first", things...) # prints "first" then "second"
variadic_fun(things...) # prints "second" then "third"
# named arguments are not supported
# instead, use a function that takes an object as a single argument
# and use an anonymous object literal
lotta_options({ tag = "example", option1 = true, option2 = 3 })
# Metamethods
class Example
fun __setindex(self, index)
echo index
end
fun __call(self, args...)
echo args
end
var __eq = fun(self, other) true end
end
var ex = Example { [0] = "first", [1] = "second" }
ex[2] = "third"
# List of allowed metamethods:
#
# __add, __mul, __sub, __div, __neg, etc...
# __le, __lt, __eq only, no methamethods for others...
# __getindex, __setindex, __delindex
# __bool, __tostring
# __exit - for context managers
# __call
# class/instance names with double underscores are reserved for metamethods
class Example
var __notallowed = true # error
fun __alsonotallowed(self)
echo "bad"
end
end
Example.__fail = -3 # this will result in an error
# Multiple inheritance
class A
fun method(self)
echo "A.method() called"
end
end
class B
fun method(self)
echo "B.method() called"
end
end
class C: A, B end
class D: C, B end
var d = D {}
d.method() # prints "A.method() called"
# see also: C3 Linearization