summary refs log tree commit diff stats
path: root/doc/manual/stmts.txt
blob: a833d7b7da56dfbcab37e6c98893651317da4356 (plain) (blame)
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
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
Statements and expressions
==========================

Nim uses the common statement/expression paradigm: Statements do not
produce a value in contrast to expressions. However, some expressions are
statements.

Statements are separated into `simple statements`:idx: and
`complex statements`:idx:.
Simple statements are statements that cannot contain other statements like
assignments, calls or the ``return`` statement; complex statements can
contain other statements. To avoid the `dangling else problem`:idx:, complex
statements always have to be indented. The details can be found in the grammar.


Statement list expression
-------------------------

Statements can also occur in an expression context that looks
like ``(stmt1; stmt2; ...; ex)``. This is called
an statement list expression or ``(;)``. The type
of ``(stmt1; stmt2; ...; ex)`` is the type of ``ex``. All the other statements
must be of type ``void``. (One can use ``discard`` to produce a ``void`` type.)
``(;)`` does not introduce a new scope.


Discard statement
-----------------

Example:

.. code-block:: nim
  proc p(x, y: int): int =
    result = x + y

  discard p(3, 4) # discard the return value of `p`

The ``discard`` statement evaluates its expression for side-effects and
throws the expression's resulting value away.

Ignoring the return value of a procedure without using a discard statement is
a static error.

The return value can be ignored implicitly if the called proc/iterator has
been declared with the `discardable`:idx: pragma:

.. code-block:: nim
  proc p(x, y: int): int {.discardable.} =
    result = x + y

  p(3, 4) # now valid

An empty ``discard`` statement is often used as a null statement:

.. code-block:: nim
  proc classify(s: string) =
    case s[0]
    of SymChars, '_': echo "an identifier"
    of '0'..'9': echo "a number"
    else: discard


Void context
------------

In a list of statements every expression except the last one needs to have the
type ``void``. In addition to this rule an assignment to the builtin ``result``
symbol also triggers a ``void`` context:

.. code-block:: nim
  proc invalid*(): string =
    result = "foo"
    "invalid"  # Error: value of type 'string' has to be discarded

.. code-block:: nim
  proc valid*(): string =
    let x = 317
    "valid"


Var statement
-------------

Var statements declare new local and global variables and
initialize them. A comma separated list of variables can be used to specify
variables of the same type:

.. code-block:: nim

  var
    a: int = 0
    x, y, z: int

If an initializer is given the type can be omitted: the variable is then of the
same type as the initializing expression. Variables are always initialized
with a default value if there is no initializing expression. The default
value depends on the type and is always a zero in binary.

============================    ==============================================
Type                            default value
============================    ==============================================
any integer type                0
any float                       0.0
char                            '\\0'
bool                            false
ref or pointer type             nil
procedural type                 nil
sequence                        nil (*not* ``@[]``)
string                          nil (*not* "")
tuple[x: A, y: B, ...]          (default(A), default(B), ...)
                                (analogous for objects)
array[0..., T]                  [default(T), ...]
range[T]                        default(T); this may be out of the valid range
T = enum                        cast[T](0); this may be an invalid value
============================    ==============================================


The implicit initialization can be avoided for optimization reasons with the
`noinit`:idx: pragma:

.. code-block:: nim
  var
    a {.noInit.}: array [0..1023, char]

If a proc is annotated with the ``noinit`` pragma this refers to its implicit
``result`` variable:

.. code-block:: nim
  proc returnUndefinedValue: int {.noinit.} = discard


The implicit initialization can be also prevented by the `requiresInit`:idx:
type pragma. The compiler requires an explicit initialization then. However
it does a `control flow analysis`:idx: to prove the variable has been
initialized and does not rely on syntactic properties:

.. code-block:: nim
  type
    MyObject = object {.requiresInit.}

  proc p() =
    # the following is valid:
    var x: MyObject
    if someCondition():
      x = a()
    else:
      x = a()
    use x


let statement
-------------

A ``let`` statement declares new local and global `single assignment`:idx:
variables and binds a value to them. The syntax is the same as that of the ``var``
statement, except that the keyword ``var`` is replaced by the keyword ``let``.
Let variables are not l-values and can thus not be passed to ``var`` parameters
nor can their address be taken. They cannot be assigned new values.

For let variables the same pragmas are available as for ordinary variables.


Tuple unpacking
---------------

In a ``var`` or ``let`` statement tuple unpacking can be performed. The special
identifier ``_`` can be used to ignore some parts of the tuple:

.. code-block:: nim
    proc returnsTuple(): (int, int, int) = (4, 2, 3)

    let (x, _, z) = returnsTuple()



Const section
-------------

`Constants`:idx: are symbols which are bound to a value. The constant's value
cannot change. The compiler must be able to evaluate the expression in a
constant declaration at compile time.

