diff options
Diffstat (limited to 'doc/astspec.txt')
-rw-r--r-- | doc/astspec.txt | 727 |
1 files changed, 473 insertions, 254 deletions
diff --git a/doc/astspec.txt b/doc/astspec.txt index f430677af..7a7053a2d 100644 --- a/doc/astspec.txt +++ b/doc/astspec.txt @@ -6,8 +6,7 @@ The AST consists of nodes (``NimNode``) with a variable number of children. Each node has a field named ``kind`` which describes what the node contains: -.. code-block:: nim - + ```nim type NimNodeKind = enum ## kind of a node; only explanatory nnkNone, ## invalid node kind @@ -28,14 +27,11 @@ contains: intVal: BiggestInt ## the int literal of nnkFloatLit..nnkFloat64Lit: floatVal: BiggestFloat ## the float literal - of nnkStrLit..nnkTripleStrLit: + of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkIdent, nnkSym: strVal: string ## the string literal - of nnkIdent: - ident: NimIdent ## the identifier - of nnkSym: - symbol: NimSym ## the symbol (after symbol lookup phase) else: sons: seq[NimNode] ## the node's sons (or children) + ``` For the ``NimNode`` type, the ``[]`` operator has been overloaded: ``n[i]`` is ``n``'s ``i``-th child. @@ -54,9 +50,9 @@ A leaf of the AST often corresponds to a terminal symbol in the concrete syntax. Note that the default ``float`` in Nim maps to ``float64`` such that the default AST for a float is ``nnkFloat64Lit`` as below. ------------------ --------------------------------------------- +================= ============================================= Nim expression Corresponding AST ------------------ --------------------------------------------- +================= ============================================= ``42`` ``nnkIntLit(intVal = 42)`` ``42'i8`` ``nnkInt8Lit(intVal = 42)`` ``42'i16`` ``nnkInt16Lit(intVal = 42)`` @@ -74,9 +70,9 @@ Nim expression Corresponding AST ``"""abc"""`` ``nnkTripleStrLit(strVal = "abc")`` ``' '`` ``nnkCharLit(intVal = 32)`` ``nil`` ``nnkNilLit()`` -``myIdentifier`` ``nnkIdent(ident = !"myIdentifier")`` -``myIdentifier`` after lookup pass: ``nnkSym(symbol = ...)`` ------------------ --------------------------------------------- +``myIdentifier`` ``nnkIdent(strVal = "myIdentifier")`` +``myIdentifier`` after lookup pass: ``nnkSym(strVal = "myIdentifier", ...)`` +================= ============================================= Identifiers are ``nnkIdent`` nodes. After the name lookup pass these nodes get transferred into ``nnkSym`` nodes. @@ -90,17 +86,19 @@ Command call Concrete syntax: -.. code-block:: nim + ```nim echo "abc", "xyz" + ``` AST: -.. code-block:: nim + ```nim nnkCommand( - nnkIdent(!"echo"), + nnkIdent("echo"), nnkStrLit("abc"), nnkStrLit("xyz") ) + ``` Call with ``()`` @@ -108,17 +106,19 @@ Call with ``()`` Concrete syntax: -.. code-block:: nim + ```nim echo("abc", "xyz") + ``` AST: -.. code-block:: nim + ```nim nnkCall( - nnkIdent(!"echo"), + nnkIdent("echo"), nnkStrLit("abc"), nnkStrLit("xyz") ) + ``` Infix operator call @@ -126,75 +126,83 @@ Infix operator call Concrete syntax: -.. code-block:: nim + ```nim "abc" & "xyz" + ``` AST: -.. code-block:: nim + ```nim nnkInfix( - nnkIdent(!"&"), + nnkIdent("&"), nnkStrLit("abc"), nnkStrLit("xyz") ) + ``` Note that with multiple infix operators, the command is parsed by operator precedence. Concrete syntax: -.. code-block:: nim + ```nim 5 + 3 * 4 + ``` AST: -.. code-block:: nim + ```nim nnkInfix( - nnkIdent(!"+"), + nnkIdent("+"), nnkIntLit(5), nnkInfix( - nnkIdent(!"*"), + nnkIdent("*"), nnkIntLit(3), nnkIntLit(4) ) ) + ``` As a side note, if you choose to use infix operators in a prefix form, the AST behaves as a -[parenthetical function call](./macros.html#calls-expressions-call-with) with +[parenthetical function call](#callsslashexpressions-call-with) with ``nnkAccQuoted``, as follows: Concrete syntax: -.. code-block:: nim + ```nim `+`(3, 4) + ``` AST: -.. code-block:: nim + ```nim nnkCall( nnkAccQuoted( - nnkIdent(!"+") + nnkIdent("+") ), nnkIntLit(3), nnkIntLit(4) ) + ``` Prefix operator call -------------------- Concrete syntax: -.. code-block:: nim + ```nim ? "xyz" + ``` AST: -.. code-block:: nim + ```nim nnkPrefix( - nnkIdent(!"?"), + nnkIdent("?"), nnkStrLit("abc") ) + ``` Postfix operator call @@ -205,16 +213,18 @@ Postfix operator call Concrete syntax: -.. code-block:: nim + ```nim identifier* + ``` AST: -.. code-block:: nim + ```nim nnkPostfix( - nnkIdent(!"*"), - nnkIdent(!"identifier") + nnkIdent("*"), + nnkIdent("identifier") ) + ``` Call with named arguments @@ -222,53 +232,59 @@ Call with named arguments Concrete syntax: -.. code-block:: nim + ```nim writeLine(file=stdout, "hallo") + ``` AST: -.. code-block:: nim + ```nim nnkCall( - nnkIdent(!"writeLine"), + nnkIdent("writeLine"), nnkExprEqExpr( - nnkIdent(!"file"), - nnkIdent(!"stdout") + nnkIdent("file"), + nnkIdent("stdout") ), nnkStrLit("hallo") ) + ``` Call with raw string literal ---------------------------- This is used, for example, in the ``bindSym`` examples -[here](http://nim-lang.org/docs/manual.html#macros-bindsym) and with +[here](manual.html#macros-bindsym) and with ``re"some regexp"`` in the regular expression module. Concrete syntax: -.. code-block:: nim + ```nim echo"abc" + ``` AST: -.. code-block:: nim + ```nim nnkCallStrLit( - nnkIdent(!"echo"), + nnkIdent("echo"), nnkRStrLit("hello") ) + ``` Dereference operator ``[]`` --------------------------- Concrete syntax: -.. code-block:: nim + ```nim x[] + ``` AST: -.. code-block:: nim - nnkDerefExpr(nnkIdent(!"x")) + ```nim + nnkDerefExpr(nnkIdent("x")) + ``` Addr operator @@ -276,13 +292,15 @@ Addr operator Concrete syntax: -.. code-block:: nim + ```nim addr(x) + ``` AST: -.. code-block:: nim - nnkAddr(nnkIdent(!"x")) + ```nim + nnkAddr(nnkIdent("x")) + ``` Cast operator @@ -290,13 +308,15 @@ Cast operator Concrete syntax: -.. code-block:: nim + ```nim cast[T](x) + ``` AST: -.. code-block:: nim - nnkCast(nnkIdent(!"T"), nnkIdent(!"x")) + ```nim + nnkCast(nnkIdent("T"), nnkIdent("x")) + ``` Object access operator ``.`` @@ -304,13 +324,15 @@ Object access operator ``.`` Concrete syntax: -.. code-block:: nim + ```nim x.y + ``` AST: -.. code-block:: nim - nnkDotExpr(nnkIdent(!"x"), nnkIdent(!"y")) + ```nim + nnkDotExpr(nnkIdent("x"), nnkIdent("y")) + ``` If you use Nim's flexible calling syntax (as in ``x.len()``), the result is the same as above but wrapped in an ``nnkCall``. @@ -321,31 +343,77 @@ Array access operator ``[]`` Concrete syntax: -.. code-block:: nim + ```nim x[y] + ``` AST: -.. code-block:: nim - nnkBracketExpr(nnkIdent(!"x"), nnkIdent(!"y")) + ```nim + nnkBracketExpr(nnkIdent("x"), nnkIdent("y")) + ``` Parentheses ----------- -Parentheses for affecting operator precedence or tuple construction -are built with the ``nnkPar`` node. +Parentheses for affecting operator precedence use the ``nnkPar`` node. Concrete syntax: -.. code-block:: nim - (1, 2, (3)) + ```nim + (a + b) * c + ``` AST: -.. code-block:: nim - nnkPar(nnkIntLit(1), nnkIntLit(2), nnkPar(nnkIntLit(3))) + ```nim + nnkInfix(nnkIdent("*"), + nnkPar( + nnkInfix(nnkIdent("+"), nnkIdent("a"), nnkIdent("b"))), + nnkIdent("c")) + ``` + +Tuple Constructors +------------------ + +Nodes for tuple construction are built with the ``nnkTupleConstr`` node. + +Concrete syntax: + + ```nim + (1, 2, 3) + (a: 1, b: 2, c: 3) + () + ``` + +AST: + + ```nim + nnkTupleConstr(nnkIntLit(1), nnkIntLit(2), nnkIntLit(3)) + nnkTupleConstr( + nnkExprColonExpr(nnkIdent("a"), nnkIntLit(1)), + nnkExprColonExpr(nnkIdent("b"), nnkIntLit(2)), + nnkExprColonExpr(nnkIdent("c"), nnkIntLit(3))) + nnkTupleConstr() + ``` + +Since the one tuple would be syntactically identical to parentheses +with an expression in them, the parser expects a trailing comma for +them. For tuple constructors with field names, this is not necessary. + ```nim + (1,) + (a: 1) + ``` + +AST: + + ```nim + nnkTupleConstr(nnkIntLit(1)) + nnkTupleConstr( + nnkExprColonExpr(nnkIdent("a"), nnkIntLit(1))) + ``` Curly braces ------------ @@ -354,28 +422,32 @@ Curly braces are used as the set constructor. Concrete syntax: -.. code-block:: nim + ```nim {1, 2, 3} + ``` AST: -.. code-block:: nim + ```nim nnkCurly(nnkIntLit(1), nnkIntLit(2), nnkIntLit(3)) + ``` When used as a table constructor, the syntax is different. Concrete syntax: -.. code-block:: nim + ```nim {a: 3, b: 5} + ``` AST: -.. code-block:: nim + ```nim nnkTableConstr( - nnkExprColonExpr(nnkIdent(!"a"), nnkIntLit(3)), - nnkExprColonExpr(nnkIdent(!"b"), nnkIntLit(5)) + nnkExprColonExpr(nnkIdent("a"), nnkIntLit(3)), + nnkExprColonExpr(nnkIdent("b"), nnkIntLit(5)) ) + ``` Brackets @@ -385,13 +457,15 @@ Brackets are used as the array constructor. Concrete syntax: -.. code-block:: nim + ```nim [1, 2, 3] + ``` AST: -.. code-block:: nim + ```nim nnkBracket(nnkIntLit(1), nnkIntLit(2), nnkIntLit(3)) + ``` Ranges @@ -403,22 +477,24 @@ AST, construction with ``..`` as an infix operator should be used instead. Concrete syntax: -.. code-block:: nim + ```nim 1..3 + ``` AST: -.. code-block:: nim + ```nim nnkInfix( - nnkIdent(!".."), + nnkIdent(".."), nnkIntLit(1), nnkIntLit(3) ) + ``` Example code: -.. code-block:: nim - macro genRepeatEcho(): stmt = + ```nim + macro genRepeatEcho() = result = newNimNode(nnkStmtList) var forStmt = newNimNode(nnkForStmt) # generate a for statement @@ -436,6 +512,7 @@ Example code: # 3 # 3 # 3 + ``` If expression @@ -445,40 +522,44 @@ The representation of the ``if`` expression is subtle, but easy to traverse. Concrete syntax: -.. code-block:: nim + ```nim if cond1: expr1 elif cond2: expr2 else: expr3 + ``` AST: -.. code-block:: nim + ```nim nnkIfExpr( nnkElifExpr(cond1, expr1), nnkElifExpr(cond2, expr2), nnkElseExpr(expr3) ) + ``` Documentation Comments ---------------------- Double-hash (``##``) comments in the code actually have their own format, -but the comments do not yet show up in the AST, which will only show that -a comment exists, not what it contains. Single-hash (``#``) comments are ignored. +using ``strVal`` to get and set the comment text. Single-hash (``#``) +comments are ignored. Concrete syntax: -.. code-block:: nim + ```nim ## This is a comment ## This is part of the first comment stmt1 ## Yet another + ``` AST: -.. code-block:: nim + ```nim nnkCommentStmt() # only appears once for the first two lines! stmt1 nnkCommentStmt() # another nnkCommentStmt because there is another comment # (separate from the first) + ``` Pragmas ------- @@ -489,37 +570,41 @@ objects, but the standalone ``emit`` pragma shows the basics with the AST. Concrete syntax: -.. code-block:: nim + ```nim {.emit: "#include <stdio.h>".} + ``` AST: -.. code-block:: nim + ```nim nnkPragma( nnkExprColonExpr( - nnkIdent(!"emit"), + nnkIdent("emit"), nnkStrLit("#include <stdio.h>") # the "argument" ) ) + ``` As many ``nnkIdent`` appear as there are pragmas between ``{..}``. Note that the declaration of new pragmas is essentially the same: Concrete syntax: -.. code-block:: nim + ```nim {.pragma: cdeclRename, cdecl.} + ``` AST: -.. code-block:: nim + ```nim nnkPragma( nnkExprColonExpr( - nnkIdent(!"pragma"), # this is always first when declaring a new pragma - nnkIdent(!"cdeclRename") # the name of the pragma + nnkIdent("pragma"), # this is always first when declaring a new pragma + nnkIdent("cdeclRename") # the name of the pragma ), - nnkIdent(!"cdecl") + nnkIdent("cdecl") ) + ``` Statements ========== @@ -532,7 +617,7 @@ there is no ``else`` branch, no ``nnkElse`` child exists. Concrete syntax: -.. code-block:: nim + ```nim if cond1: stmt1 elif cond2: @@ -541,16 +626,18 @@ Concrete syntax: stmt3 else: stmt4 + ``` AST: -.. code-block:: nim + ```nim nnkIfStmt( nnkElifBranch(cond1, stmt1), nnkElifBranch(cond2, stmt2), nnkElifBranch(cond3, stmt3), nnkElse(stmt4) ) + ``` When statement @@ -564,13 +651,15 @@ Assignment Concrete syntax: -.. code-block:: nim + ```nim x = 42 + ``` AST: -.. code-block:: nim - nnkAsgn(nnkIdent(!"x"), nnkIntLit(42)) + ```nim + nnkAsgn(nnkIdent("x"), nnkIntLit(42)) + ``` This is not the syntax for assignment when combined with ``var``, ``let``, or ``const``. @@ -580,15 +669,17 @@ Statement list Concrete syntax: -.. code-block:: nim + ```nim stmt1 stmt2 stmt3 + ``` AST: -.. code-block:: nim + ```nim nnkStmtList(stmt1, stmt2, stmt3) + ``` Case statement @@ -596,7 +687,7 @@ Case statement Concrete syntax: -.. code-block:: nim + ```nim case expr1 of expr2, expr3..expr4: stmt1 @@ -606,10 +697,11 @@ Concrete syntax: stmt3 else: stmt4 + ``` AST: -.. code-block:: nim + ```nim nnkCaseStmt( expr1, nnkOfBranch(expr2, nnkRange(expr3, expr4), stmt1), @@ -617,6 +709,7 @@ AST: nnkElifBranch(cond1, stmt3), nnkElse(stmt4) ) + ``` The ``nnkElifBranch`` and ``nnkElse`` parts may be missing. @@ -626,14 +719,16 @@ While statement Concrete syntax: -.. code-block:: nim + ```nim while expr1: stmt1 + ``` AST: -.. code-block:: nim + ```nim nnkWhileStmt(expr1, stmt1) + ``` For statement @@ -641,14 +736,16 @@ For statement Concrete syntax: -.. code-block:: nim + ```nim for ident1, ident2 in expr1: stmt1 + ``` AST: -.. code-block:: nim + ```nim nnkForStmt(ident1, ident2, expr1, stmt1) + ``` Try statement @@ -656,7 +753,7 @@ Try statement Concrete syntax: -.. code-block:: nim + ```nim try: stmt1 except e1, e2: @@ -667,10 +764,11 @@ Concrete syntax: stmt4 finally: stmt5 + ``` AST: -.. code-block:: nim + ```nim nnkTryStmt( stmt1, nnkExceptBranch(e1, e2, stmt2), @@ -678,6 +776,7 @@ AST: nnkExceptBranch(stmt4), nnkFinally(stmt5) ) + ``` Return statement @@ -685,13 +784,15 @@ Return statement Concrete syntax: -.. code-block:: nim + ```nim return expr1 + ``` AST: -.. code-block:: nim + ```nim nnkReturnStmt(expr1) + ``` Yield statement @@ -699,8 +800,9 @@ Yield statement Like ``return``, but with ``nnkYieldStmt`` kind. -.. code-block:: nim + ```nim nnkYieldStmt(expr1) + ``` Discard statement @@ -708,8 +810,9 @@ Discard statement Like ``return``, but with ``nnkDiscardStmt`` kind. -.. code-block:: nim + ```nim nnkDiscardStmt(expr1) + ``` Continue statement @@ -717,26 +820,30 @@ Continue statement Concrete syntax: -.. code-block:: nim + ```nim continue + ``` AST: -.. code-block:: nim + ```nim nnkContinueStmt() + ``` Break statement --------------- Concrete syntax: -.. code-block:: nim + ```nim break otherLocation + ``` AST: -.. code-block:: nim - nnkBreakStmt(nnkIdent(!"otherLocation")) + ```nim + nnkBreakStmt(nnkIdent("otherLocation")) + ``` If ``break`` is used without a jump-to location, ``nnkEmpty`` replaces ``nnkIdent``. @@ -745,13 +852,15 @@ Block statement Concrete syntax: -.. code-block:: nim + ```nim block name: + ``` AST: -.. code-block:: nim - nnkBlockStmt(nnkIdent(!"name"), nnkStmtList(...)) + ```nim + nnkBlockStmt(nnkIdent("name"), nnkStmtList(...)) + ``` A ``block`` doesn't need an name, in which case ``nnkEmpty`` is used. @@ -760,18 +869,20 @@ Asm statement Concrete syntax: -.. code-block:: nim + ```nim asm """ some asm """ + ``` AST: -.. code-block:: nim + ```nim nnkAsmStmt( nnkEmpty(), # for pragmas nnkTripleStrLit("some asm"), ) + ``` Import section -------------- @@ -781,44 +892,50 @@ on what keywords are present. Let's start with the simplest form. Concrete syntax: -.. code-block:: nim - import math + ```nim + import std/math + ``` AST: -.. code-block:: nim - nnkImportStmt(nnkIdent(!"math")) + ```nim + nnkImportStmt(nnkIdent("math")) + ``` With ``except``, we get ``nnkImportExceptStmt``. Concrete syntax: -.. code-block:: nim - import math except pow + ```nim + import std/math except pow + ``` AST: -.. code-block:: nim - nnkImportExceptStmt(nnkIdent(!"math"),nnkIdent(!"pow")) + ```nim + nnkImportExceptStmt(nnkIdent("math"),nnkIdent("pow")) + ``` -Note that ``import math as m`` does not use a different node; rather, +Note that ``import std/math as m`` does not use a different node; rather, we use ``nnkImportStmt`` with ``as`` as an infix operator. Concrete syntax: -.. code-block:: nim - import strutils as su + ```nim + import std/strutils as su + ``` AST: -.. code-block:: nim + ```nim nnkImportStmt( nnkInfix( - nnkIdent(!"as"), - nnkIdent(!"strutils"), - nnkIdent(!"su") + nnkIdent("as"), + nnkIdent("strutils"), + nnkIdent("su") ) ) + ``` From statement -------------- @@ -827,15 +944,17 @@ If we use ``from ... import``, the result is different, too. Concrete syntax: -.. code-block:: nim - from math import pow + ```nim + from std/math import pow + ``` AST: -.. code-block:: nim - nnkFromStmt(nnkIdent(!"math"), nnkIdent(!"pow")) + ```nim + nnkFromStmt(nnkIdent("math"), nnkIdent("pow")) + ``` -Using ``from math as m import pow`` works identically to the ``as`` modifier +Using ``from std/math as m import pow`` works identically to the ``as`` modifier with the ``import`` statement, but wrapped in ``nnkFromStmt``. Export statement @@ -846,26 +965,30 @@ the ``export`` syntax is pretty straightforward. Concrete syntax: -.. code-block:: nim + ```nim export unsigned + ``` AST: -.. code-block:: nim - nnkExportStmt(nnkIdent(!"unsigned")) + ```nim + nnkExportStmt(nnkIdent("unsigned")) + ``` Similar to the ``import`` statement, the AST is different for ``export ... except``. Concrete syntax: -.. code-block:: nim + ```nim export math except pow # we're going to implement our own exponentiation + ``` AST: -.. code-block:: nim - nnkExportExceptStmt(nnkIdent(!"math"),nnkIdent(!"pow")) + ```nim + nnkExportExceptStmt(nnkIdent("math"),nnkIdent("pow")) + ``` Include statement ----------------- @@ -874,39 +997,43 @@ Like a plain ``import`` statement but with ``nnkIncludeStmt``. Concrete syntax: -.. code-block:: nim + ```nim include blocks + ``` AST: -.. code-block:: nim - nnkIncludeStmt(nnkIdent(!"blocks")) + ```nim + nnkIncludeStmt(nnkIdent("blocks")) + ``` Var section ----------- Concrete syntax: -.. code-block:: nim + ```nim var a = 3 + ``` AST: -.. code-block:: nim + ```nim nnkVarSection( nnkIdentDefs( - nnkIdent(!"a"), + nnkIdent("a"), nnkEmpty(), # or nnkIdent(...) if the variable declares the type nnkIntLit(3), ) ) + ``` Note that either the second or third (or both) parameters above must exist, as the compiler needs to know the type somehow (which it can infer from the given assignment). This is not the same AST for all uses of ``var``. See -[Procedure declaration](http://nim-lang.org/docs/macros.html#statements-procedure-declaration) +[Procedure declaration](macros.html#statements-procedure-declaration) for details. Let section @@ -917,38 +1044,42 @@ This is equivalent to ``var``, but with ``nnkLetSection`` rather than Concrete syntax: -.. code-block:: nim - let v = 3 + ```nim + let a = 3 + ``` AST: -.. code-block:: nim + ```nim nnkLetSection( nnkIdentDefs( - nnkIdent(!"a"), + nnkIdent("a"), nnkEmpty(), # or nnkIdent(...) for the type nnkIntLit(3), ) ) + ``` Const section ------------- Concrete syntax: -.. code-block:: nim + ```nim const a = 3 + ``` AST: -.. code-block:: nim + ```nim nnkConstSection( nnkConstDef( # not nnkConstDefs! - nnkIdent(!"a"), + nnkIdent("a"), nnkEmpty(), # or nnkIdent(...) if the variable declares the type nnkIntLit(3), # required in a const declaration! ) ) + ``` Type section ------------ @@ -958,56 +1089,61 @@ and ``const``. Concrete syntax: -.. code-block:: nim + ```nim type A = int + ``` AST: -.. code-block:: nim + ```nim nnkTypeSection( nnkTypeDef( - nnkIdent(!"A"), + nnkIdent("A"), nnkEmpty(), - nnkIdent(!"int") + nnkIdent("int") ) ) + ``` Declaring ``distinct`` types is similar, with the last ``nnkIdent`` wrapped in ``nnkDistinctTy``. Concrete syntax: -.. code-block:: nim + ```nim type MyInt = distinct int + ``` AST: -.. code-block:: nim + ```nim # ... nnkTypeDef( - nnkIdent(!"MyInt"), + nnkIdent("MyInt"), nnkEmpty(), nnkDistinctTy( - nnkIdent(!"int") + nnkIdent("int") ) ) + ``` If a type section uses generic parameters, they are treated here: Concrete syntax: -.. code-block:: nim + ```nim type A[T] = expr1 + ``` AST: -.. code-block:: nim + ```nim nnkTypeSection( nnkTypeDef( - nnkIdent(!"A"), + nnkIdent("A"), nnkGenericParams( nnkIdentDefs( - nnkIdent(!"T"), + nnkIdent("T"), nnkEmpty(), # if the type is declared with options, like # ``[T: SomeInteger]``, they are given here nnkEmpty(), @@ -1016,89 +1152,101 @@ AST: expr1, ) ) + ``` Note that not all ``nnkTypeDef`` utilize ``nnkIdent`` as their -their parameter. One of the most common uses of type declarations +parameter. One of the most common uses of type declarations is to work with objects. Concrete syntax: -.. code-block:: nim + ```nim type IO = object of RootObj + ``` AST: -.. code-block:: nim + ```nim # ... nnkTypeDef( - nnkIdent(!"IO"), + nnkIdent("IO"), nnkEmpty(), nnkObjectTy( nnkEmpty(), # no pragmas here nnkOfInherit( - nnkIdent(!"RootObj") # inherits from RootObj - ) + nnkIdent("RootObj") # inherits from RootObj + ), nnkEmpty() ) ) + ``` Nim's object syntax is rich. Let's take a look at an involved example in its entirety to see some of the complexities. Concrete syntax: -.. code-block:: nim - type Obj[T] = object {.inheritable.} + ```nim + type Obj[T] {.inheritable.} = object name: string case isFat: bool of true: m: array[100_000, T] of false: m: array[10, T] + ``` AST: -.. code-block:: nim + ```nim # ... + nnkPragmaExpr( + nnkIdent("Obj"), + nnkPragma(nnkIdent("inheritable")) + ), + nnkGenericParams( + nnkIdentDefs( + nnkIdent("T"), + nnkEmpty(), + nnkEmpty()) + ), nnkObjectTy( - nnkPragma( - nnkIdent(!"inheritable") - ), + nnkEmpty(), nnkEmpty(), nnkRecList( # list of object parameters nnkIdentDefs( - nnkIdent(!"name"), - nnkIdent(!"string"), + nnkIdent("name"), + nnkIdent("string"), nnkEmpty() ), nnkRecCase( # case statement within object (not nnkCaseStmt) nnkIdentDefs( - nnkIdent(!"isFat"), - nnkIdent(!"bool"), + nnkIdent("isFat"), + nnkIdent("bool"), nnkEmpty() ), nnkOfBranch( - nnkIdent(!"true"), + nnkIdent("true"), nnkRecList( # again, a list of object parameters nnkIdentDefs( - nnkIdent(!"m"), + nnkIdent("m"), nnkBracketExpr( - nnkIdent(!"array"), + nnkIdent("array"), nnkIntLit(100000), - nnkIdent(!"T") + nnkIdent("T") ), nnkEmpty() ) ), nnkOfBranch( - nnkIdent(!"false"), + nnkIdent("false"), nnkRecList( nnkIdentDefs( - nnkIdent(!"m"), + nnkIdent("m"), nnkBracketExpr( - nnkIdent(!"array"), + nnkIdent("array"), nnkIntLit(10), - nnkIdent(!"T") + nnkIdent("T") ), nnkEmpty() ) @@ -1107,71 +1255,78 @@ AST: ) ) ) + ``` Using an ``enum`` is similar to using an ``object``. Concrete syntax: -.. code-block:: nim + ```nim type X = enum First + ``` AST: -.. code-block:: nim + ```nim # ... nnkEnumTy( nnkEmpty(), - nnkIdent(!"First") # you need at least one nnkIdent or the compiler complains + nnkIdent("First") # you need at least one nnkIdent or the compiler complains ) + ``` The usage of ``concept`` (experimental) is similar to objects. Concrete syntax: -.. code-block:: nim + ```nim type Con = concept x,y,z (x & y & z) is string + ``` AST: -.. code-block:: nim + ```nim # ... nnkTypeClassTy( # note this isn't nnkConceptTy! - nnkArglist( + nnkArgList( # ... idents for x, y, z ) # ... ) + ``` Static types, like ``static[int]``, use ``nnkIdent`` wrapped in ``nnkStaticTy``. Concrete syntax: -.. code-block:: nim + ```nim type A[T: static[int]] = object + ``` AST: -.. code-block:: nim + ```nim # ... within nnkGenericParams nnkIdentDefs( - nnkIdent(!"T"), + nnkIdent("T"), nnkStaticTy( - nnkIdent(!"int") + nnkIdent("int") ), nnkEmpty() ) # ... + ``` In general, declaring types mirrors this syntax (i.e., ``nnkStaticTy`` for ``static``, etc.). Examples follow (exceptions marked by ``*``): -------------- --------------------------------------------- +============= ============================================= Nim type Corresponding AST -------------- --------------------------------------------- +============= ============================================= ``static`` ``nnkStaticTy`` ``tuple`` ``nnkTupleTy`` ``var`` ``nnkVarTy`` @@ -1180,11 +1335,11 @@ Nim type Corresponding AST ``distinct`` ``nnkDistinctTy`` ``enum`` ``nnkEnumTy`` ``concept`` ``nnkTypeClassTy``\* -``array`` ``nnkBracketExpr(nnkIdent(!"array"),...``\* +``array`` ``nnkBracketExpr(nnkIdent("array"),...``\* ``proc`` ``nnkProcTy`` ``iterator`` ``nnkIteratorTy`` ``object`` ``nnkObjectTy`` -------------- --------------------------------------------- +============= ============================================= Take special care when declaring types as ``proc``. The behavior is similar to ``Procedure declaration``, below, but does not treat ``nnkGenericParams``. @@ -1192,53 +1347,91 @@ Generic parameters are treated in the type, not the ``proc`` itself. Concrete syntax: -.. code-block:: nim - type MyProc[T] = proc(x: T) + ```nim + type MyProc[T] = proc(x: T) {.nimcall.} + ``` AST: -.. code-block:: nim + ```nim # ... nnkTypeDef( - nnkIdent(!"MyProc"), + nnkIdent("MyProc"), nnkGenericParams( # here, not with the proc # ... ) nnkProcTy( # behaves like a procedure declaration from here on nnkFormalParams( # ... - ) + ), + nnkPragma(nnkIdent("nimcall")) ) ) + ``` The same syntax applies to ``iterator`` (with ``nnkIteratorTy``), but *does not* apply to ``converter`` or ``template``. +Type class versions of these nodes generally share the same node kind but +without any child nodes. The ``tuple`` type class is represented by +``nnkTupleClassTy``, while a ``proc`` or ``iterator`` type class with pragmas +has an ``nnkEmpty`` node in place of the ``nnkFormalParams`` node of a +concrete ``proc`` or ``iterator`` type node. + + ```nim + type TypeClass = proc {.nimcall.} | ref | tuple + ``` + +AST: + + ```nim + nnkTypeDef( + nnkIdent("TypeClass"), + nnkEmpty(), + nnkInfix( + nnkIdent("|"), + nnkProcTy( + nnkEmpty(), + nnkPragma(nnkIdent("nimcall")) + ), + nnkInfix( + nnkIdent("|"), + nnkRefTy(), + nnkTupleClassTy() + ) + ) + ) + ``` + Mixin statement --------------- Concrete syntax: -.. code-block:: nim + ```nim mixin x + ``` AST: -.. code-block:: nim - nnkMixinStmt(nnkIdent(!"x")) + ```nim + nnkMixinStmt(nnkIdent("x")) + ``` Bind statement -------------- Concrete syntax: -.. code-block:: nim + ```nim bind x + ``` AST: -.. code-block:: nim - nnkBindStmt(nnkIdent(!"x")) + ```nim + nnkBindStmt(nnkIdent("x")) + ``` Procedure declaration --------------------- @@ -1248,37 +1441,41 @@ a feel for how procedure calls are broken down. Concrete syntax: -.. code-block:: nim + ```nim proc hello*[T: SomeInteger](x: int = 3, y: float32): int {.inline.} = discard + ``` AST: -.. code-block:: nim + ```nim nnkProcDef( - nnkPostfix(nnkIdent(!"*"), nnkIdent(!"hello")), # the exported proc name + nnkPostfix(nnkIdent("*"), nnkIdent("hello")), # the exported proc name nnkEmpty(), # patterns for term rewriting in templates and macros (not procs) nnkGenericParams( # generic type parameters, like with type declaration nnkIdentDefs( - nnkIdent(!"T"), nnkIdent(!"SomeInteger") + nnkIdent("T"), + nnkIdent("SomeInteger"), + nnkEmpty() ) ), nnkFormalParams( - nnkIdent(!"int"), # the first FormalParam is the return type. nnkEmpty() if there is none + nnkIdent("int"), # the first FormalParam is the return type. nnkEmpty() if there is none nnkIdentDefs( - nnkIdent(!"x"), - nnkIdent(!"int"), # type type (required for procs, not for templates) + nnkIdent("x"), + nnkIdent("int"), # type type (required for procs, not for templates) nnkIntLit(3) # a default value ), nnkIdentDefs( - nnkIdent(!"y"), - nnkIdent(!"float32"), + nnkIdent("y"), + nnkIdent("float32"), nnkEmpty() ) - nnkPragma(nnkIdent(!"inline")), - nnkEmpty(), # reserved slot for future use - nnkStmtList(nnkDiscardStmt(nnkEmpty())) # the meat of the proc - ) + ), + nnkPragma(nnkIdent("inline")), + nnkEmpty(), # reserved slot for future use + nnkStmtList(nnkDiscardStmt(nnkEmpty())) # the meat of the proc ) + ``` There is another consideration. Nim has flexible type identification for its procs. Even though ``proc(a: int, b: int)`` and ``proc(a, b: int)`` @@ -1286,41 +1483,45 @@ are equivalent in the code, the AST is a little different for the latter. Concrete syntax: -.. code-block:: nim + ```nim proc(a, b: int) + ``` AST: -.. code-block:: nim + ```nim # ...AST as above... nnkFormalParams( nnkEmpty(), # no return here nnkIdentDefs( - nnkIdent(!"a"), # the first parameter - nnkIdent(!"b"), # directly to the second parameter - nnkIdent(!"int"), # their shared type identifier + nnkIdent("a"), # the first parameter + nnkIdent("b"), # directly to the second parameter + nnkIdent("int"), # their shared type identifier nnkEmpty(), # default value would go here ) ), # ... + ``` When a procedure uses the special ``var`` type return variable, the result is different from that of a var section. Concrete syntax: -.. code-block:: nim + ```nim proc hello(): var int + ``` AST: -.. code-block:: nim + ```nim # ... nnkFormalParams( nnkVarTy( - nnkIdent(!"int") + nnkIdent("int") ) ) + ``` Iterator declaration -------------------- @@ -1330,17 +1531,19 @@ replacing ``nnkProcDef``. Concrete syntax: -.. code-block:: nim + ```nim iterator nonsense[T](x: seq[T]): float {.closure.} = ... + ``` AST: -.. code-block:: nim + ```nim nnkIteratorDef( - nnkIdent(!"nonsense"), + nnkIdent("nonsense"), nnkEmpty(), ... ) + ``` Converter declaration --------------------- @@ -1349,41 +1552,45 @@ A converter is similar to a proc. Concrete syntax: -.. code-block:: nim + ```nim converter toBool(x: float): bool + ``` AST: -.. code-block:: nim + ```nim nnkConverterDef( - nnkIdent(!"toBool"), + nnkIdent("toBool"), # ... ) + ``` Template declaration -------------------- Templates (as well as macros, as we'll see) have a slightly expanded AST when compared to procs and iterators. The reason for this is [term-rewriting -macros](http://nim-lang.org/docs/manual.html#term-rewriting-macros). Notice +macros](manual.html#term-rewriting-macros). Notice the ``nnkEmpty()`` as the second argument to ``nnkProcDef`` and ``nnkIteratorDef`` above? That's where the term-rewriting macros go. Concrete syntax: -.. code-block:: nim + ```nim template optOpt{expr1}(a: int): int + ``` AST: -.. code-block:: nim + ```nim nnkTemplateDef( - nnkIdent(!"optOpt"), + nnkIdent("optOpt"), nnkStmtList( # instead of nnkEmpty() expr1 ), # follows like a proc or iterator ) + ``` If the template does not have types for its parameters, the type identifiers inside ``nnkFormalParams`` just becomes ``nnkEmpty``. @@ -1394,6 +1601,18 @@ Macro declaration Macros behave like templates, but ``nnkTemplateDef`` is replaced with ``nnkMacroDef``. +Hidden Standard Conversion +-------------------------- + + ```nim + var f: float = 1 + ``` + +The type of "f" is ``float`` but the type of "1" is actually ``int``. Inserting +``int`` into a ``float`` is a type error. Nim inserts the ``nnkHiddenStdConv`` +node around the ``nnkIntLit`` node so that the new node has the correct type of +``float``. This works for any auto converted nodes and makes the conversion +explicit. Special node kinds ================== |