summary refs log tree commit diff stats
path: root/doc
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2009-06-08 08:13:09 +0200
committerAndreas Rumpf <rumpf_a@web.de>2009-06-08 08:13:09 +0200
commit36818817bd61594ea6d106328bb8df0f5a25cfc4 (patch)
tree58180069c9a32400e7e11ba5bc5c85be2f11f0cc /doc
parent4d4b3b1c04d41868ebb58bd9ccba7b303007e900 (diff)
downloadNim-36818817bd61594ea6d106328bb8df0f5a25cfc4.tar.gz
version0.7.10
Diffstat (limited to 'doc')
-rw-r--r--doc/abstypes.txt152
-rw-r--r--doc/astspec.txt572
2 files changed, 724 insertions, 0 deletions
diff --git a/doc/abstypes.txt b/doc/abstypes.txt
new file mode 100644
index 000000000..00a42639c
--- /dev/null
+++ b/doc/abstypes.txt
@@ -0,0 +1,152 @@
+==============
+Abstract types
+==============
+
+.. contents::
+
+Abstract types in Nimrod provide a means to model different `units`:idx: of
+a `base type`:idx:.
+
+
+Use case 1: SQL strings
+-----------------------
+An SQL statement that is passed from Nimrod to an SQL database might be 
+modelled as a string. However, using string templates and filling in the
+values is vulnerable to the famous `SQL injection attack`:idx:\: 
+
+.. code-block:: nimrod
+  proc query(db: TDbHandle, statement: TSQL) = ...
+
+  var
+    username: string
+  
+  db.query("SELECT FROM users WHERE name = '$1'" % username)
+  # Horrible security whole, but the compiler does not mind!
+  
+This can be avoided by distinguishing strings that contain SQL from strings 
+that don't. Abstract types provide a means to introduce a new string type 
+``TSQL`` that is incompatible with ``string``:
+
+.. code-block:: nimrod
+  type
+    TSQL = abstract string
+
+  proc query(db: TDbHandle, statement: TSQL) = ...
+
+  var
+    username: string
+  
+  db.query("SELECT FROM users WHERE name = '$1'" % username)
+  # Error at compile time: `query` expects an SQL string!
+  
+  
+It is an essential property of abstract types that they **do not** imply a
+subtype relation between the abtract type and its base type. Explict type
+conversions from ``string`` to ``TSQL`` are allowed: 
+
+.. code-block:: nimrod
+  proc properQuote(s: string): TSQL = 
+    # quotes a string properly for an SQL statement
+    ...
+  
+  proc `%` (frmt: TSQL, values: openarray[string]): TSQL = 
+    # quote each argument:
+    var v = values.each(properQuote)
+    # we need a temporary type for the type conversion :-(
+    type TStrSeq = seq[string]
+    # call strutils.`%`: 
+    result = TSQL(string(frmt) % TStrSeq(v))
+
+  db.query("SELECT FROM users WHERE name = $1".TSQL % username)
+
+Now we have compile-time checking against SQL injection attacks.
+Since ``"".TSQL`` is transformed to ``TSQL("")`` no new syntax is needed
+for nice looking ``TSQL`` string literals.
+
+
+
+Use case 2: Money
+-----------------
+Different currencies should not be mixed in monetary calculations. Abstract
+types are a perfect tool to model different currencies: 
+
+.. code-block:: nimrod
+  type
+    TDollar = abstract int
+    TEuro = abstract int
+  
+  var
+    d: TDollar
+    e: TEuro
+    
+  echo d + 12  
+  # Error: cannot add a number with no unit with a ``TDollar``
+
+Unfortunetaly, ``d + 12.TDollar`` is not allowed either, 
+because ``+`` is defined for ``int`` (among others), not for ``TDollar``. So
+we define our own ``+`` for dollars: 
+
+.. code-block:: 
+  proc `+` (x, y: TDollar): TDollar = 
+    result = TDollar(int(x) + int(y))
+
+It does not make sense to multiply a dollar with a dollar, but with a
+unit-less number; and the same holds for division:
+
+.. code-block:: 
+  proc `*` (x: TDollar, y: int): TDollar = 
+    result = TDollar(int(x) * y)
+
+  proc `*` (x: int, y: TDollar): TDollar = 
+    result = TDollar(x * int(y))
+    
+  proc `div` ...
+
+This quickly gets tedious. The implementations are trivial and the compiler 
+should not generate all this code only to optimize it away later - after all
+``+`` for dollars should produce the same binary code as ``+`` for ints. 
+The pragma ``borrow`` has been designed to solve this problem; in principle
+it generates the trivial implementation for us:
+
+.. code-block:: nimrod
+  proc `*` (x: TDollar, y: int): TDollar {.borrow.}
+  proc `*` (x: int, y: TDollar): TDollar {.borrow.}
+  proc `div` (x: TDollar, y: int): TDollar {.borrow.}
+
+The ``borrow`` pragma makes the compiler use the same implementation as 
+the proc that deals with the abstract type's base type, so no code is
+generated. 
+
+But it seems we still have to repeat all this boilerplate code for
+the ``TEuro`` currency. Fortunately, Nimrod has a template mechanism: 
+
+.. code-block:: nimrod
+  template Additive(typ: typeExpr): stmt =
+    proc `+` *(x, y: typ): typ {.borrow.}
+    proc `-` *(x, y: typ): typ {.borrow.}
+    
+    # unary operators:
+    proc `+` *(x: typ): typ {.borrow.}
+    proc `-` *(x: typ): typ {.borrow.}
+    
+  template Multiplicative(typ, base: typeExpr): stmt = 
+    proc `*` *(x: typ, y: base): typ {.borrow.}
+    proc `*` *(x: base, y: typ): typ {.borrow.}
+    proc `div` *(x: typ, y: base): typ {.borrow.}
+    proc `mod` *(x: typ, y: base): typ {.borrow.}
+    
+  template Comparable(typ: typeExpr): stmt = 
+    proc `<` * (x, y: typ): bool {.borrow.}
+    proc `<=` * (x, y: typ): bool {.borrow.}
+    proc `==` * (x, y: typ): bool {.borrow.}
+  
+  template DefineCurrency(typ, base: expr): stmt = 
+    type
+      typ* = abstract base
+    Additive(typ)
+    Multiplicative(typ, base)
+    Comparable(typ)
+    
+  DefineCurrency(TDollar, int)
+  DefineCurrency(TEuro, int)
+
diff --git a/doc/astspec.txt b/doc/astspec.txt
new file mode 100644
index 000000000..eec2808fd
--- /dev/null
+++ b/doc/astspec.txt
@@ -0,0 +1,572 @@
+The AST in Nimrod
+=================
+This section describes how the AST is modelled with Nimrod's type system.
+The AST consists of nodes (``PNimrodNode``) with a variable number of
+children. Each node has a field named ``kind`` which describes what the node
+contains:
+
+.. code-block:: nimrod
+
+  type
+    TNimrodNodeKind = 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
+
+    PNimrodNode = ref TNimrodNode
+    TNimrodNode {.final.} = object
+      case kind                        ## the node's kind
+      of nnkNone, nnkEmpty, nnkNilLit:
+        nil                            ## node contains no additional fields
+      of nnkCharLit..nnkInt64Lit:
+        intVal: biggestInt             ## the int literal
+      of nnkFloatLit..nnkFloat64Lit:
+        floatVal: biggestFloat         ## the float literal
+      of nnkStrLit..nnkTripleStrLit:
+        strVal: string                 ## the string literal
+      of nnkIdent:
+        ident: TNimrodIdent            ## the identifier
+      of nnkSym:
+        symbol: PNimrodSymbol          ## the symbol (after symbol lookup phase)
+      else:
+        sons: seq[PNimrodNode]         ## the node's sons (or children)
+
+For the ``PNimrodNode`` type, the ``[]`` operator has been overloaded:
+``n[i]`` is ``n``'s ``i``-th child.
+
+To specify the AST for the different Nimrod constructs, the notation
+``nodekind(son1, son2, ...)`` or ``nodekind(value)`` or 
+``nodekind(field=value)`` is used.
+
+
+Leaf nodes/Atoms
+================
+A leaf of the AST often corresponds to a terminal symbol in the concrete 
+syntax.
+
+-----------------                ---------------------------------------------
+Nimrod 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.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. However, a macro receives an AST that
+has not been checked for semantics and thus the identifiers have not been
+looked up. Thus macros deal with ``nnkIdent`` nodes. 
+ 
+
+Calls/expressions
+=================
+
+Command call
+------------
+
+Concrete syntax:
+
+.. code-block:: nimrod
+  echo "abc", "xyz"
+
+AST:
+
+.. code-block:: nimrod
+  nnkCommand(nnkIdent(!"echo"), nnkStrLit("abc"), nnkStrLit("xyz"))
+
+
+Call with ``()``
+----------------
+
+Concrete syntax:
+
+.. code-block:: nimrod
+  echo("abc", "xyz")
+
+AST:
+
+.. code-block:: nimrod
+  nnkCall(nnkIdent(!"echo"), nnkStrLit("abc"), nnkStrLit("xyz"))
+
+
+Infix operator call
+-------------------
+
+Concrete syntax:
+
+.. code-block:: nimrod
+  "abc" & "xyz"
+
+AST:
+
+.. code-block:: nimrod
+  nnkInfix(nnkIdent(!"&"), nnkStrLit("abc"), nnkStrLit("xyz"))
+
+
+Prefix operator call
+--------------------
+
+Concrete syntax:
+
+.. code-block:: nimrod
+  ? "xyz"
+
+AST:
+
+.. code-block:: nimrod
+  nnkPrefix(nnkIdent(!"?"), nnkStrLit("abc"))
+
+
+Postfix operator call
+---------------------
+
+**Note:** There are no postfix operators in Nimrod. However, the 
+``nnkPostfix`` node is used for the *asterisk export marker* ``*``:
+
+Concrete syntax:
+
+.. code-block:: nimrod
+  identifier*
+
+AST:
+
+.. code-block:: nimrod
+  nnkPostfix(nnkIdent(!"*"), nnkIdent(!"identifier"))
+
+
+Call with named arguments
+-------------------------
+
+Concrete syntax:
+
+.. code-block:: nimrod
+  writeln(file=stdout, "hallo")
+
+AST:
+
+.. code-block:: nimrod
+  nnkCall(nnkIdent(!"writeln"), 
+          nnkExprEqExpr(nnkIdent(!"file"), nnkIdent(!"stdout")), 
+          nnkStrLit("hallo"))
+
+
+Dereference operator ``^``
+--------------------------
+
+Concrete syntax:
+
+.. code-block:: nimrod
+  x^
+
+AST:
+
+.. code-block:: nimrod
+  nnkDerefExpr(nnkIdent(!"x"))
+
+
+Addr operator
+-------------
+
+Concrete syntax:
+
+.. code-block:: nimrod
+  addr(x)
+
+AST:
+
+.. code-block:: nimrod
+  nnkAddr(nnkIdent(!"x"))
+
+
+Cast operator
+-------------
+
+Concrete syntax:
+
+.. code-block:: nimrod
+  cast[T](x)
+
+AST:
+
+.. code-block:: nimrod
+  nnkCast(nnkIdent(!"T"), nnkIdent(!"x"))
+
+
+Object access operator ``.``
+----------------------------
+
+Concrete syntax:
+
+.. code-block:: nimrod
+  x.y
+
+AST:
+
+.. code-block:: nimrod
+  nnkDotExpr(nnkIdent(!"x"), nnkIdent(!"y"))
+
+
+Array access operator ``[]``
+----------------------------
+
+Concrete syntax:
+
+.. code-block:: nimrod
+  x[y]
+
+AST:
+
+.. code-block:: nimrod
+  nnkBracketExpr(nnkIdent(!"x"), nnkIdent(!"y"))
+
+
+Parentheses
+-----------
+
+Parentheses for affecting operator precedence or tuple construction 
+are built with the ``nnkPar`` node.
+
+Concrete syntax:
+
+.. code-block:: nimrod
+  (1, 2, (3))
+
+AST:
+
+.. code-block:: nimrod
+  nnkPar(nnkIntLit(1), nnkIntLit(2), nnkPar(nnkIntLit(3)))
+  
+  
+Curly braces
+------------
+
+Curly braces are used as the set constructor. 
+
+Concrete syntax:
+
+.. code-block:: nimrod
+  {1, 2, 3}
+
+AST:
+
+.. code-block:: nimrod
+  nnkCurly(nnkIntLit(1), nnkIntLit(2), nnkIntLit(3))
+
+
+Brackets
+--------
+
+Brackets are used as the array constructor.  
+
+Concrete syntax:
+
+.. code-block:: nimrod
+  [1, 2, 3]
+
+AST:
+
+.. code-block:: nimrod
+  nnkBracket(nnkIntLit(1), nnkIntLit(2), nnkIntLit(3))
+
+
+Ranges
+------
+
+Ranges occur in set constructors, case statement branches or array slices.
+
+Concrete syntax:
+
+.. code-block:: nimrod
+  1..3
+
+AST:
+
+.. code-block:: nimrod
+  nnkRange(nnkIntLit(1), nnkIntLit(3))
+
+
+If expression
+-------------
+
+The representation of the if expression is subtle, but easy to traverse.
+
+Concrete syntax:
+
+.. code-block:: nimrod
+  if cond1: expr1 elif cond2: expr2 else: expr3
+
+AST:
+
+.. code-block:: nimrod
+  nnkIfExpr(
+    nnkElifExpr(cond1, expr1),
+    nnkElifExpr(cond2, expr2),
+    nnkElseExpr(expr3)
+  )
+
+
+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:: nimrod
+  if cond1: 
+    stmt1
+  elif cond2:
+    stmt2
+  elif cond3:
+    stmt3
+  else:
+    stmt4
+
+AST:
+
+.. code-block:: nimrod
+  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:: nimrod
+  x = 42
+
+AST:
+
+.. code-block:: nimrod
+  nnkAsgn(nnkIdent(!"x"), nnkIntLit(42))
+
+
+Statement list
+--------------
+
+Concrete syntax:
+
+.. code-block:: nimrod
+  stmt1
+  stmt2
+  stmt3
+
+AST:
+
+.. code-block:: nimrod
+  nnkStmtList(stmt1, stmt2, stmt3)
+
+  
+Case statement
+--------------
+
+Concrete syntax:
+
+.. code-block:: nimrod
+  case expr1
+  of expr2, expr3..expr4: 
+    stmt1
+  of expr5:
+    stmt2
+  elif cond1:
+    stmt3
+  else:
+    stmt4
+
+AST:
+
+.. code-block:: nimrod
+  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:: nimrod
+  while expr1:
+    stmt1
+
+AST:
+
+.. code-block:: nimrod
+  nnkWhileStmt(expr1, stmt1)
+
+
+For statement
+-------------
+
+Concrete syntax:
+
+.. code-block:: nimrod
+  for ident1, ident2 in expr1:
+    stmt1
+
+AST:
+
+.. code-block:: nimrod
+  nnkForStmt(ident1, ident2, expr1, stmt1)
+
+
+Try statement
+-------------
+
+Concrete syntax:
+
+.. code-block:: nimrod
+  try:
+    stmt1
+  except e1, e2: 
+    stmt2
+  except e3:
+    stmt3
+  except: 
+    stmt4
+  finally:
+    stmt5
+
+AST:
+
+.. code-block:: nimrod
+  nnkTryStmt(
+    stmt1, 
+    nnkExceptBranch(e1, e2, stmt2), 
+    nnkExceptBranch(e3, stmt3), 
+    nnkExceptBranch(stmt4),
+    nnkFinally(stmt5)
+  )
+
+
+Return statement
+----------------
+
+Concrete syntax:
+
+.. code-block:: nimrod
+  return expr1
+
+AST:
+
+.. code-block:: nimrod
+  nnkReturnStmt(expr1)
+
+
+Yield statement
+---------------
+
+Like ``return``, but with ``nnkYieldStmt`` kind.
+
+
+Discard statement
+-----------------
+
+Like ``return``, but with ``nnkDiscardStmt`` kind.
+
+
+Continue statement
+------------------
+
+Concrete syntax:
+
+.. code-block:: nimrod
+  continue
+
+AST:
+
+.. code-block:: nimrod
+  nnkContinueStmt()
+
+Var section
+-----------
+
+To be written.
+
+
+Const section
+-------------
+
+To be written.
+
+
+Type section
+------------
+
+To be written.
+
+
+Procedure declaration
+---------------------
+
+To be written.
+
+
+Iterator declaration
+--------------------
+
+To be written.
+
+
+Template declaration
+--------------------
+
+To be written.
+
+
+Macro declaration
+-----------------
+
+To be written.
+
+
+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.
+