implemented multi methods

This commit is contained in:
Andreas Rumpf 2009-09-23 23:38:00 +02:00
commit 3f3dda5a77
65 changed files with 11086 additions and 1258 deletions

View file

@ -8,6 +8,8 @@
.. contents::
Abstraction is layering ignorance on top of reality. -- unknown
Directory structure
===================
@ -72,18 +74,6 @@ the same::
./boot [-d:release]
Coding Guidelines
=================
* Use CamelCase, not underscored_identifiers.
* Indent with two spaces.
* Max line length is 80 characters.
* Provide spaces around binary operators if that enhances readability.
* Use a space after a colon, but not before it.
* Start types with a capital ``T``, unless they are pointers which start with
``P``.
Pascal annotations
==================
There are some annotations that the Pascal sources use so that they can

View file

@ -5,6 +5,10 @@ Nimrod Standard Library
:Author: Andreas Rumpf
:Version: |nimrodversion|
..
The good thing about reinventing the wheel is that you can get a round one.
Though the Nimrod Standard Library is still evolving, it is already quite
usable. It is divided into *pure libraries*, *impure libraries* and *wrappers*.
@ -12,10 +16,15 @@ Pure libraries do not depend on any external ``*.dll`` or ``lib*.so`` binary
while impure libraries do. A wrapper is an impure library that is a very
low-level interface to a C library.
Read this `document <apis.html>`_ for a quick overview of the API design.
Pure libraries
==============
Core
----
* `system <system.html>`_
Basic procs and operators that every program needs. It also provides IO
facilities for reading and writing text and binary files. It is imported
@ -25,11 +34,35 @@ Pure libraries
* `macros <macros.html>`_
Contains the AST API and documentation of Nimrod for writing macros.
String handling
---------------
* `strutils <strutils.html>`_
This module contains common string handling operations like converting a
string into uppercase, splitting a string into substrings, searching for
substrings, replacing substrings.
* `strtabs <strtabs.html>`_
The ``strtabs`` module implements an efficient hash table that is a mapping
from strings to strings. Supports a case-sensitive, case-insensitive and
style-insensitive mode. An efficient string substitution operator ``%``
for the string table is also provided.
* `unicode <unicode.html>`_
This module provides support to handle the Unicode UTF-8 encoding.
* `regexprs <regexprs.html>`_
This module contains procedures and operators for handling regular
expressions. Consider using `pegs` instead.
* `pegs <pegs.html>`_
This module contains procedures and operators for handling PEGs.
Generic Operating System Services
---------------------------------
* `os <os.html>`_
Basic operating system facilities like retrieving environment variables,
reading command line arguments, working with directories, running shell
@ -37,7 +70,28 @@ Pure libraries
platform independant.
* `osproc <osproc.html>`_
Module for process communication beyond ``os.executeShellCommand``.
Module for process communication beyond ``os.execShellCmd``.
* `times <times.html>`_
The ``times`` module contains basic support for working with time.
* `dynlib <dynlib.html>`_
This module implements the ability to access symbols from shared libraries.
* `streams <streams.html>`_
This module provides a stream interface and two implementations thereof:
the `PFileStream` and the `PStringStream` which implement the stream
interface for Nimrod file objects (`TFile`) and strings. Other modules
may provide other implementations for this standard stream interface.
* `terminal <terminal.html>`_
This module contains a few procedures to control the *terminal*
(also called *console*). The implementation simply uses ANSI escape
sequences and does not depend on any other module.
Math libraries
--------------
* `math <math.html>`_
Mathematical operations like cosine, square root.
@ -45,11 +99,17 @@ Pure libraries
* `complex <complex.html>`_
This module implements complex numbers and their mathematical operations.
* `times <times.html>`_
The ``times`` module contains basic support for working with time.
* `dynlib <dynlib.html>`_
This module implements the ability to access symbols from shared libraries.
Internet Protocols and Support
------------------------------
* `cgi <cgi.html>`_
This module implements helpers for CGI applictions.
Parsers
-------
* `parseopt <parseopt.html>`_
The ``parseopt`` module implements a command line option parser. This
@ -71,48 +131,32 @@ Pure libraries
* `parsecsv <parsecsv.html>`_
The ``parsecsv`` module implements a simple high performance CSV parser.
* `parsesql <parsesql.html>`_
The ``parsesql`` module implements a simple high performance SQL parser.
* `strtabs <strtabs.html>`_
The ``strtabs`` module implements an efficient hash table that is a mapping
from strings to strings. Supports a case-sensitive, case-insensitive and
style-insensitive mode. An efficient string substitution operator ``%``
for the string table is also provided.
* `lexbase <lexbase.html>`_
This is a low level module that implements an extremely efficient buffering
scheme for lexers and parsers. This is used by the diverse parsing modules.
* `streams <streams.html>`_
This module provides a stream interface and two implementations thereof:
the `PFileStream` and the `PStringStream` which implement the stream
interface for Nimrod file objects (`TFile`) and strings. Other modules
may provide other implementations for this standard stream interface.
Code generation
---------------
* `xmlgen <xmlgen.html>`_
This module implements macros for XML/HTML code generation.
Cryptography and Hashing
------------------------
* `hashes <hashes.html>`_
This module implements efficient computations of hash values for diverse
Nimrod types.
* `lexbase <lexbase.html>`_
This is a low level module that implements an extremely efficient buffering
scheme for lexers and parsers. This is used by the ``parsecfg`` module.
* `terminal <terminal.html>`_
This module contains a few procedures to control the *terminal*
(also called *console*). The implementation simply uses ANSI escape
sequences and does not depend on any other module.
* `cgi <cgi.html>`_
This module implements helper procs for CGI applictions.
* `unicode <unicode.html>`_
This module provides support to handle the Unicode UTF-8 encoding.
* `regexprs <regexprs.html>`_
This module contains procedures and operators for handling regular
expressions.
* `md5 <md5.html>`_
This module implements the MD5 checksum algorithm.
* `xmlgen <xmlgen.html>`_
This module implements macros for HTML code generation.
Impure libraries

