-
Notifications
You must be signed in to change notification settings - Fork 0
/
intro.luma
273 lines (216 loc) · 6.43 KB
/
intro.luma
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
(comments are in parentheses, can nest, and can
span multiple lines)
(the following are special characters: []().:')
(for now everything else can be used in words)
(numbers, strings, words)
1
'string'
a
foo
a-word-with-dashes
(keywords: # !)
(special forms: import if when while set and or)
(application is like lisp)
[+ 1 2] (returns 3)
(brackets can be omitted at the start of a line)
+ 1 2 (the same)
(arguments can be indented, one per line)
+
1
2 (also returns 3)
(exception: with one expression alone, and no
indented body, then brackets are needed if you
want to call it)
[fn]
(this returns the fn without applying it)
fn
(the general rule is: if a line has multiple
top-level items (including indented parts) it's
wrapped in [], otherwise it isn't)
(brackets are used to group function calls)
+ 1 [* 2 3] (evaluates to 7)
+ 1
* 2 3 (the same)
(a pair is a key expression followed by a colon
and a value expression)
plus: +
(in a body, pairs define variables)
one: 1
two: 2
plus one two (evaluates to 3)
(in special forms, pairs can have other
meanings: see when and while later)
(set variables with `set`)
a: 1
print a (prints 1)
set a 2
print a (prints 2)
(functions can be defined using brackets []
around the key in a pair. think of it like
a pattern match)
(for example, this defines a function `fn` that
takes a and b as arguments and adds them)
[fn a b]: + a b
[fn 1 2] (returns 3)
(putting a . as the function name makes an
anonymous function instead)
[. a b]: + a b
(if a colon is followed by a newline, then the
indented region is treated like a body, and the
expressions are evaluated one by one)
three:
one: 1
two: 2
+ one two (the last item is returned)
(putting a dot in front of a word makes it a symbol)
.abc
(methods / fields are used by putting
a symbol after an expression)
'asdf'.length
(this is sort of like calling 'asdf' with
the symbol .length as an argument, except
that symbols aren't passed as regular arguments)
(symbols associate with the item on their left,
so brackets may be omited more often)
print 'asdf'.length (prints 4)
(symbol applications at the start of a line can
take arguments)
'asdf'.substring 1 3 (returns 'sd')
(this calls 'asdf' with arguments
.substring, 1, and 3)
(when not starting a line, brackets are
used to call a method with arguments)
print 'asdf'.substring 1 3 (doesn't work,
tries to print ['asdf'.substring], 1,
and 3 as three separate values)
print ['asdf'.substring 1 3] (prints 'sd')
(arithmetic is implemented as method calls,
so these two are the same)
+ 1 2
1 .+ 2
(the + function is implemented like this)
[+ a b]: a .+ b
(if always has two branches, each a single
expression, and returns one of them,
like a ternary expression)
if [a .< 0]
'true expression'
'false expression'
(and returns the first value if it's false,
otherwise evaluates and returns the second value)
(or returns the first value if it's not false,
otherwise evaluates and returns the second value)
and 1 2 (returns 2)
or 1 2 (returns 1)
and something-that-must-be-falsy [error 'it wasnt falsy!']
or something-that-must-be-truthy [error 'it wasnt truthy!']
(when uses condition: result pairs instead)
when
[a .< 1]:
'first then block goes here'
[a .< 2]:
'another then block'
(only the first matching one is executed)
else:
'else catches other cases (optional)'
'like other bodies, can include multiple'
'indented expressions'
(unlike `if`, `when` lets you:
- have more than one condition,
- omit the else case,
- have bodies with multiple expressions)
(when you only have one condition / body, putting
the condition on the same line is more compact)
when [a .< 1]:
'first expression'
'more expressions can go here,'
'they are all executed'
(note that when pairs aren't the first thing on a
line, they only accept one value on the right of
the colon)
when [a .< 1]: f 1 2 (not valid)
when [a .< 1]: [f 1 2] (put brackets)
when [a .< 1]:
f 1 2 (indenting the body also works)
(while takes a condition/result pair, repeats the
result while the condition is true)
a: 0
while [a .< 100]:
print a
set a [a .+ 1] (prints numbers from 0-99)
(`#` creates a table, or object)
obj: [#] (empty object)
(i'm thinking of renaming this to `table`, or
splitting it into `object` and `type`, which are
the two things it's used for)
(tables can have fields)
tbl: #
.field-a: 1
.field-b: 2
tbl.field-a (gets field-a)
set tbl.field-a 3 (sets field-a)
(including anonymous functions in tables lets you
call the table with the corresponding number of
arguments, this is how you overload a function
depending on number of arguments)
tbl2: #
[.]: tbl2 1 2
[. a b]: + a b
[tbl2] (returns 3)
tbl2 2 3 (returns 5)
(including a symbol as the first argument lets you
call the table with that symbol)
tbl3: #
[. .something]: 1
(symbol-functions can have arguments)
[. .something-else a b]: + a b
tbl3.something (returns 1)
tbl3.something-else 1 2 (returns 3)
(tables can also be used as types, providing
methods to other tables)
some-type: #
(methods are defined by putting a name
before the symbol (usually but not
always `self`))
[self.foo a]: + a self.field
(methods are not called on the table itself, but
w hen the table is used as the type for another
table)
(pass the type object `some-type` when making a
table to allow the methods to be used on it)
tbl4: # some-type
.field: 1
tbl4.foo 2 (returns 3)
(this is used to implement classes)
(by convention, classes are named with words that
start with `#`)
(here's a two dimensional vector class)
#vec2: #
(invoking [#vec2 x y] calls this constructor,
which makes a new table that inherits #vec2's
methods)
[. x y]: # #vec2
.x: x
.y: y
(this plus method will be available on instances
of #vec2 (but not directly on the #vec2 class))
[a .+ b]:
#vec2 [a.x .+ b.x]
[a.y .+ b.y]
[vec2 1 2] .+ [vec2 3 4] (returns [vec2 4 6])
+ [vec2 1 2] [vec2 3 4] (the same)
(note that only methods are inherited in this way,
not other functions or fields)
(todo: import, module exports, and, or)
(import brings stuff from another file into scope)
import 'prelude' (most files start with this)
(a file that's imported in this way should end in
a table literal with fields for each thing to be
imported:)
#
.name: name
(or)
.exported-name: internal-name
(imported fields are used without the leading .)
(currently all imported names are visible globally,
probably this will change to file scope later)