diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2009-06-08 08:13:09 +0200 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2009-06-08 08:13:09 +0200 |
commit | 36818817bd61594ea6d106328bb8df0f5a25cfc4 (patch) | |
tree | 58180069c9a32400e7e11ba5bc5c85be2f11f0cc /doc | |
parent | 4d4b3b1c04d41868ebb58bd9ccba7b303007e900 (diff) | |
download | Nim-36818817bd61594ea6d106328bb8df0f5a25cfc4.tar.gz |
version0.7.10
Diffstat (limited to 'doc')
-rw-r--r-- | doc/abstypes.txt | 152 | ||||
-rw-r--r-- | doc/astspec.txt | 572 |
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. + |