summary refs log tree commit diff stats
path: root/doc/astspec.txt
diff options
context:
space:
mode:
Diffstat (limited to 'doc/astspec.txt')
-rw-r--r--doc/astspec.txt921
1 files changed, 876 insertions, 45 deletions
diff --git a/doc/astspec.txt b/doc/astspec.txt
index 4c27272e2..c84fad8e8 100644
--- a/doc/astspec.txt
+++ b/doc/astspec.txt
@@ -1,5 +1,6 @@
 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
@@ -23,10 +24,10 @@ contains:
       case kind: NimNodeKind           ## the node's kind
       of nnkNone, nnkEmpty, nnkNilLit:
         discard                        ## node contains no additional fields
-      of nnkCharLit..nnkInt64Lit:
-        intVal: biggestInt             ## the int literal
+      of nnkCharLit..nnkUInt64Lit:
+        intVal: BiggestInt             ## the int literal
       of nnkFloatLit..nnkFloat64Lit:
-        floatVal: biggestFloat         ## the float literal
+        floatVal: BiggestFloat         ## the float literal
       of nnkStrLit..nnkTripleStrLit:
         strVal: string                 ## the string literal
       of nnkIdent:
@@ -40,27 +41,32 @@ 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(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``; 
+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.
+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
+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.0``                         ``nnkFloatLit(floatVal = 42.0)``
+``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")``
@@ -90,7 +96,11 @@ Concrete syntax:
 AST:
 
 .. code-block:: nim
-  nnkCommand(nnkIdent(!"echo"), nnkStrLit("abc"), nnkStrLit("xyz"))
+  nnkCommand(
+    nnkIdent(!"echo"),
+    nnkStrLit("abc"),
+    nnkStrLit("xyz")
+  )
 
 
 Call with ``()``
@@ -104,7 +114,11 @@ Concrete syntax:
 AST:
 
 .. code-block:: nim
-  nnkCall(nnkIdent(!"echo"), nnkStrLit("abc"), nnkStrLit("xyz"))
+  nnkCall(
+    nnkIdent(!"echo"),
+    nnkStrLit("abc"),
+    nnkStrLit("xyz")
+  )
 
 
 Infix operator call
@@ -118,8 +132,53 @@ Concrete syntax:
 AST:
 
 .. code-block:: nim
-  nnkInfix(nnkIdent(!"&"), nnkStrLit("abc"), nnkStrLit("xyz"))
+  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
 --------------------
@@ -132,13 +191,16 @@ Concrete syntax:
 AST:
 
 .. code-block:: nim
-  nnkPrefix(nnkIdent(!"?"), nnkStrLit("abc"))
+  nnkPrefix(
+    nnkIdent(!"?"),
+    nnkStrLit("abc")
+  )
 
 
 Postfix operator call
 ---------------------
 
-**Note:** There are no postfix operators in Nim. However, the 
+**Note:** There are no postfix operators in Nim. However, the
 ``nnkPostfix`` node is used for the *asterisk export marker* ``*``:
 
 Concrete syntax:
@@ -149,7 +211,10 @@ Concrete syntax:
 AST:
 
 .. code-block:: nim
-  nnkPostfix(nnkIdent(!"*"), nnkIdent(!"identifier"))
+  nnkPostfix(
+    nnkIdent(!"*"),
+    nnkIdent(!"identifier")
+  )
 
 
 Call with named arguments
@@ -158,15 +223,39 @@ Call with named arguments
 Concrete syntax:
 
 .. code-block:: nim
-  writeln(file=stdout, "hallo")
+  writeLine(file=stdout, "hallo")
 
 AST:
 
 .. code-block:: nim
-  nnkCall(nnkIdent(!"writeln"), 
-          nnkExprEqExpr(nnkIdent(!"file"), nnkIdent(!"stdout")), 
-          nnkStrLit("hallo"))
+  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 ``[]``
 ---------------------------
@@ -223,6 +312,9 @@ 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 ``[]``
 ----------------------------
@@ -241,7 +333,7 @@ AST:
 Parentheses
 -----------
 
-Parentheses for affecting operator precedence or tuple construction 
+Parentheses for affecting operator precedence or tuple construction
 are built with the ``nnkPar`` node.
 
 Concrete syntax:
@@ -253,12 +345,12 @@ AST:
 
 .. code-block:: nim
   nnkPar(nnkIntLit(1), nnkIntLit(2), nnkPar(nnkIntLit(3)))
-  
-  
+
+
 Curly braces
 ------------
 
-Curly braces are used as the set constructor. 
+Curly braces are used as the set constructor.
 
 Concrete syntax:
 
@@ -270,11 +362,26 @@ 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.  
+Brackets are used as the array constructor.
 
 Concrete syntax:
 
@@ -290,7 +397,9 @@ AST:
 Ranges
 ------
 
-Ranges occur in set constructors, case statement branches or array slices.
+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:
 
@@ -300,13 +409,39 @@ Concrete syntax:
 AST:
 
 .. code-block:: nim
-  nnkRange(nnkIntLit(1), nnkIntLit(3))
+  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.
+The representation of the ``if`` expression is subtle, but easy to traverse.
 
 Concrete syntax:
 
@@ -322,6 +457,69 @@ AST:
     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.
+
+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
 ==========
@@ -335,7 +533,7 @@ there is no ``else`` branch, no ``nnkElse`` child exists.
 Concrete syntax:
 
 .. code-block:: nim
-  if cond1: 
+  if cond1:
     stmt1
   elif cond2:
     stmt2
@@ -374,6 +572,8 @@ 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
 --------------
@@ -390,7 +590,7 @@ AST:
 .. code-block:: nim
   nnkStmtList(stmt1, stmt2, stmt3)
 
-  
+
 Case statement
 --------------
 
@@ -398,7 +598,7 @@ Concrete syntax:
 
 .. code-block:: nim
   case expr1
-  of expr2, expr3..expr4: 
+  of expr2, expr3..expr4:
     stmt1
   of expr5:
     stmt2
@@ -459,11 +659,11 @@ Concrete syntax:
 .. code-block:: nim
   try:
     stmt1
-  except e1, e2: 
+  except e1, e2:
     stmt2
   except e3:
     stmt3
-  except: 
+  except:
     stmt4
   finally:
     stmt5
@@ -472,9 +672,9 @@ AST:
 
 .. code-block:: nim
   nnkTryStmt(
-    stmt1, 
-    nnkExceptBranch(e1, e2, stmt2), 
-    nnkExceptBranch(e3, stmt3), 
+    stmt1,
+    nnkExceptBranch(e1, e2, stmt2),
+    nnkExceptBranch(e3, stmt3),
     nnkExceptBranch(stmt4),
     nnkFinally(stmt5)
   )
@@ -499,12 +699,18 @@ 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
 ------------------
@@ -519,54 +725,679 @@ 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
 -----------
 
-To be written.
+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(!"v"),
+    nnkEmpty(), # for the type
+    nnkIntLit(3)
+  )
 
 Const section
 -------------
 
-To be written.
+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
 ------------
 
-To be written.
+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
 ---------------------
 
-To be written.
+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
 --------------------
 
-To be written.
+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
 --------------------
 
-To be written.
+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
 -----------------
 
-To be written.
+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 
+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. 
+These are explained here.
 
 To be written.