Nim contains a sophisticated compile-time evaluator, so procedures which
have no side-effect can be used in constant expressions too:

.. code-block:: nim
  import strutils
  const
    constEval = contains("abc", 'b') # computed at compile time!


The rules for compile-time computability are:

1. Literals are compile-time computable.
2. Type conversions are compile-time computable.
3. Procedure calls of the form ``p(X)`` are compile-time computable if
   ``p`` is a proc without side-effects (see the `noSideEffect pragma
   <#pragmas-nosideeffect-pragma>`_ for details) and if ``X`` is a
   (possibly empty) list of compile-time computable arguments.


Constants cannot be of type ``ptr``, ``ref``, ``var`` or ``object``, nor can
they contain such a type.


Static statement/expression
---------------------------

A static statement/expression can be used to enforce compile
time evaluation explicitly. Enforced compile time evaluation can even evaluate
code that has side effects:

.. code-block::

  static:
    echo "echo at compile time"

It's a static error if the compiler cannot perform the evaluation at compile
time.

The current implementation poses some restrictions for compile time
evaluation: Code which contains ``cast`` or makes use of the foreign function
interface cannot be evaluated at compile time. Later versions of Nim will
support the FFI at compile time.


If statement
------------

Example:

.. code-block:: nim

  var name = readLine(stdin)

  if name == "Andreas":
    echo("What a nice name!")
  elif name == "":
    echo("Don't you have a name?")
  else:
    echo("Boring name...")

The ``if`` statement is a simple way to make a branch in the control flow:
The expression after the keyword ``if`` is evaluated, if it is true
the corresponding statements after the ``:`` are executed. Otherwise
the expression after the ``elif`` is evaluated (if there is an
``elif`` branch), if it is true the corresponding statements after
the ``:`` are executed. This goes on until the last ``elif``. If all
conditions fail, the ``else`` part is executed. If there is no ``else``
part, execution continues with the next statement.

In ``if`` statements new scopes begin immediately after the ``if``/``elif``/``else`` keywords and ends after the corresponding *then* block.
For visualization purposes the scopes have been enclosed in ``{|  |}`` in the following example:

.. code-block:: nim
  if {| (let m = input =~ re"(\w+)=\w+"; m.isMatch):
    echo "key ", m[0], " value ", m[1]  |}
  elif {| (let m = input =~ re""; m.isMatch):
    echo "new m in this scope"  |}
  else: {|
    echo "m not declared here"  |}

Case statement
--------------

Example:

.. code-block:: nim

  case readline(stdin)
  of "delete-everything", "restart-computer":
    echo("permission denied")
  of "go-for-a-walk":     echo("please yourself")
  else:                   echo("unknown command")

  # indentation of the branches is also allowed; and so is an optional colon
  # after the selecting expression:
  case readline(stdin):
    of "delete-everything", "restart-computer":
      echo("permission denied")
    of "go-for-a-walk":     echo("please yourself")
    else:                   echo("unknown command")


The ``case`` statement is similar to the if statement, but it represents
a multi-branch selection. The expression after the keyword ``case`` is
evaluated and if its value is in a *slicelist* the corresponding statements
(after the ``of`` keyword) are executed. If the value is not in any
given *slicelist* the ``else`` part is executed. If there is no ``else``
part and not all possible values that ``expr`` can hold occur in a
``slicelist``, a static error occurs. This holds only for expressions of
ordinal types. "All possible values" of ``expr`` are determined by ``expr``'s
type. To suppress the static error an ``else`` part with an
empty ``discard`` statement should be used.

For non ordinal types it is not possible to list every possible value and so
these always require an ``else`` part.

As a special semantic extension, an expression in an ``of`` branch of a case
statement may evaluate to a set or array constructor; the set or array is then
expanded into a list of its elements:

.. code-block:: nim
  const
    SymChars: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'}

  proc classify(s: string) =
    case s[0]
    of SymChars, '_': echo "an identifier"
    of '0'..'9': echo "a number"
    else: echo "other"

  # is equivalent to:
  proc classify(s: string) =
    case s[0]
    of 'a'..'z', 'A'..'Z', '\x80'..'\xFF', '_': echo "an identifier"
    of '0'..'9': echo "a number"
    else: echo "other"


When statement
--------------

Example:

.. code-block:: nim

  when sizeof(int) == 2:
    echo("running on a 16 bit system!")
  elif sizeof(int) == 4:
    echo("running on a 32 bit system!")
  elif sizeof(int) == 8:
    echo("running on a 64 bit system!")
  else:
    echo("cannot happen!")

The ``when`` statement is almost identical to the ``if`` statement with some
exceptions:

* Each condition (``expr``) has to be a constant expression (of type ``bool``).
* The statements do not open a new scope.
* The statements that belong to the expression that evaluated to true are
  translated by the compiler, the other statements are not checked for
  semantics! However, each condition is checked for semantics.

The ``when`` statement enables conditional compilation techniques. As
a special syntactic extension, the ``when`` construct is also available
within ``object`` definitions.


When nimvm statement
--------------------

``nimvm`` is a special symbol, that may be used as expression of ``when nimvm``
statement to differentiate execution path between runtime and compile time.

Example:

.. code-block:: nim
  proc someProcThatMayRunInCompileTime(): bool =
    when nimvm:
      # This code runs in compile time
      result = true
    else:
      # This code runs in runtime
      result = false
  const ctValue = someProcThatMayRunInCompileTime()
  let rtValue = someProcThatMayRunInCompileTime()
  assert(ctValue == true)
  assert(rtValue == false)

``when nimvm`` statement must meet the following requirements:

* Its expression must always be ``nimvm``. More complex expressions are not
  allowed.
* It must not contain ``elif`` branches.
* It must contain ``else`` branch.
* Code in branches must not affect semantics of the code that follows the
  ``when nimvm`` statement. E.g. it must not define symbols that are used in
  the following code.

Return statement
----------------

Example:

.. code-block:: nim
  return 40+2

The ``return`` statement ends the execution of the current procedure.
It is only allowed in procedures. If there is an ``expr``, this is syntactic
sugar for:

.. code-block:: nim
  result = expr
  return result


``return`` without an expression is a short notation for ``return result`` if
the proc has a return type. The `result`:idx: variable is always the return
value of the procedure. It is automatically declared by the compiler. As all
variables, ``result`` is initialized to (binary) zero:

.. code-block:: nim
  proc returnZero(): int =
    # implicitly returns 0


Yield statement
---------------

Example:

.. code-block:: nim
  yield (1, 2, 3)

The ``yield`` statement is used instead of the ``return`` statement in
iterators. It is only valid in iterators. Execution is returned to the body
of the for loop that called the iterator. Yield does not end the iteration
process, but execution is passed back to the iterator if the next iteration
starts. See the section about iterators (`Iterators and the for statement`_)
for further information.


Block statement
---------------

Example:

.. code-block:: nim
  var found = false
  block myblock:
    for i in 0..3:
      for j in 0..3:
        if a[j][i] == 7:
          found = true
          break myblock # leave the block, in this case both for-loops
  echo(found)

The block statement is a means to group statements to a (named) ``block``.
Inside the block, the ``break`` statement is allowed to leave the block
immediately. A ``break`` statement can contain a name of a surrounding
block to specify which block is to leave.


Break statement
---------------

Example:

.. code-block:: nim
  break

The ``break`` statement is used to leave a block immediately. If ``symbol``
is given, it is the name of the enclosing block that is to leave. If it is
absent, the innermost block is left.


While statement
---------------

Example:

.. code-block:: nim
  echo("Please tell me your password: \n")
  var pw = readLine(stdin)
  while pw != "12345":
    echo("Wrong password! Next try: \n")
    pw = readLine(stdin)


The ``while`` statement is executed until the ``expr`` evaluates to false.
Endless loops are no error. ``while`` statements open an `implicit block`,
so that they can be left with a ``break`` statement.


Continue statement
------------------

A ``continue`` statement leads to the immediate next iteration of the
surrounding loop construct. It is only allowed within a loop. A continue
statement is syntactic sugar for a nested block:

.. code-block:: nim
  while expr1:
    stmt1
    continue
    stmt2

Is equivalent to:

.. code-block:: nim
  while expr1:
    block myBlockName:
      stmt1
      break myBlockName
      stmt2


Assembler statement
-------------------