View file

@ -1357,7 +1357,7 @@ given *slicelist* the ``else`` part is executed. If there is no ``else``
part and not all possible values that ``expr`` can hold occur in a ``vallist``,
a static error is given. This holds only for expressions of ordinal types.
If the expression is not of an ordinal type, and no ``else`` part is
given, control just passes after the ``case`` statement.
given, control passes after the ``case`` statement.
To suppress the static error in the ordinal case an ``else`` part with a ``nil``
statement can be used.
@ -1604,7 +1604,9 @@ statement is syntactic sugar for a nested block:
continue
stmt2
# is equivalent to:
Is equivalent to:
.. code-block:: nimrod
while expr1:
block myBlockName:
stmt1
@ -1669,7 +1671,7 @@ object on the stack and can thus reference a non-existing object.
Procedures
~~~~~~~~~~
What most programming languages call `methods`:idx: or `funtions`:idx: are
What most programming languages call `methods`:idx: or `functions`:idx: are
called `procedures`:idx: in Nimrod (which is the correct terminology). A
procedure declaration defines an identifier and associates it with a block
of code. A procedure may call itself recursively. The syntax is::

View file

@ -51,8 +51,8 @@ that its extension should be ``.cfg``.
Command line settings have priority over configuration file settings.
Nimrod's directory structure
----------------------------
Generated C code directory
--------------------------
The generated files that Nimrod produces all go into a subdirectory called
``nimcache`` in your project directory. This makes it easy to delete all
generated files.
@ -116,7 +116,7 @@ No_decl Pragma
The `no_decl`:idx: pragma can be applied to almost any symbol (variable, proc,
type, etc.) and is sometimes useful for interoperability with C:
It tells Nimrod that it should not generate a declaration for the symbol in
the C code. Thus it makes the following possible, for example:
the C code. For example:
.. code-block:: Nimrod
var
@ -129,12 +129,12 @@ However, the ``header`` pragma is often the better alternative.
Header Pragma
~~~~~~~~~~~~~
The `header`:idx: pragma is very similar to the ``no_decl`` pragma: It can be
applied to almost any symbol and specifies that not only it should not be
declared but also that it leads to the inclusion of a given header file:
applied to almost any symbol and specifies that it should not be declared
and instead the generated code should contain an ``#include``:
.. code-block:: Nimrod
type
PFile {.importc: "FILE*", header: "<stdio.h>".} = pointer
PFile {.importc: "FILE*", header: "<stdio.h>".} = distinct pointer
# import C's FILE* type; Nimrod will treat it as a new pointer type
The ``header`` pragma expects always a string constant. The string contant
@ -159,7 +159,7 @@ strings automatically:
Line_dir Option
~~~~~~~~~~~~~~~
The `line_dir`:idx: option can be turned on or off. If on the generated C code
contains ``#line`` directives.
contains ``#line`` directives. This may be helpful for debugging with GDB.
Stack_trace Option
@ -198,7 +198,7 @@ Register Pragma
The `register`:idx: pragma is for variables only. It declares the variable as
``register``, giving the compiler a hint that the variable should be placed
in a hardware register for faster access. C compilers usually ignore this
though and for good reason: Often they do a better job without it anyway.
though and for good reasons: Often they do a better job without it anyway.
In highly specific cases (a dispatch loop of an bytecode interpreter for
example) it may provide benefits, though.
@ -228,7 +228,7 @@ memory, but nothing worse happens.
Dead_code_elim Pragma
~~~~~~~~~~~~~~~~~~~~~
The `dead_code_elim`:idx: pragma only applies to whole modules: It tells the
compiler to active (or deactivate) dead code elimination for the module the
compiler to activate (or deactivate) dead code elimination for the module the
pragma appers in.
The ``--dead_code_elim:on`` command line switch has the same effect as marking
@ -288,9 +288,9 @@ Optimizing string handling
String assignments are sometimes expensive in Nimrod: They are required to
copy the whole string. However, the compiler is often smart enough to not copy
strings. Due to the argument passing semantics, strings are never copied when
passed to subroutines. The compiler does not copy strings that are returned by
a routine, because a routine returns a new string anyway. Thus it is efficient
to do:
passed to subroutines. The compiler does not copy strings that are result from
a procedure call, because the called procedure returns a new string anyway.
Thus it is efficient to do:
.. code-block:: Nimrod
var s = procA() # assignment will not copy the string; procA allocates a new

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
============================
The Nimrod Tutorial (Part I)
============================
========================
Nimrod Tutorial (Part I)
========================
:Author: Andreas Rumpf
:Version: |nimrodversion|
@ -63,7 +63,8 @@ Nimrod: It is a good compromise between brevity and readability.
The "hallo world" program contains several identifiers that are already
known to the compiler: ``echo``, ``readLine``, etc. These built-in items are
declared in the system_ module which is implicitly imported by any other module.
declared in the system_ module which is implicitly imported by any other
module.
Lexical elements
@ -173,7 +174,7 @@ to a storage location:
var x = "abc" # introduces a new variable `x` and assigns a value to it
x = "xyz" # assigns a new value to `x`
The ``=`` is called the *assignment operator*. The assignment operator cannot
``=`` is the *assignment operator*. The assignment operator cannot
be overloaded, overwritten or forbidden, but this might change in a future
version of Nimrod.
@ -196,7 +197,7 @@ constants:
x = 1
# a comment can occur here too
y = 2
z = y + 5 # simple computations are possible
z = y + 5 # computations are possible
Control flow statements
@ -277,14 +278,13 @@ the compiler that for every other value nothing should be done:
The ``nil`` statement is a *do nothing* statement. The compiler knows that a
case statement with an else part cannot fail and thus the error disappers. Note
that it is impossible to cover any possible string value: That is why there is
no such compiler check for string cases.
no such check for string cases.
In general the case statement is used for subrange types or enumerations where
it is of great help that the compiler checks that you covered any possible
value.
While statement
---------------
@ -327,7 +327,7 @@ the same:
Echo($i)
inc(i) # increment i by 1
Counting down can be achieved as easily (but is much less needed):
Counting down can be achieved as easily (but is less often needed):
.. code-block:: nimrod
Echo("Counting down from 10 to 1: ")
@ -372,7 +372,7 @@ Break statement
---------------
A block can be left prematurely with a ``break`` statement. The break statement
can leave a while, for, or a block statement. It leaves the innermost construct,
unless the label of a block is given:
unless a label of a block is given:
.. code-block:: nimrod
block myblock:
@ -392,7 +392,7 @@ unless the label of a block is given:
Continue statement
------------------
Like in many other programming languages, a ``continue`` statement leads to
Like in many other programming languages, a ``continue`` statement starts
the next iteration immediately:
.. code-block:: nimrod
@ -512,7 +512,7 @@ the procedure (and therefore the while loop) immediately. The
parameter named ``question`` of type ``string`` and returns a value of type
``bool``. ``Bool`` is a built-in type: The only valid values for ``bool`` are
``true`` and ``false``.
The conditions in if or while statements need to have the type ``bool``.
The conditions in if or while statements should be of the type ``bool``.
Some terminology: In the example ``question`` is called a (formal) *parameter*,
``"Should I..."`` is called an *argument* that is passed to this parameter.
@ -614,7 +614,7 @@ Now the call to ``createWindow`` only needs to set the values that differ
from the defaults.
Note that type inference works for parameters with default values, there is
no need to specify ``title: string = "unknown"``, for example.
no need to write ``title: string = "unknown"``, for example.
Overloaded procedures
@ -857,7 +857,6 @@ loses information, the `EOutOfRange`:idx: exception is raised (if the error
cannot be detected at compile time).
Floats
------
Nimrod has these floating point types built-in: ``float float32 float64``.
@ -1232,7 +1231,7 @@ mysterious crashes.
**Note**: The example only works because the memory is initialized with zero
(``alloc0`` instead of ``alloc`` does this): ``d.s`` is thus initialized to
``nil`` which the string assignment can handle. You need to know low level
details like this when mixing garbage collected data with unmanaged memory!
details like this when mixing garbage collected data with unmanaged memory.
Procedural type
@ -1240,8 +1239,7 @@ Procedural type
A `procedural type`:idx: is a (somewhat abstract) pointer to a procedure.
``nil`` is an allowed value for a variable of a procedural type.
Nimrod uses procedural types to achieve `functional`:idx: programming
techniques. Dynamic dispatch for OOP constructs can also be implemented with
procedural types (details follow in the OOP section).
techniques.
Example:
@ -1303,9 +1301,9 @@ because then one module cannot be reused without the other.
The algorithm for compiling modules is:
- Compile the whole module as usual, following import statements recursively
- if there is a cycle only import the already parsed symbols (that are
exported); if an unknown identifier occurs then abort
- Compile the whole module as usual, following import statements recursively.
- If there is a cycle only import the already parsed symbols (that are
exported); if an unknown identifier occurs then abort.
This is best illustrated by an example:

