aboutsummaryrefslogtreecommitdiff
path: root/examples/how_do_i.nom
blob: 107b987b3227cfb42cf5d129c8553658ea3d7a18 (plain)
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
# How do I...
# Write a comment? Put a # and go till the end of the line

# How do I write a multi-line comment?
    After a comment line, any indented text
    is considered part of the comment
        (including any deeper-level indented text)
    The comment ends when the indentation ends

# How do I import a file?
use "core/control_flow.nom"

# How do I import all the files in a directory?
use "core"

# How do I print stuff?
say "Hello world!"

# How do I set a variable?
%foobar <- 1
%text <- "Hello world"
# Expressions that are more than just literal values require parentheses:
%foobar <- (2 + 3)
%one-two <- 12
say %one-two

# How do I modify a variable?
%foobar <- (%foobar + 1)
# Or, as a shorthand, you can do this to increment a variable:
%foobar +<- 1

# How do I define a mutli-line string?
# In Nomsu, "strings" are called "text", and multi-line text looks like:
%mutli-text <- ".."
    Start with "..", then put indented lines below it. The indented lines will not include
    the indentation, except when the lines are indented more than 4 spaces relative
    to the "..".
      <- E.g. the 2 spaces here will be included as part of the text.
    But this line will have no leading spaces.

    The text will continue until the indentation ends, skipping trailing newlines.

# How do I put values inside text? (AKA string formatting, string interpolation)
say ".."
    Text can contain a backslash followed by a variable, list, dict, or parenthesized
    expression. This escaped value will be converted to readable text, like so:
    The value of %foobar is \%foobar, isn't that nice?
    These are some numbers: \[1+1, 2+1, 3+1]
    The sum of 2 and 4 is \(2 + 4).

    A backslash not followed by any of these, and not at the end of a line
    like this: \ will just be treated as a backslash.

    Or, two backlashes will be treated as a single backslash, no matter what follows,
    like this: \\%foobar <- won't insert any values

    If you need to split a long line without inserting a newline, you can \
    ..end a line with backslash and start the next line with two periods, like that.

    Similarly, you can put a long interpolated indented value like: \
        1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9
    .. between a backslash and two periods.

say "Single-line text can contain escape sequences like \", \\, \n, \065, and \x0A"

# How do I define a list?
%my-list <- [1,2,"hello"]
# Really long lists can use [..] followed by a bunch of indented values delimited
    by commas and/or newlines
%my-really-long-list <- [..]
    1,2,3,4
    5,6
    7
    8,9,10

# How do I use a list?
%my-list <- ["first item", "second item", "third item"]
# Lists are 1-indexed because they're implemented as Lua tables, so this prints "first item"
say %my-list.1
# List entries can be modified like this:
%my-list.1 <- "ONE!!!"
say (length of %my-list)

# How do I define a dictionary/hash map?
%my-dict <- {x: 99, y: 101}
%my-dict <- {..}
    x: 101, y: 2
    "99 bottles": 99
    653: 292

# How do I use a dict?
# Dicts are also implemented as Lua tables, so they're accessed and modified the same way as lists
say %my-dict.x
%my-dict.x <- 9999

# How do I do conditional branching?
if: 1 < 10
    say "1 is indeed < 10"

if: 1 > 10
    say "this won't print"
..else
    say "this will print"

# There's no "elseif", so for longer conditionals, a "when" branch is the best option
when
    * (3 > 6)
    * (3 > 5)
    * (3 > 4)
        say "this won't print"
    * (3 > 3)
        say "this won't print"
    * (3 > 2)
        say "this will print"
    * else
        say "this is the default case"

# How do I do a switch statement?
when 3 = ?
    * 0
    * 1
    * 2
        say "this won't print"
    * 3
        say "this will print"
    * else
        say "this won't print"

# How do I loop over a list (a foreach loop)?
%list <- [1,2,3]
for %x in %list
    say "For %x loop #\%x"

# How do I loop over a number range?
# This is inclusive, so it will loop over 1,2, and 3
for %i in 1 to 3
    say "For %i in 1 to 3 loop #\%i"
# This will print 0,2, and 4
for %even in 0 to 5 by 2
    say "Even #\%even"
for %backwards in 3 to 1 by -1
    say "Backwards #\%backwards"

# How do I do a 'while' loop?
%x <- 1
repeat while: %x <= 3
    say "repeat while loop #\%x"
    %x +<- 1

%x <- 1
repeat until: %x > 3
    say "repeat until loop #\%x"
    %x +<- 1

# How do I do an infinite loop?
%x <- 1
repeat
    say "repeat loop #\%x"
    %x +<- 1
    if: %x > 3
        stop repeating

# How do I do a 'goto'?
do
    %x <- 1
    === %again ===
    say "GOTO loop #\%x"
    %x +<- 1
    if: %x <= 3
        go to %again
    say "finished going to"


# How do I define a function/method/procedure?
# In nomsu, they're called "action"s, and they can be declared like this:
action [say both %first and also %second]
    say %first
    # Function arguments are accessed just like variables
    say %second

# Actions can use "return" to return a value early
action [first fibonacci above %n]
    %f1 <- 0
    %f2 <- 1
    repeat
        %tmp <- (%f1 + %f2)
        %f1 <- %f2
        %f2 <- %tmp
        if: %f2 > %n
            return %f2