The direct embedding of assembler code into Nim code is supported
by the unsafe ``asm`` statement. Identifiers in the assembler code that refer to
Nim identifiers shall be enclosed in a special character which can be
specified in the statement's pragmas. The default special character is ``'`'``:

.. code-block:: nim
  {.push stackTrace:off.}
  proc addInt(a, b: int): int =
    # a in eax, and b in edx
    asm """
        mov eax, `a`
        add eax, `b`
        jno theEnd
        call `raiseOverflow`
      theEnd:
    """
  {.pop.}

If the GNU assembler is used, quotes and newlines are inserted automatically:

.. code-block:: nim
  proc addInt(a, b: int): int =
    asm """
      addl %%ecx, %%eax
      jno 1
      call `raiseOverflow`
      1:
      :"=a"(`result`)
      :"a"(`a`), "c"(`b`)
    """

Instead of:

.. code-block:: nim
  proc addInt(a, b: int): int =
    asm """
      "addl %%ecx, %%eax\n"
      "jno 1\n"
      "call `raiseOverflow`\n"
      "1: \n"
      :"=a"(`result`)
      :"a"(`a`), "c"(`b`)
    """

Using statement
---------------

**Warning**: The ``using`` statement is highly experimental and has to be
explicitly enabled with the `experimental`:idx: pragma or command line option!

The using statement provides syntactic convenience for procs that
heavily use a single contextual parameter. When applied to a variable or a
constant, it will instruct Nim to automatically consider the used symbol as
a hidden leading parameter for any procedure calls, following the using
statement in the current scope. Thus, it behaves much like the hidden `this`
parameter available in some object-oriented programming languages.

.. code-block:: nim

  var s = socket()
  using s

  connect(host, port)
  send(data)

  while true:
    let line = readLine(timeout)
    ...


When applied to a callable symbol, it brings the designated symbol in the
current scope. Thus, it can be used to disambiguate between imported symbols
from different modules having the same name.

.. code-block:: nim
  import windows, sdl
  using sdl.SetTimer

Note that ``using`` only *adds* to the current context, it doesn't remove or
replace, **neither** does it create a new scope. What this means is that if one
applies this to multiple variables the compiler will find conflicts in what
variable to use:

.. code-block:: nim
  var a, b = "kill it"
  using a
  add(" with fire")
  using b
  add(" with water")
  echo a
  echo b

When the compiler reaches the second ``add`` call, both ``a`` and ``b`` could
be used with the proc, so one gets ``Error: expression '(a|b)' has no type (or
is ambiguous)``. To solve this one would need to nest ``using`` with a
``block`` statement so as to control the reach of the ``using`` statement.

If expression
-------------

An `if expression` is almost like an if statement, but it is an expression.
Example:

.. code-block:: nim
  var y = if x > 8: 9 else: 10

An if expression always results in a value, so the ``else`` part is
required. ``Elif`` parts are also allowed.

When expression
---------------

Just like an `if expression`, but corresponding to the when statement.

Case expression
---------------

The `case expression` is again very similar to the case statement:

.. code-block:: nim
  var favoriteFood = case animal
    of "dog": "bones"
    of "cat": "mice"
    elif animal.endsWith"whale": "plankton"
    else:
      echo "I'm not sure what to serve, but everybody loves ice cream"
      "ice cream"

As seen in the above example, the case expression can also introduce side
effects. When multiple statements are given for a branch, Nim will use
the last expression as the result value, much like in an `expr` template.

Table constructor
-----------------

A table constructor is syntactic sugar for an array constructor:

.. code-block:: nim
  {"key1": "value1", "key2", "key3": "value2"}

  # is the same as:
  [("key1", "value1"), ("key2", "value2"), ("key3", "value2")]


The empty table can be written ``{:}`` (in contrast to the empty set
which is ``{}``) which is thus another way to write as the empty array
constructor ``[]``. This slightly unusual way of supporting tables
has lots of advantages:

* The order of the (key,value)-pairs is preserved, thus it is easy to
  support ordered dicts with for example ``{key: val}.newOrderedTable``.
* A table literal can be put into a ``const`` section and the compiler
  can easily put it into the executable's data section just like it can
  for arrays and the generated data section requires a minimal amount
  of memory.
* Every table implementation is treated equal syntactically.
* Apart from the minimal syntactic sugar the language core does not need to
  know about tables.


Type conversions
----------------
Syntactically a `type conversion` is like a procedure call, but a
type name replaces the procedure name. A type conversion is always
safe in the sense that a failure to convert a type to another
results in an exception (if it cannot be determined statically).

Ordinary procs are often preferred over type conversions in Nim: For instance,
``$`` is the ``toString`` operator by convention and ``toFloat`` and ``toInt``
can be used to convert from floating point to integer or vice versa.


Type casts
----------
Example:

.. code-block:: nim
  cast[int](x)

Type casts are a crude mechanism to interpret the bit pattern of
an expression as if it would be of another type. Type casts are
only needed for low-level programming and are inherently unsafe.


The addr operator
-----------------
The ``addr`` operator returns the address of an l-value. If the type of the
location is ``T``, the `addr` operator result is of the type ``ptr T``. An
address is always an untraced reference. Taking the address of an object that
resides on the stack is **unsafe**, as the pointer may live longer than the
object on the stack and can thus reference a non-existing object. One can get
the address of variables, but one can't use it on variables declared through
``let`` statements:

.. code-block:: nim

  let t1 = "Hello"
  var
    t2 = t1
    t3 : pointer = addr(t2)
  echo repr(addr(t2))
  # --> ref 0x7fff6b71b670 --> 0x10bb81050"Hello"
  echo cast[ptr string](t3)[]
  # --> Hello
  # The following line doesn't compile:
  echo repr(addr(t1))
  # Error: expression has no address