View file

@ -1,6 +1,6 @@
=============================
The Nimrod Tutorial (Part II)
=============================
=========================
Nimrod Tutorial (Part II)
=========================
:Author: Andreas Rumpf
:Version: |nimrodversion|
@ -73,9 +73,9 @@ section.
Inheritance is done with the ``object of`` syntax. Multiple inheritance is
currently not supported. If an object type has no suitable ancestor, ``TObject``
should be used as its ancestor, but this is only a convention.
can be used as its ancestor, but this is only a convention.
Note that aggregation (*has-a* relation) is often preferable to inheritance
**Note**: Aggregation (*has-a* relation) is often preferable to inheritance
(*is-a* relation) for simple code reuse. Since objects are value types in
Nimrod, aggregation is as efficient as inheritance.
@ -84,9 +84,9 @@ Mutually recursive types
------------------------
Objects, tuples and references can model quite complex data structures which
depend on each other. This is called *mutually recursive types*. In Nimrod
these types need to be declared within a single type section. Anything else
would require arbitrary symbol lookahead which slows down compilation.
depend on each other; they are *mutually recursive*. In Nimrod
these types can only be declared within a single type section. (Anything else
would require arbitrary symbol lookahead which slows down compilation.)
Example:
@ -147,7 +147,7 @@ An example:
TNode = object
case kind: TNodeKind # the ``kind`` field is the discriminator
of nkInt: intVal: int
of nkFloat: floavVal: float
of nkFloat: floatVal: float
of nkString: strVal: string
of nkAdd, nkSub:
leftOp, rightOp: PNode
@ -176,18 +176,25 @@ bound to a class. This has disadvantages:
* Adding a method to a class the programmer has no control over is
impossible or needs ugly workarounds.
* Often it is unclear where the procedure should belong to: Is
* Often it is unclear where the method should belong to: Is
``join`` a string method or an array method? Should the complex
``vertexCover`` algorithm really be a method of the ``graph`` class?
Nimrod avoids these problems by not distinguishing between methods and
procedures. Methods are just ordinary procedures. However, there is a special
syntactic sugar for calling procedures: The syntax ``obj.method(args)`` can be
used instead of ``method(obj, args)``. If there are no remaining arguments, the
parentheses can be omitted: ``obj.len`` (instead of ``len(obj)``).
Nimrod avoids these problems by not assigning methods to a class. All methods
in Nimrod are `multi-methods`:idx:. As we will see later, multi-methods are
distinguished from procs only for dynamic binding purposes.
Method call syntax
------------------
There is a syntactic sugar for calling routines:
The syntax ``obj.method(args)`` can be used instead of ``method(obj, args)``.
If there are no remaining arguments, the parentheses can be omitted:
``obj.len`` (instead of ``len(obj)``).
This `method call syntax`:idx: is not restricted to objects, it can be used
for any type:
for any type:
.. code-block:: nimrod
@ -196,9 +203,17 @@ for any type:
echo({'a', 'b', 'c'}.card)
stdout.writeln("Hallo") # the same as write(stdout, "Hallo")
If it gives you warm fuzzy feelings, you can even write ``1.`+`(2)`` instead of
``1 + 2`` and claim that Nimrod is a pure object oriented language. (But
that's not true. :-)
(Another way to look at the method call syntax is that it provides the missing
postfix notation.)
So code that looks "pure object oriented" is easy to write:
.. code-block:: nimrod
import strutils
stdout.writeln("Give a list of numbers (separated by spaces): ")
stdout.write(stdin.readLine.split.each(parseInt).max.`$`)
stdout.writeln(" is the maximum!")
Properties
@ -261,60 +276,75 @@ already provides ``v[]`` access.
Dynamic dispatch
----------------
In Nimrod procedural types are used to implement dynamic dispatch. The
following example also shows some more conventions: The ``self`` or ``this``
object is named ``my`` (because it is shorter than the alternatives), each
class provides a constructor, etc.
Procedures always use static dispatch. To get dynamic dispatch, replace the
``proc`` keyword by ``method``:
.. code-block:: nimrod
type
TFigure = object of TObject # abstract base class:
fDraw: proc (my: var TFigure) # concrete classes implement this proc
TExpr = object ## abstract base class for an expression
TLiteral = object of TExpr
x: int
TPlusExpr = object of TExpr
a, b: ref TExpr
method eval(e: ref TExpr): int =
# override this base method
quit "to override!"
method eval(e: ref TLiteral): int = return e.x
method eval(e: ref TPlusExpr): int =
# watch out: relies on dynamic binding
return eval(e.a) + eval(e.b)
proc newLit(x: int): ref TLiteral =
new(result)
result.x = x
proc init(f: var TFigure) =
f.fDraw = nil
proc newPlus(a, b: ref TExpr): ref TPlusExpr =
new(result)
result.a = a
result.b = b
proc draw(f: var TFigure) =
# ``draw`` dispatches dynamically:
f.fDraw(f)
echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
type
TCircle = object of TFigure
radius: int
proc drawCircle(my: var TCircle) = echo("o " & $my.radius)
proc init(my: var TCircle) =
init(TFigure(my)) # call base constructor
my.radius = 5
my.fdraw = drawCircle
Note that in the example the constructors ``newLit`` and ``newPlus`` are procs
because they should use static binding, but ``eval`` is a method because it
requires dynamic binding.
In a multi-method all parameters that have an object type are used for the
dispatching:
.. code-block:: nimrod
type
TRectangle = object of TFigure
width, height: int
TThing = object
TUnit = object of TThing
x: int
method collide(a, b: TThing) {.inline.} =
quit "to override!"
method collide(a: TThing, b: TUnit) {.inline.} =
echo "1"
proc drawRectangle(my: var TRectangle) = echo("[]")
method collide(a: TUnit, b: TThing) {.inline.} =
echo "2"
proc init(my: var TRectangle) =
init(TFigure(my)) # call base constructor
my.width = 5
my.height = 10
my.fdraw = drawRectangle
# now use these classes:
var
r: TRectangle
c: TCircle
init(r)
init(c)
r.draw()
c.draw()
a, b: TUnit
collide(a, b) # output: 2
The code uses a ``draw`` procedure that is bound statically, but inside it
the dynamic dispatch happens with the help of the ``fdraw`` field. Even though
this solution has its advantages compared to traditional OOP-languages, it is
a **preliminary** solution. Multimethods are a planned language feature that
provide a more flexible and efficient mechanism.
As the example demonstrates, invokation of a multi-method cannot be ambiguous:
Collide 2 is prefered over collide 1 because the resolution works from left to
right. Thus ``TUnit, TThing`` is prefered over ``TThing, TUnit``.
**Perfomance note**: Nimrod does not produce a virtual method table, but
generates dispatch trees. This avoids the expensive indirect branch for method
calls and enables inlining. However, other optimizations like compile time
evaluation or dead code elimination do not work with methods.
Exceptions
@ -497,8 +527,7 @@ simple proc for logging:
debug = True
proc log(msg: string) {.inline.} =
if debug:
stdout.writeln(msg)
if debug: stdout.writeln(msg)
var
x = 4
@ -506,7 +535,7 @@ simple proc for logging:
This code has a shortcoming: If ``debug`` is set to false someday, the quite
expensive ``$`` and ``&`` operations are still performed! (The argument
evaluation for procedures is said to be *eager*).
evaluation for procedures is *eager*).
Turning the ``log`` proc into a template solves this problem:
@ -514,21 +543,20 @@ Turning the ``log`` proc into a template solves this problem:
const
debug = True
template log(msg: expr): stmt =
if debug:
stdout.writeln(msg)
template log(msg: string) =
if debug: stdout.writeln(msg)
var
x = 4
log("x has the value: " & $x)
The "types" of templates can be the symbols ``expr`` (stands for *expression*),
``stmt`` (stands for *statement*) or ``typedesc`` (stands for *type
description*). These are no real types, they just help the compiler parsing.
However, real types are supported too.
The parameters' types can be ordinary types or the meta types ``expr``
(stands for *expression*), ``stmt`` (stands for *statement*) or ``typedesc``
(stands for *type description*). If the template has no explicit return type,
``stmt`` is used for consistency with procs and methods.
The template body does not open a new scope. To open a new scope
use a ``block`` statement:
The template body does not open a new scope. To open a new scope use a ``block``
statement:
.. code-block:: nimrod
template declareInScope(x: expr, t: typeDesc): stmt =
@ -573,7 +601,7 @@ In the example the two ``writeln`` statements are bound to the ``actions``
parameter. The ``withFile`` template contains boilerplate code and helps to
avoid a common bug: To forget to close the file. Note how the
``var fn = filename`` statement ensures that ``filename`` is evaluated only
once.
once.
Macros