say (first fibonacci above 10)

# Actions can have aliases, which may or may not have the arguments in different order
action [..]
    I hate %worse-things more than %better-things
    I think %worse-things are worse than %better-things
    I like %better-things more than %worse-things
..
    say "\(%better-things capitalized) rule and \%worse-things drool!"

I like "dogs" more than "cats"
I think "chihuahuas" are worse than "corgis"


# Actions can have parts of the action's name spread throughout.
    Everything that's not a literal value is treated as part of the action's name
say both "Hello" and also "again!"

# Actions can even start with a parameter
action [%what-she-said is what she said]
    say %what-she-said
    say "-- she said"

"Howdy pardner" is what she said

# The language only reserves []{}().,:;% as special characters, so actions
    can have really funky names!
action [>> %foo-bar $$$^ --> % @& _~-^-~_~-^ %1 !]
    say %foo-bar
    say %
    say %1

>> "wow" $$$^ --> "so flexible!" @& _~-^-~_~-^ "even numbers can be variables!" !

# There's also full unicode support
%こんにちは <- "こんにちは"
action [% と言う]
    "\(%)世界"
say (%こんにちは と言う)

# Math and logic operations are just treated the same as actions in the syntax
say (2 + 3)
# So you can define your own operators, although they will need to be parenthesized to
    play nicely with other operators
action [%a ++ %b]
    2 * (%a + %b)
say (1 ++ (2 * 3))


# How do I do grouping?
# Expressions can be grouped by enclosing parentheses:
say (2 + 3)
# Or by an indented region
say
    2 + 3
# Or by ":" until the end of the line
say: 2 + 3
# If you need to keep going after an indented region, you can start the next line with ".."
say both
    "Very long first argument that needs its own line"
..and also "short second arg"

action [my favorite number]: 21 + 2

# This can be nested:
say both
    my favorite
    ..number
..and also "foo"


# Macros: 
# The "lua> %" and "=lua %" macros can be used to write raw lua code: 
action [say the time]
    lua> ".."
        io.write("The OS time is: "..os.time().."\n");
say the time
say "Math expression result is: \(=lua "(1 + 2*3 + 3*4)^2")"

# Variables can be accessed via \%varname
action [square root of %n]
    =lua "math.sqrt(\%n)"
say "The square root of 2 is \(square root of 2)"

# The "immediately %" macro forces the indented code below it to run before the rest of
    the file finishes compiling, so it's often useful for writing your own macros.
immediately
    # Macros can be defined to transform one bit of nomsu code into another using "parse % as %":
    parse [if %condition is untrue %body] as
        if (not %condition) %body

    # Or to transform nomsu code into custom lua code using "compile % to %"
    compile [if %condition on opposite day %body] to
        Lua ".."
            if not \(%condition as lua expr) then
                \(%body as lua statements)
            end
    
    # Constants can be defined as macros
    parse [TWENTY] as: 20
    # When they're invoked, they'll need parentheses just like a function call
    parse [TWENTY ONE] as: 21

if (1 > (TWENTY)) is untrue
    say "Nomsu parsing macros work!"
    say "It looks like a keyword, but there's no magic here!"

if (1 > (TWENTY)) on opposite day
    say "Lua compiling macros work!"
    say "It looks like a keyword, but there's no magic here!"


# How do I use an action as a value?
# Well... it's always *possible* to fall back to Lua behavior for something like this:
action [best of %items according to %key-fn]
    <- {%best:nil, %best-key:nil}
    for % in %items
        %key <- (=lua "\%key-fn(\%)")
        if: (%best is (nil)) or (%key > %best-key)
            <- {%best:%, %best-key:%key}
    return %best

immediately
    compile [function %var %body] to
        Lua value ".."
            (function(\(%var as lua expr))
                return \(%body as lua expr)
            end)

say: best of [2,-3,4,-8] according to (function %: % * %)

# But nomsu was mostly designed so that you don't *need* to. Instead of creating a
    one-off function to pass to another function and get called a bunch of times, you
    could use a macro to generate a single block of code that inlines the expression you
    want to use:
immediately
    parse [best of %items according to %key-var in %key-expr] as
        result of
            <- {%best:nil, %best-key:nil}
            for %key-var in %items
                %key <- %key-expr
                if: (%best is (nil)) or (%key > %best-key)
                    <- {%best:%, %best-key:%key}
            return %best
# This results in generated code that is more efficient (no function calls in the
    inner loop)
say: best of [2,-3,4,-8] according to % in (% * %)

# In a functional programming language, you might do something like:
        doubled = map(list, function(x) return 2 * x end)
    to get a new list with every entry multiplied by 2, but it's *much* more readable to
    do something like:
%nums <- [1,2,3,4,5]
%double-nums <- ((2 * %num) for %num in %nums)

# Nomsu comes with built-in list comprehensions, but the flexible macro system makes it
    incredibly easy to make similar constructs.
immediately
    parse [%expr for %key in %list BACKWARDS!] as
        result of
            %result <- []
            %N <- (length of %list)
            for %i = %key in %list
                %result.(%N - %i + 1) <- %expr
            return %result

%double-nums <- ((2 * %num) for %num in %nums BACKWARDS!)
say %double-nums