123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407 |
- The AST in Nim
- ==============
- This section describes how the AST is modelled with Nim's type system.
- 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
- type
- NimNodeKind = enum ## kind of a node; only explanatory
- nnkNone, ## invalid node kind
- nnkEmpty, ## empty node
- nnkIdent, ## node contains an identifier
- nnkIntLit, ## node contains an int literal (example: 10)
- nnkStrLit, ## node contains a string literal (example: "abc")
- nnkNilLit, ## node contains a nil literal (example: nil)
- nnkCaseStmt, ## node represents a case statement
- ... ## many more
- NimNode = ref NimNodeObj
- NimNodeObj = object
- case kind: NimNodeKind ## the node's kind
- of nnkNone, nnkEmpty, nnkNilLit:
- discard ## node contains no additional fields
- of nnkCharLit..nnkUInt64Lit:
- intVal: BiggestInt ## the int literal
- of nnkFloatLit..nnkFloat64Lit:
- floatVal: BiggestFloat ## the float literal
- of nnkStrLit..nnkTripleStrLit:
- 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.
- To specify the AST for the different Nim constructs, the notation
- ``nodekind(son1, son2, ...)`` or ``nodekind(value)`` or
- ``nodekind(field=value)`` is used.
- Some child may be missing. A missing child is a node of kind ``nnkEmpty``;
- a child can never be nil.
- Leaf nodes/Atoms
- ================
- 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)``
- ``42'i32`` ``nnkInt32Lit(intVal = 42)``
- ``42'i64`` ``nnkInt64Lit(intVal = 42)``
- ``42'u8`` ``nnkUInt8Lit(intVal = 42)``
- ``42'u16`` ``nnkUInt16Lit(intVal = 42)``
- ``42'u32`` ``nnkUInt32Lit(intVal = 42)``
- ``42'u64`` ``nnkUInt64Lit(intVal = 42)``
- ``42.0`` ``nnkFloat64Lit(floatVal = 42.0)``
- ``42.0'f32`` ``nnkFloat32Lit(floatVal = 42.0)``
- ``42.0'f64`` ``nnkFloat64Lit(floatVal = 42.0)``
- ``"abc"`` ``nnkStrLit(strVal = "abc")``
- ``r"abc"`` ``nnkRStrLit(strVal = "abc")``
- ``"""abc"""`` ``nnkTripleStrLit(strVal = "abc")``
- ``' '`` ``nnkCharLit(intVal = 32)``
- ``nil`` ``nnkNilLit()``
- ``myIdentifier`` ``nnkIdent(ident = !"myIdentifier")``
- ``myIdentifier`` after lookup pass: ``nnkSym(symbol = ...)``
- ----------------- ---------------------------------------------
- Identifiers are ``nnkIdent`` nodes. After the name lookup pass these nodes
- get transferred into ``nnkSym`` nodes.
- Calls/expressions
- =================
- Command call
- ------------
- Concrete syntax:
- .. code-block:: nim
- echo "abc", "xyz"
- AST:
- .. code-block:: nim
- nnkCommand(
- nnkIdent(!"echo"),
- nnkStrLit("abc"),
- nnkStrLit("xyz")
- )
- Call with ``()``
- ----------------
- Concrete syntax:
- .. code-block:: nim
- echo("abc", "xyz")
- AST:
- .. code-block:: nim
- nnkCall(
- nnkIdent(!"echo"),
- nnkStrLit("abc"),
- nnkStrLit("xyz")
- )
- Infix operator call
- -------------------
- Concrete syntax:
- .. code-block:: nim
- "abc" & "xyz"
- AST:
- .. code-block:: nim
- nnkInfix(
- nnkIdent(!"&"),
- nnkStrLit("abc"),
- nnkStrLit("xyz")
- )
- Note that with multiple infix operators, the command is parsed by operator
- precedence.
- Concrete syntax:
- .. code-block:: nim
- 5 + 3 * 4
- AST:
- .. code-block:: nim
- nnkInfix(
- nnkIdent(!"+"),
- nnkIntLit(5),
- nnkInfix(
- 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
- ``nnkAccQuoted``, as follows:
- Concrete syntax:
- .. code-block:: nim
- `+`(3, 4)
- AST:
- .. code-block:: nim
- nnkCall(
- nnkAccQuoted(
- nnkIdent(!"+")
- ),
- nnkIntLit(3),
- nnkIntLit(4)
- )
- Prefix operator call
- --------------------
- Concrete syntax:
- .. code-block:: nim
- ? "xyz"
- AST:
- .. code-block:: nim
- nnkPrefix(
- nnkIdent(!"?"),
- nnkStrLit("abc")
- )
- Postfix operator call
- ---------------------
- **Note:** There are no postfix operators in Nim. However, the
- ``nnkPostfix`` node is used for the *asterisk export marker* ``*``:
- Concrete syntax:
- .. code-block:: nim
- identifier*
- AST:
- .. code-block:: nim
- nnkPostfix(
- nnkIdent(!"*"),
- nnkIdent(!"identifier")
- )
- Call with named arguments
- -------------------------
- Concrete syntax:
- .. code-block:: nim
- writeLine(file=stdout, "hallo")
- AST:
- .. code-block:: nim
- nnkCall(
- nnkIdent(!"writeLine"),
- nnkExprEqExpr(
- 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
- ``re"some regexp"`` in the regular expression module.
- Concrete syntax:
- .. code-block:: nim
- echo"abc"
- AST:
- .. code-block:: nim
- nnkCallStrLit(
- nnkIdent(!"echo"),
- nnkRStrLit("hello")
- )
- Dereference operator ``[]``
- ---------------------------
- Concrete syntax:
- .. code-block:: nim
- x[]
- AST:
- .. code-block:: nim
- nnkDerefExpr(nnkIdent(!"x"))
- Addr operator
- -------------
- Concrete syntax:
- .. code-block:: nim
- addr(x)
- AST:
- .. code-block:: nim
- nnkAddr(nnkIdent(!"x"))
- Cast operator
- -------------
- Concrete syntax:
- .. code-block:: nim
- cast[T](x)
- AST:
- .. code-block:: nim
- nnkCast(nnkIdent(!"T"), nnkIdent(!"x"))
- Object access operator ``.``
- ----------------------------
- Concrete syntax:
- .. code-block:: nim
- x.y
- AST:
- .. code-block:: 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``.
- Array access operator ``[]``
- ----------------------------
- Concrete syntax:
- .. code-block:: nim
- x[y]
- AST:
- .. code-block:: nim
- nnkBracketExpr(nnkIdent(!"x"), nnkIdent(!"y"))
- Parentheses
- -----------
- Parentheses for affecting operator precedence or tuple construction
- are built with the ``nnkPar`` node.
- Concrete syntax:
- .. code-block:: nim
- (1, 2, (3))
- AST:
- .. code-block:: nim
- nnkPar(nnkIntLit(1), nnkIntLit(2), nnkPar(nnkIntLit(3)))
- Curly braces
- ------------
- Curly braces are used as the set constructor.
- Concrete syntax:
- .. code-block:: nim
- {1, 2, 3}
- AST:
- .. code-block:: nim
- nnkCurly(nnkIntLit(1), nnkIntLit(2), nnkIntLit(3))
- When used as a table constructor, the syntax is different.
- Concrete syntax:
- .. code-block:: nim
- {a: 3, b: 5}
- AST:
- .. code-block:: nim
- nnkTableConstr(
- nnkExprColonExpr(nnkIdent(!"a"), nnkIntLit(3)),
- nnkExprColonExpr(nnkIdent(!"b"), nnkIntLit(5))
- )
- Brackets
- --------
- Brackets are used as the array constructor.
- Concrete syntax:
- .. code-block:: nim
- [1, 2, 3]
- AST:
- .. code-block:: nim
- nnkBracket(nnkIntLit(1), nnkIntLit(2), nnkIntLit(3))
- Ranges
- ------
- Ranges occur in set constructors, case statement branches, or array slices.
- Internally, the node kind ``nnkRange`` is used, but when constructing the
- AST, construction with ``..`` as an infix operator should be used instead.
- Concrete syntax:
- .. code-block:: nim
- 1..3
- AST:
- .. code-block:: nim
- nnkInfix(
- nnkIdent(!".."),
- nnkIntLit(1),
- nnkIntLit(3)
- )
- Example code:
- .. code-block:: nim
- macro genRepeatEcho(): stmt =
- result = newNimNode(nnkStmtList)
- var forStmt = newNimNode(nnkForStmt) # generate a for statement
- forStmt.add(ident("i")) # use the variable `i` for iteration
- var rangeDef = newNimNode(nnkInfix).add(
- ident("..")).add(
- newIntLitNode(3),newIntLitNode(5)) # iterate over the range 3..5
- forStmt.add(rangeDef)
- forStmt.add(newCall(ident("echo"), newIntLitNode(3))) # meat of the loop
- result.add(forStmt)
- genRepeatEcho() # gives:
- # 3
- # 3
- # 3
- If expression
- -------------
- The representation of the ``if`` expression is subtle, but easy to traverse.
- Concrete syntax:
- .. code-block:: nim
- if cond1: expr1 elif cond2: expr2 else: expr3
- AST:
- .. code-block:: nim
- nnkIfExpr(
- nnkElifExpr(cond1, expr1),
- nnkElifExpr(cond2, expr2),
- nnkElseExpr(expr3)
- )
- Documentation Comments
- ----------------------
- Double-hash (``##``) comments in the code actually have their own format,
- using ``strVal`` to get and set the comment text. Single-hash (``#``)
- comments are ignored.
- Concrete syntax:
- .. code-block:: nim
- ## This is a comment
- ## This is part of the first comment
- stmt1
- ## Yet another
- AST:
- .. code-block:: nim
- nnkCommentStmt() # only appears once for the first two lines!
- stmt1
- nnkCommentStmt() # another nnkCommentStmt because there is another comment
- # (separate from the first)
- Pragmas
- -------
- One of Nim's cool features is pragmas, which allow fine-tuning of various
- aspects of the language. They come in all types, such as adorning procs and
- objects, but the standalone ``emit`` pragma shows the basics with the AST.
- Concrete syntax:
- .. code-block:: nim
- {.emit: "#include <stdio.h>".}
- AST:
- .. code-block:: nim
- nnkPragma(
- nnkExprColonExpr(
- 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
- {.pragma: cdeclRename, cdecl.}
- AST:
- .. code-block:: nim
- nnkPragma(
- nnkExprColonExpr(
- nnkIdent(!"pragma"), # this is always first when declaring a new pragma
- nnkIdent(!"cdeclRename") # the name of the pragma
- ),
- nnkIdent(!"cdecl")
- )
- Statements
- ==========
- If statement
- ------------
- The representation of the if statement is subtle, but easy to traverse. If
- there is no ``else`` branch, no ``nnkElse`` child exists.
- Concrete syntax:
- .. code-block:: nim
- if cond1:
- stmt1
- elif cond2:
- stmt2
- elif cond3:
- stmt3
- else:
- stmt4
- AST:
- .. code-block:: nim
- nnkIfStmt(
- nnkElifBranch(cond1, stmt1),
- nnkElifBranch(cond2, stmt2),
- nnkElifBranch(cond3, stmt3),
- nnkElse(stmt4)
- )
- When statement
- --------------
- Like the ``if`` statement, but the root has the kind ``nnkWhenStmt``.
- Assignment
- ----------
- Concrete syntax:
- .. code-block:: nim
- x = 42
- AST:
- .. code-block:: nim
- nnkAsgn(nnkIdent(!"x"), nnkIntLit(42))
- This is not the syntax for assignment when combined with ``var``, ``let``,
- or ``const``.
- Statement list
- --------------
- Concrete syntax:
- .. code-block:: nim
- stmt1
- stmt2
- stmt3
- AST:
- .. code-block:: nim
- nnkStmtList(stmt1, stmt2, stmt3)
- Case statement
- --------------
- Concrete syntax:
- .. code-block:: nim
- case expr1
- of expr2, expr3..expr4:
- stmt1
- of expr5:
- stmt2
- elif cond1:
- stmt3
- else:
- stmt4
- AST:
- .. code-block:: nim
- nnkCaseStmt(
- expr1,
- nnkOfBranch(expr2, nnkRange(expr3, expr4), stmt1),
- nnkOfBranch(expr5, stmt2),
- nnkElifBranch(cond1, stmt3),
- nnkElse(stmt4)
- )
- The ``nnkElifBranch`` and ``nnkElse`` parts may be missing.
- While statement
- ---------------
- Concrete syntax:
- .. code-block:: nim
- while expr1:
- stmt1
- AST:
- .. code-block:: nim
- nnkWhileStmt(expr1, stmt1)
- For statement
- -------------
- Concrete syntax:
- .. code-block:: nim
- for ident1, ident2 in expr1:
- stmt1
- AST:
- .. code-block:: nim
- nnkForStmt(ident1, ident2, expr1, stmt1)
- Try statement
- -------------
- Concrete syntax:
- .. code-block:: nim
- try:
- stmt1
- except e1, e2:
- stmt2
- except e3:
- stmt3
- except:
- stmt4
- finally:
- stmt5
- AST:
- .. code-block:: nim
- nnkTryStmt(
- stmt1,
- nnkExceptBranch(e1, e2, stmt2),
- nnkExceptBranch(e3, stmt3),
- nnkExceptBranch(stmt4),
- nnkFinally(stmt5)
- )
- Return statement
- ----------------
- Concrete syntax:
- .. code-block:: nim
- return expr1
- AST:
- .. code-block:: nim
- nnkReturnStmt(expr1)
- Yield statement
- ---------------
- Like ``return``, but with ``nnkYieldStmt`` kind.
- .. code-block:: nim
- nnkYieldStmt(expr1)
- Discard statement
- -----------------
- Like ``return``, but with ``nnkDiscardStmt`` kind.
- .. code-block:: nim
- nnkDiscardStmt(expr1)
- Continue statement
- ------------------
- Concrete syntax:
- .. code-block:: nim
- continue
- AST:
- .. code-block:: nim
- nnkContinueStmt()
- Break statement
- ---------------
- Concrete syntax:
- .. code-block:: nim
- break otherLocation
- AST:
- .. code-block:: nim
- nnkBreakStmt(nnkIdent(!"otherLocation"))
- If ``break`` is used without a jump-to location, ``nnkEmpty`` replaces ``nnkIdent``.
- Block statement
- ---------------
- Concrete syntax:
- .. code-block:: nim
- block name:
- AST:
- .. code-block:: nim
- nnkBlockStmt(nnkIdent(!"name"), nnkStmtList(...))
- A ``block`` doesn't need an name, in which case ``nnkEmpty`` is used.
- Asm statement
- -------------
- Concrete syntax:
- .. code-block:: nim
- asm """
- some asm
- """
- AST:
- .. code-block:: nim
- nnkAsmStmt(
- nnkEmpty(), # for pragmas
- nnkTripleStrLit("some asm"),
- )
- Import section
- --------------
- Nim's ``import`` statement actually takes different variations depending
- on what keywords are present. Let's start with the simplest form.
- Concrete syntax:
- .. code-block:: nim
- import math
- AST:
- .. code-block:: nim
- nnkImportStmt(nnkIdent(!"math"))
- With ``except``, we get ``nnkImportExceptStmt``.
- Concrete syntax:
- .. code-block:: nim
- import math except pow
- AST:
- .. code-block:: nim
- nnkImportExceptStmt(nnkIdent(!"math"),nnkIdent(!"pow"))
- Note that ``import 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
- AST:
- .. code-block:: nim
- nnkImportStmt(
- nnkInfix(
- nnkIdent(!"as"),
- nnkIdent(!"strutils"),
- nnkIdent(!"su")
- )
- )
- From statement
- --------------
- If we use ``from ... import``, the result is different, too.
- Concrete syntax:
- .. code-block:: nim
- from math import pow
- AST:
- .. code-block:: nim
- nnkFromStmt(nnkIdent(!"math"), nnkIdent(!"pow"))
- Using ``from math as m import pow`` works identically to the ``as`` modifier
- with the ``import`` statement, but wrapped in ``nnkFromStmt``.
- Export statement
- ----------------
- When you are making an imported module accessible by modules that import yours,
- the ``export`` syntax is pretty straightforward.
- Concrete syntax:
- .. code-block:: nim
- export unsigned
- AST:
- .. code-block:: nim
- nnkExportStmt(nnkIdent(!"unsigned"))
- Similar to the ``import`` statement, the AST is different for
- ``export ... except``.
- Concrete syntax:
- .. code-block:: nim
- export math except pow # we're going to implement our own exponentiation
- AST:
- .. code-block:: nim
- nnkExportExceptStmt(nnkIdent(!"math"),nnkIdent(!"pow"))
- Include statement
- -----------------
- Like a plain ``import`` statement but with ``nnkIncludeStmt``.
- Concrete syntax:
- .. code-block:: nim
- include blocks
- AST:
- .. code-block:: nim
- nnkIncludeStmt(nnkIdent(!"blocks"))
- Var section
- -----------
- Concrete syntax:
- .. code-block:: nim
- var a = 3
- AST:
- .. code-block:: nim
- nnkVarSection(
- nnkIdentDefs(
- 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)
- for details.
- Let section
- -----------
- This is equivalent to ``var``, but with ``nnkLetSection`` rather than
- ``nnkVarSection``.
- Concrete syntax:
- .. code-block:: nim
- let v = 3
- AST:
- .. code-block:: nim
- nnkLetSection(
- nnkIdentDefs(
- nnkIdent(!"a"),
- nnkEmpty(), # or nnkIdent(...) for the type
- nnkIntLit(3),
- )
- )
- Const section
- -------------
- Concrete syntax:
- .. code-block:: nim
- const a = 3
- AST:
- .. code-block:: nim
- nnkConstSection(
- nnkConstDef( # not nnkConstDefs!
- nnkIdent(!"a"),
- nnkEmpty(), # or nnkIdent(...) if the variable declares the type
- nnkIntLit(3), # required in a const declaration!
- )
- )
- Type section
- ------------
- Starting with the simplest case, a ``type`` section appears much like ``var``
- and ``const``.
- Concrete syntax:
- .. code-block:: nim
- type A = int
- AST:
- .. code-block:: nim
- nnkTypeSection(
- nnkTypeDef(
- nnkIdent(!"A"),
- nnkEmpty(),
- nnkIdent(!"int")
- )
- )
- Declaring ``distinct`` types is similar, with the last ``nnkIdent`` wrapped
- in ``nnkDistinctTy``.
- Concrete syntax:
- .. code-block:: nim
- type MyInt = distinct int
- AST:
- .. code-block:: nim
- # ...
- nnkTypeDef(
- nnkIdent(!"MyInt"),
- nnkEmpty(),
- nnkDistinctTy(
- nnkIdent(!"int")
- )
- )
- If a type section uses generic parameters, they are treated here:
- Concrete syntax:
- .. code-block:: nim
- type A[T] = expr1
- AST:
- .. code-block:: nim
- nnkTypeSection(
- nnkTypeDef(
- nnkIdent(!"A"),
- nnkGenericParams(
- nnkIdentDefs(
- nnkIdent(!"T"),
- nnkEmpty(), # if the type is declared with options, like
- # ``[T: SomeInteger]``, they are given here
- nnkEmpty(),
- )
- )
- expr1,
- )
- )
- Note that not all ``nnkTypeDef`` utilize ``nnkIdent`` as their
- their parameter. One of the most common uses of type declarations
- is to work with objects.
- Concrete syntax:
- .. code-block:: nim
- type IO = object of RootObj
- AST:
- .. code-block:: nim
- # ...
- nnkTypeDef(
- nnkIdent(!"IO"),
- nnkEmpty(),
- nnkObjectTy(
- nnkEmpty(), # no pragmas here
- nnkOfInherit(
- 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.}
- name: string
- case isFat: bool
- of true:
- m: array[100_000, T]
- of false:
- m: array[10, T]
- AST:
- .. code-block:: nim
- # ...
- nnkObjectTy(
- nnkPragma(
- nnkIdent(!"inheritable")
- ),
- nnkEmpty(),
- nnkRecList( # list of object parameters
- nnkIdentDefs(
- nnkIdent(!"name"),
- nnkIdent(!"string"),
- nnkEmpty()
- ),
- nnkRecCase( # case statement within object (not nnkCaseStmt)
- nnkIdentDefs(
- nnkIdent(!"isFat"),
- nnkIdent(!"bool"),
- nnkEmpty()
- ),
- nnkOfBranch(
- nnkIdent(!"true"),
- nnkRecList( # again, a list of object parameters
- nnkIdentDefs(
- nnkIdent(!"m"),
- nnkBracketExpr(
- nnkIdent(!"array"),
- nnkIntLit(100000),
- nnkIdent(!"T")
- ),
- nnkEmpty()
- )
- ),
- nnkOfBranch(
- nnkIdent(!"false"),
- nnkRecList(
- nnkIdentDefs(
- nnkIdent(!"m"),
- nnkBracketExpr(
- nnkIdent(!"array"),
- nnkIntLit(10),
- nnkIdent(!"T")
- ),
- nnkEmpty()
- )
- )
- )
- )
- )
- )
- Using an ``enum`` is similar to using an ``object``.
- Concrete syntax:
- .. code-block:: nim
- type X = enum
- First
- AST:
- .. code-block:: nim
- # ...
- nnkEnumTy(
- nnkEmpty(),
- 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
- type Con = concept x,y,z
- (x & y & z) is string
- AST:
- .. code-block:: nim
- # ...
- nnkTypeClassTy( # note this isn't nnkConceptTy!
- nnkArglist(
- # ... idents for x, y, z
- )
- # ...
- )
- Static types, like ``static[int]``, use ``nnkIdent`` wrapped in
- ``nnkStaticTy``.
- Concrete syntax:
- .. code-block:: nim
- type A[T: static[int]] = object
- AST:
- .. code-block:: nim
- # ... within nnkGenericParams
- nnkIdentDefs(
- nnkIdent(!"T"),
- nnkStaticTy(
- 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``
- ``ptr`` ``nnkPtrTy``
- ``ref`` ``nnkRefTy``
- ``distinct`` ``nnkDistinctTy``
- ``enum`` ``nnkEnumTy``
- ``concept`` ``nnkTypeClassTy``\*
- ``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``.
- Generic parameters are treated in the type, not the ``proc`` itself.
- Concrete syntax:
- .. code-block:: nim
- type MyProc[T] = proc(x: T)
- AST:
- .. code-block:: nim
- # ...
- nnkTypeDef(
- nnkIdent(!"MyProc"),
- nnkGenericParams( # here, not with the proc
- # ...
- )
- nnkProcTy( # behaves like a procedure declaration from here on
- nnkFormalParams(
- # ...
- )
- )
- )
- The same syntax applies to ``iterator`` (with ``nnkIteratorTy``), but
- *does not* apply to ``converter`` or ``template``.
- Mixin statement
- ---------------
- Concrete syntax:
- .. code-block:: nim
- mixin x
- AST:
- .. code-block:: nim
- nnkMixinStmt(nnkIdent(!"x"))
- Bind statement
- --------------
- Concrete syntax:
- .. code-block:: nim
- bind x
- AST:
- .. code-block:: nim
- nnkBindStmt(nnkIdent(!"x"))
- Procedure declaration
- ---------------------
- Let's take a look at a procedure with a lot of interesting aspects to get
- a feel for how procedure calls are broken down.
- Concrete syntax:
- .. code-block:: nim
- proc hello*[T: SomeInteger](x: int = 3, y: float32): int {.inline.} = discard
- AST:
- .. code-block:: nim
- nnkProcDef(
- 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")
- )
- ),
- nnkFormalParams(
- 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)
- nnkIntLit(3) # a default value
- ),
- nnkIdentDefs(
- nnkIdent(!"y"),
- nnkIdent(!"float32"),
- nnkEmpty()
- )
- 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)``
- are equivalent in the code, the AST is a little different for the latter.
- Concrete syntax:
- .. code-block:: nim
- proc(a, b: int)
- AST:
- .. code-block:: 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
- 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
- proc hello(): var int
- AST:
- .. code-block:: nim
- # ...
- nnkFormalParams(
- nnkVarTy(
- nnkIdent(!"int")
- )
- )
- Iterator declaration
- --------------------
- The syntax for iterators is similar to procs, but with ``nnkIteratorDef``
- replacing ``nnkProcDef``.
- Concrete syntax:
- .. code-block:: nim
- iterator nonsense[T](x: seq[T]): float {.closure.} = ...
- AST:
- .. code-block:: nim
- nnkIteratorDef(
- nnkIdent(!"nonsense"),
- nnkEmpty(),
- ...
- )
- Converter declaration
- ---------------------
- A converter is similar to a proc.
- Concrete syntax:
- .. code-block:: nim
- converter toBool(x: float): bool
- AST:
- .. code-block:: nim
- nnkConverterDef(
- 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
- the ``nnkEmpty()`` as the second argument to ``nnkProcDef`` and
- ``nnkIteratorDef`` above? That's where the term-rewriting macros go.
- Concrete syntax:
- .. code-block:: nim
- template optOpt{expr1}(a: int): int
- AST:
- .. code-block:: nim
- nnkTemplateDef(
- 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``.
- Macro declaration
- -----------------
- Macros behave like templates, but ``nnkTemplateDef`` is replaced with
- ``nnkMacroDef``.
- Special node kinds
- ==================
- There are several node kinds that are used for semantic checking or code
- generation. These are accessible from this module, but should not be used.
- Other node kinds are especially designed to make AST manipulations easier.
- These are explained here.
- To be written.
|