12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403 |
- 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, nnkCommentStmt, nnkIdent, nnkSym:
- strVal: string ## the string literal
- 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(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.
- 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 a = 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.
|