123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690 |
- 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 mandatory ``void`` context for the subsequent expressions:
- .. 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 for the object
- and all of its fields. 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`` or ``var``, 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:"
- var pw = readLine(stdin)
- while pw != "12345":
- echo "Wrong password! Next try:"
- 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
- ---------------
- The using statement provides syntactic convenience in modules where
- the same parameter names and types are used over and over. Instead of:
- .. code-block:: nim
- proc foo(c: Context; n: Node) = ...
- proc bar(c: Context; n: Node, counter: int) = ...
- proc baz(c: Context; n: Node) = ...
- One can tell the compiler about the convention that a parameter of
- name ``c`` should default to type ``Context``, ``n`` should default to
- ``Node`` etc.:
- .. code-block:: nim
- using
- c: Context
- n: Node
- counter: int
- proc foo(c, n) = ...
- proc bar(c, n, counter) = ...
- proc baz(c, n) = ...
- The ``using`` section uses the same indentation based grouping syntax as
- a ``var`` or ``let`` section.
- Note that ``using`` is not applied for ``template`` since untyped template
- parameters default to the type ``system.untyped``.
- 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
|