aboutsummaryrefslogtreecommitdiff
path: root/tutorial.nom
blob: 1b25f69040963c7e2e75a908fa26a32fa02de47e (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
(# Comments use (# ... #), and can be nested #)

(# Import files like so: #)
run file "core.nom"

(# Numbers: #)
23
4.5
(# Since this language cross-compiles to lua, integers and floating point numbers are
    both represented using the same primitive. #)

(# Strings: #)
"asdf"
".."
    |This is a multi-line string
    |that starts with ".." and includes each indented line that starts with a "|"
    |until the indentation ends

(# Lists: #)
[1,2,3]
[..]
    "like multi-line strings, lists have an indented form", "that can use commas too"
    "or just newlines to separate items"
    5
    6,7,8

(# Function calls: #)
say "Hello world!"

(# Function definition: #)
rule "say both %first and also %second":
    (# Variables use the "%" sign: #)
    say %first
    say %second

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

(# Functions can even have their name at the end: #)
rule "%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 functions and variables
    can have really funky names! #)
rule ">> %foo-bar###^ --> %@@& _~-^-~_~-^ %1 !":
    say %foo-bar###^
    say %@@&
    say %1
>> "wow" --> "so flexible!" _~-^-~_~-^ "even numbers can be variables!" !
(# Though literals can't be used in function names #)

(# Math and logic operations are just treated the same as function calls in the syntax #)
say (2 + 3)
(# So it's easy to define your own operators #)
rule "%a ++ %b": 2 * (%a + %b)
say (2 ++ 3)


(# Code blocks start with ":" and either continue until the end of the line
    or are indented blocks #)

(# One liner: #)
: say "hi"

(# Block version: #)
:
    say "one"
    say "two"

(# So the function definitions above are actually just passing a regular string, like
    "say both %first and also %second", and a code block to a function called "rule % %"
    that takes two arguments. #)

(# Line continuations work by either ending a line with ".." and continuing with an indented block: #)
say..
    both "Tom" and
    also
    "Sawyer"

(# Or by starting the next line with ".." #)
say both "Bruce"
..and also "Lee"

(# This can be mixed and matched: #)
say both..
    "Rick"
..and also..
    "Moranis"

(# And combined with the block forms of literals: #)
say both ".."
    |Four score and seven years ago our fathers brought forth, upon this continent,
    |a new nation, conceived in liberty, and dedicated to the proposition that
    |"all men are created equal"
..and also..
    "-- Abraham Lincoln"

rule "my favorite number": return 23
(# Subexpressions are wrapped in parentheses: #)
(# printf takes a list of bits that are converted to strings and concatenated together, and printed #)
printf ["My favorite number is ", my favorite number]
(# There's a multi-line indented block form for subexpressions too: #)
printf [..]
    "My favorite number is still ", (..)
        my favorite
        number

(# There's a few macros in the language for things like conditional branches and logic/math
    operations, but they can be thought of as basically the same as functions.
    There are no keywords in the language! #)
if (1 < 10):
    say "One is less than ten"
..else:
    say "One is not less than ten"

(# Breakdown of the above: #)
(# Function call (actually a macro) to "if % % else %" #)
(# First argument is a subexpression that is a function call (also a macro) to "% < %"
    that performs a comparison on its arguments, 1 and 10 #)
(# Second argument is a block of code that includes a function call to "say %", the "if" body #)
(# Third argument is a block of code that includes a different function call to "say %", the "else" body #)

(# Line continuations can be used for "elseif" #)
if (1 > 10):
    say "First condition"
..else: if (1 > 5):
    say "Second condition"
..else:
    say "Last condition"

(# ^that's the same as: #)
if (1 > 10):
    say "First condition"
..else:
    if (1 > 5):
        say "Second condition"
    ..else:
        say "Last condition"

(# Variables are modified with a macro, "let % = %" #)
let "numbers" = [5,6,7]

(# Looping: #)
printf ["Looping over: ",%numbers,"!"]
for "number" in %numbers:
    say (%number + 100)

rule "sing %starting-bottles bottles of beer":
    for "n" in (%starting-bottles down through 0):
        printf [..]
            (%n if (%n > 0) else "No more")
            (" bottle" if (%n == 1) else " bottles")
            " of beer on the wall."
            ("" if (%n == 0) else " Take one down, pass it around...")

sing 9 bottles of beer


(# Note that because math and logic operations are just macros, they require a lot
    of parentheses to disambiguate. There's no PEMDAS. #) 
say (5 + (4 * (- (1 + (6 + 2)))))
(# For convenience, +,*,"and", and "or" have been hand defined to work with up to 4 operands: #)
1 + 2 + 3 + 4
1 * 2 * 3 * 4
1 and 2 and 3 and 4
1 or 2 or 3 or 4
(# Longer lists can use "sum of %", "product of %", "all of %", and "any of %", respectively, or lots of parentheses. #)
sum of [1,2,3,4,5,6,7]
product of [1,2,3,4,5,6,7]
all of [1,1,1,1,0,1,1]
any of [0,0,0,0,1,0,0]
(# And 3-operand chained inequality comparisons have been defined: #)
1 < 2 <= 3


(# Macros: #)
(# The "lua block %" and "lua expr %" macros can be used to write raw lua code: #)
rule "say time":
    lua block ".."
        |io.write("The OS time is: ")
        |io.write(tostring(os.time()).."\n")
say time
printf ["Math expression: ", lua expr "(1 + 2*3 + 3*4)^2"]
(# In the lua environment, "vars" can be used to get local variables/function args, and "game"
    can be used to access the compiler, function defs, and other things #)
rule "square root of %n":
    return (lua expr "math.sqrt(vars.n)")
printf ["the square root of 2 is ", square root of 2]

(# Macros can be defined as functions that take unprocessed syntax trees and return lua code #)
(# "macro block %" is for defining macros that produce blocks of code, not values #)
macro block "unless %condition %body":
    concat [..]
        (# "% as lua expr" and "% as lua block" are two useful helper functions here. #)
        "if not (", %condition as lua expr, ") then"
        (# Extract the inner part of the code block's body and insert it: #)
        "\n    ", (lua expr "vars.body.value.value") as lua block
        "\nend"

unless (1 > 10):
    say "Macros work!"
    say "It looks like a keyword, but there's no magic here!"

(# and "macro %" is for defining macros that produce an expression #)
macro "%value as a boolean":
    concat ["(not not (", %value as lua expr, "))"]
macro "yep": "true"