version 0.7.6
This commit is contained in:
parent
439aa2d04d
commit
e792940f52
106 changed files with 8394 additions and 10083 deletions
21
doc/docs.txt
21
doc/docs.txt
|
|
@ -1,28 +1,25 @@
|
|||
"Incorrect documentation is often worse than no documentation."
|
||||
Incorrect documentation is often worse than no documentation.
|
||||
-- Bertrand Meyer
|
||||
|
||||
The documentation consists of several documents:
|
||||
|
||||
- | `Nimrod tutorial (part I) <tut1.html>`_
|
||||
- | `Tutorial (part I) <tut1.html>`_
|
||||
| The Nimrod tutorial part one deals with the basics.
|
||||
|
||||
- | `Nimrod tutorial (part II) <tut2.html>`_
|
||||
- | `Tutorial (part II) <tut2.html>`_
|
||||
| The Nimrod tutorial part two deals with the advanced language constructs.
|
||||
|
||||
- | `Nimrod manual <manual.html>`_
|
||||
| The Nimrod manual is a draft that will evolve into a proper specification.
|
||||
- | `Library documentation <lib.html>`_
|
||||
| This document describes Nimrod's standard library.
|
||||
|
||||
- | `User guide for the Nimrod Compiler <nimrodc.html>`_
|
||||
- | `User guide <nimrodc.html>`_
|
||||
| The user guide lists command line arguments, special features of the
|
||||
compiler, etc.
|
||||
|
||||
- | `User guide for the Embedded Nimrod Debugger <endb.html>`_
|
||||
| This document describes how to use the Embedded Debugger.
|
||||
- | `Manual <manual.html>`_
|
||||
| The Nimrod manual is a draft that will evolve into a proper specification.
|
||||
|
||||
- | `Nimrod library documentation <lib.html>`_
|
||||
| This document describes Nimrod's standard library.
|
||||
|
||||
- | `Nimrod internal documentation <intern.html>`_
|
||||
- | `Internal documentation <intern.html>`_
|
||||
| The internal documentation describes how the compiler is implemented. Read
|
||||
this if you want to hack the compiler.
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ Short description of Nimrod's modules
|
|||
Module Description
|
||||
============== ==========================================================
|
||||
nimrod main module: parses the command line and calls
|
||||
```main.MainCommand``
|
||||
``main.MainCommand``
|
||||
main implements the top-level command dispatching
|
||||
lexbase buffer handling of the lexical analyser
|
||||
scanner lexical analyser
|
||||
|
|
@ -31,6 +31,7 @@ sigmatch contains the matching algorithm that is used for proc
|
|||
semexprs contains the semantic checking phase for expressions
|
||||
semstmts contains the semantic checking phase for statements
|
||||
semtypes contains the semantic checking phase for types
|
||||
seminst instantiation of generic procs and types
|
||||
semfold contains code to deal with constant folding
|
||||
evals contains an AST interpreter for compile time evaluation
|
||||
pragmas semantic checking of pragmas
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ ifStmt ::= 'if' expr ':' stmt ('elif' expr ':' stmt)* ['else' ':' stmt]
|
|||
whenStmt ::= 'when' expr ':' stmt ('elif' expr ':' stmt)* ['else' ':' stmt]
|
||||
caseStmt ::= 'case' expr [':'] ('of' sliceExprList ':' stmt)*
|
||||
('elif' expr ':' stmt)*
|
||||
['else' ':' stmt]
|
||||
['else' ':' stmt]
|
||||
whileStmt ::= 'while' expr ':' stmt
|
||||
forStmt ::= 'for' symbol (comma symbol)* 'in' expr ['..' expr] ':' stmt
|
||||
exceptList ::= [qualifiedIdent (comma qualifiedIdent)*]
|
||||
|
|
|
|||
50
doc/lib.txt
50
doc/lib.txt
|
|
@ -6,12 +6,15 @@ Nimrod Standard Library
|
|||
:Version: |nimrodversion|
|
||||
|
||||
Though the Nimrod Standard Library is still evolving, it is already quite
|
||||
usable. It is divided into basic libraries that contains modules that virtually
|
||||
every program will need and advanced libraries which are more heavy weight.
|
||||
Advanced libraries are in the ``lib/base`` directory.
|
||||
usable. It is divided into *pure libraries*, *impure libraries* and *wrappers*.
|
||||
|
||||
Basic libraries
|
||||
===============
|
||||
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.
|
||||
|
||||
|
||||
Pure libraries
|
||||
==============
|
||||
|
||||
* `system <system.html>`_
|
||||
Basic procs and operators that every program needs. It also provides IO
|
||||
|
|
@ -51,6 +54,12 @@ Basic libraries
|
|||
literals, raw string literals and triple quote string literals are supported
|
||||
as in the Nimrod programming language.
|
||||
|
||||
* `parsexml <parsexml.html>`_
|
||||
The ``parsexml`` module implements a simple high performance XML/HTML parser.
|
||||
The only encoding that is supported is UTF-8. The parser has been designed
|
||||
to be somewhat error correcting, so that even some "wild HTML" found on the
|
||||
web can be parsed with it.
|
||||
|
||||
* `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
|
||||
|
|
@ -68,17 +77,31 @@ Basic libraries
|
|||
Nimrod types.
|
||||
|
||||
* `lexbase <lexbase.html>`_
|
||||
This is a low level module that implements an extremely efficent buffering
|
||||
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.
|
||||
|
||||
Advanced libaries
|
||||
=================
|
||||
* `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.
|
||||
|
||||
|
||||
Impure libraries
|
||||
================
|
||||
|
||||
* `dialogs <dialogs.html>`_
|
||||
This module implements portable dialogs for Nimrod; the implementation
|
||||
builds on the GTK interface. On Windows, native dialogs are shown if
|
||||
|
|
@ -86,6 +109,11 @@ Advanced libaries
|
|||
|
||||
* `zipfiles <zipfiles.html>`_
|
||||
This module implements a zip archive creator/reader/modifier.
|
||||
|
||||
* `web <web.html>`_
|
||||
This module contains simple high-level procedures for dealing with the
|
||||
web like loading the contents of a web page from an URL.
|
||||
|
||||
|
||||
Wrappers
|
||||
========
|
||||
|
|
@ -97,6 +125,12 @@ not contained in the distribution. You can then find them on the website.
|
|||
Contains a wrapper for the POSIX standard.
|
||||
* `windows <windows.html>`_
|
||||
Contains a wrapper for the Win32 API.
|
||||
* `mysql <mysql.html>`_
|
||||
Contains a wrapper for the mySQL API.
|
||||
* `sqlite3 <sqlite3.html>`_
|
||||
Contains a wrapper for SQLite 3 API.
|
||||
* `libcurl <libcurl.html>`_
|
||||
Contains a wrapper for the libcurl library.
|
||||
* `shellapi <shellapi.html>`_
|
||||
Contains a wrapper for the ``shellapi.h`` header.
|
||||
* `shfolder <shfolder.html>`_
|
||||
|
|
|
|||
|
|
@ -1011,8 +1011,8 @@ char '\0'
|
|||
bool false
|
||||
ref or pointer type nil
|
||||
procedural type nil
|
||||
sequence nil (**not** ``@[]``)
|
||||
string nil (**not** "")
|
||||
sequence nil (*not* ``@[]``)
|
||||
string nil (*not* "")
|
||||
tuple[x: A, y: B, ...] (default(A), default(B), ...)
|
||||
(analogous for objects)
|
||||
array[0..., T] [default(T), ...]
|
||||
|
|
@ -1080,9 +1080,9 @@ Case statement
|
|||
|
||||
Syntax::
|
||||
|
||||
caseStmt ::= 'case' expr ('of' sliceExprList ':' stmt)*
|
||||
('elif' expr ':' stmt)*
|
||||
['else' ':' stmt]
|
||||
caseStmt ::= 'case' expr [':'] ('of' sliceExprList ':' stmt)*
|
||||
('elif' expr ':' stmt)*
|
||||
['else' ':' stmt]
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -1241,11 +1241,11 @@ sugar for:
|
|||
``return`` without an expression is a short notation for ``return result`` if
|
||||
the proc has a return type. The `result`:idx: variable is always the return
|
||||
value of the procedure. It is automatically declared by the compiler. As all
|
||||
variables, ``result`` is initialized to (binary) zero::
|
||||
variables, ``result`` is initialized to (binary) zero:
|
||||
|
||||
.. code-block:: nimrod
|
||||
proc returnZero(): int =
|
||||
# implicitely returns 0
|
||||
proc returnZero(): int =
|
||||
# implicitely returns 0
|
||||
|
||||
|
||||
Yield statement
|
||||
|
|
@ -1649,7 +1649,7 @@ possible within a single ``type`` section.
|
|||
Generics
|
||||
~~~~~~~~
|
||||
|
||||
`Version 0.7.4: Complex generic types like in the example do not work.`:red:
|
||||
`Version 0.7.6: Generic types like in the example do not work.`:red:
|
||||
|
||||
Example:
|
||||
|
||||
|
|
|
|||
|
|
@ -208,8 +208,9 @@ Acyclic Pragma
|
|||
~~~~~~~~~~~~~~
|
||||
The `acyclic`:idx: pragma can be used for object types to mark them as acyclic
|
||||
even though they seem to be cyclic. This is an **optimization** for the garbage
|
||||
collector to not consider objects of this type as part of a cycle::
|
||||
collector to not consider objects of this type as part of a cycle:
|
||||
|
||||
.. code-block:: nimrod
|
||||
type
|
||||
PNode = ref TNode
|
||||
TNode {.acyclic, final.} = object
|
||||
|
|
|
|||
12308
doc/theindex.txt
12308
doc/theindex.txt
File diff suppressed because it is too large
Load diff
110
doc/tut1.txt
110
doc/tut1.txt
|
|
@ -46,12 +46,14 @@ The most used commands and switches have abbreviations, so you can also use::
|
|||
|
||||
Though it should be pretty obvious what the program does, I will explain the
|
||||
syntax: Statements which are not indented are executed when the program
|
||||
starts. Indentation is Nimrod's way of grouping statements. String literals
|
||||
are enclosed in double quotes. The ``var`` statement declares a new variable
|
||||
named ``name`` of type ``string`` with the value that is returned by the
|
||||
``readline`` procedure. Since the compiler knows that ``readline`` returns
|
||||
a string, you can leave out the type in the declaration (this is called
|
||||
`local type inference`:idx:). So this will work too:
|
||||
starts. Indentation is Nimrod's way of grouping statements. Indentation is
|
||||
done with spaces only, tabulators are not allowed.
|
||||
|
||||
String literals are enclosed in double quotes. The ``var`` statement declares
|
||||
a new variable named ``name`` of type ``string`` with the value that is
|
||||
returned by the ``readline`` procedure. Since the compiler knows that
|
||||
``readline`` returns a string, you can leave out the type in the declaration
|
||||
(this is called `local type inference`:idx:). So this will work too:
|
||||
|
||||
.. code-block:: Nimrod
|
||||
var name = readline(stdin)
|
||||
|
|
@ -73,7 +75,7 @@ keywords, comments, operators, and other punctation marks. Case is
|
|||
*insignificant* in Nimrod and even underscores are ignored:
|
||||
``This_is_an_identifier`` and this is the same identifier
|
||||
``ThisIsAnIdentifier``. This feature enables you to use other
|
||||
peoples code without bothering about a naming convention that conflicts with
|
||||
people's code without bothering about a naming convention that conflicts with
|
||||
yours. It also frees you from remembering the exact spelling of an identifier
|
||||
(was it ``parseURL`` or ``parseUrl`` or ``parse_URL``?).
|
||||
|
||||
|
|
@ -129,6 +131,9 @@ the syntax, watch their indentation:
|
|||
Echo("Hi!")
|
||||
# comment has not the right indentation -> syntax error!
|
||||
|
||||
**Note**: To comment out a large piece of code, it is often better to use a
|
||||
``when false:`` statement.
|
||||
|
||||
|
||||
Numbers
|
||||
-------
|
||||
|
|
@ -137,7 +142,7 @@ Numerical literals are written as in most other languages. As a special twist,
|
|||
underscores are allowed for better readability: ``1_000_000`` (one million).
|
||||
A number that contains a dot (or 'e' or 'E') is a floating point literal:
|
||||
``1.0e9`` (one million). Hexadecimal literals are prefixed with ``0x``,
|
||||
binary literals with ``0b`` and octal literals with ``0c``. A leading zero
|
||||
binary literals with ``0b`` and octal literals with ``0o``. A leading zero
|
||||
alone does not produce an octal.
|
||||
|
||||
|
||||
|
|
@ -426,7 +431,11 @@ The ``when`` statement is useful for writing platform specific code, similar to
|
|||
the ``#ifdef`` construct in the C programming language.
|
||||
|
||||
**Note**: The documentation generator currently always follows the first branch
|
||||
of when statements.
|
||||
of when statements.
|
||||
|
||||
**Note**: To comment out a large piece of code, it is often better to use a
|
||||
``when false:`` statement than to use real comments. This way nesting is
|
||||
possible.
|
||||
|
||||
|
||||
Statements and indentation
|
||||
|
|
@ -440,7 +449,7 @@ statements*. *Simple statements* cannot contain other statements:
|
|||
Assignment, procedure calls or the ``return`` statement belong to the simple
|
||||
statements. *Complex statements* like ``if``, ``when``, ``for``, ``while`` can
|
||||
contain other statements. To avoid ambiguities, complex statements always have
|
||||
to be intended, but single simple statements do not:
|
||||
to be indented, but single simple statements do not:
|
||||
|
||||
.. code-block:: nimrod
|
||||
# no indentation needed for single assignment statement:
|
||||
|
|
@ -518,9 +527,11 @@ shorthand for ``return result``. So all tree code snippets are equivalent:
|
|||
.. code-block:: nimrod
|
||||
return 42
|
||||
|
||||
.. code-block:: nimrod
|
||||
result = 42
|
||||
return
|
||||
|
||||
.. code-block:: nimrod
|
||||
result = 42
|
||||
return result
|
||||
|
||||
|
|
@ -880,7 +891,7 @@ In Nimrod new types can be defined within a ``type`` statement:
|
|||
.. code-block:: nimrod
|
||||
type
|
||||
biggestInt = int64 # biggest integer type that is available
|
||||
biggestFLoat = float64 # biggest float type that is available
|
||||
biggestFloat = float64 # biggest float type that is available
|
||||
|
||||
Enumeration and object types cannot be defined on the fly, but only within a
|
||||
``type`` statement.
|
||||
|
|
@ -980,7 +991,7 @@ basetype can only be an ordinal type. The reason is that sets are implemented
|
|||
as high performance bit vectors.
|
||||
|
||||
Sets can be constructed via the set constructor: ``{}`` is the empty set. The
|
||||
empty set is type combatible with any concrete set type. The constructor
|
||||
empty set is type compatible with any concrete set type. The constructor
|
||||
can also be used to include elements (and ranges of elements):
|
||||
|
||||
.. code-block:: nimrod
|
||||
|
|
@ -1013,7 +1024,7 @@ operation meaning
|
|||
================== ========================================================
|
||||
|
||||
Sets are often used to define a type for the *flags* of a procedure. This is
|
||||
much cleaner (and type safe solution) solution than just defining integer
|
||||
much cleaner (and type safe) solution than just defining integer
|
||||
constants that should be ``or``'ed together.
|
||||
|
||||
|
||||
|
|
@ -1047,36 +1058,6 @@ The built-in ``len`` proc returns the array's length. ``low(a)`` returns the
|
|||
lowest valid index for the array `a` and ``high(a)`` the highest valid index.
|
||||
|
||||
|
||||
Open arrays
|
||||
-----------
|
||||
Often fixed size arrays turn out to be too inflexible; procedures should
|
||||
be able to deal with arrays of different sizes. The `openarray`:idx: type
|
||||
allows this. Openarrays are always indexed with an ``int`` starting at
|
||||
position 0. The ``len``, ``low`` and ``high`` operations are available
|
||||
for open arrays too. Any array with a compatible base type can be passed to
|
||||
an openarray parameter, the index type does not matter.
|
||||
|
||||
The openarray type cannot be nested: Multidimensional openarrays are not
|
||||
supported because this is seldom needed and cannot be done efficiently.
|
||||
|
||||
An openarray is also a means to implement passing a variable number of
|
||||
arguments to a procedure. The compiler converts the list of arguments
|
||||
to an array automatically:
|
||||
|
||||
.. code-block:: nimrod
|
||||
proc myWriteln(f: TFile, a: openarray[string]) =
|
||||
for s in items(a):
|
||||
write(f, s)
|
||||
write(f, "\n")
|
||||
|
||||
myWriteln(stdout, "abc", "def", "xyz")
|
||||
# is transformed by the compiler to:
|
||||
myWriteln(stdout, ["abc", "def", "xyz"])
|
||||
|
||||
This transformation is only done if the openarray parameter is the
|
||||
last parameter in the procedure header.
|
||||
|
||||
|
||||
Sequences
|
||||
---------
|
||||
`Sequences`:idx: are similar to arrays but of dynamic length which may change
|
||||
|
|
@ -1108,6 +1089,38 @@ rather than ``nil`` as the *empty* value. But ``@[]`` creates a sequence
|
|||
object on the heap, so there is a trade-off to be made here.
|
||||
|
||||
|
||||
Open arrays
|
||||
-----------
|
||||
**Note**: Openarrays can only be used for parameters.
|
||||
|
||||
Often fixed size arrays turn out to be too inflexible; procedures should
|
||||
be able to deal with arrays of different sizes. The `openarray`:idx: type
|
||||
allows this. Openarrays are always indexed with an ``int`` starting at
|
||||
position 0. The ``len``, ``low`` and ``high`` operations are available
|
||||
for open arrays too. Any array with a compatible base type can be passed to
|
||||
an openarray parameter, the index type does not matter.
|
||||
|
||||
The openarray type cannot be nested: Multidimensional openarrays are not
|
||||
supported because this is seldom needed and cannot be done efficiently.
|
||||
|
||||
An openarray is also a means to implement passing a variable number of
|
||||
arguments to a procedure. The compiler converts the list of arguments
|
||||
to an array automatically:
|
||||
|
||||
.. code-block:: nimrod
|
||||
proc myWriteln(f: TFile, a: openarray[string]) =
|
||||
for s in items(a):
|
||||
write(f, s)
|
||||
write(f, "\n")
|
||||
|
||||
myWriteln(stdout, "abc", "def", "xyz")
|
||||
# is transformed by the compiler to:
|
||||
myWriteln(stdout, ["abc", "def", "xyz"])
|
||||
|
||||
This transformation is only done if the openarray parameter is the
|
||||
last parameter in the procedure header.
|
||||
|
||||
|
||||
Tuples
|
||||
------
|
||||
|
||||
|
|
@ -1271,8 +1284,19 @@ with an asterisk (``*``) are exported:
|
|||
# multiply two int sequences:
|
||||
for i in 0..len(a)-1: result[i] = a[i] * b[i]
|
||||
|
||||
when isMainModule:
|
||||
# test the new ``*`` operator for sequences:
|
||||
assert(@[1, 2, 3] * @[1, 2, 3] == @[1, 4, 9])
|
||||
|
||||
The above module exports ``x`` and ``*``, but not ``y``.
|
||||
|
||||
The top-level statements of a module are executed at the start of the program.
|
||||
This can be used to initalize complex data structures for example.
|
||||
|
||||
Each module has a special magic constant ``isMainModule`` that is true if the
|
||||
module is compiled as the main file. This is very useful to embed tests within
|
||||
the module as shown by the above example.
|
||||
|
||||
Modules that depend on each other are possible, but strongly discouraged,
|
||||
because then one module cannot be reused without the other.
|
||||
|
||||
|
|
|
|||
160
doc/tut2.txt
160
doc/tut2.txt
|
|
@ -33,7 +33,8 @@ Object Oriented Programming
|
|||
While Nimrod's support for object oriented programming (OOP) is minimalistic,
|
||||
powerful OOP technics can be used. OOP is seen as *one* way to design a
|
||||
program, not *the only* way. Often a procedural approach leads to simpler
|
||||
and more efficient code.
|
||||
and more efficient code. In particular, prefering aggregation over inheritance
|
||||
often yields to a better design.
|
||||
|
||||
|
||||
Objects
|
||||
|
|
@ -196,8 +197,8 @@ for any type:
|
|||
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. (That
|
||||
would not even be lying: *pure OO* has no meaning anyway. :-)
|
||||
``1 + 2`` and claim that Nimrod is a pure object oriented language. (But
|
||||
that's not true. :-)
|
||||
|
||||
|
||||
Properties
|
||||
|
|
@ -237,15 +238,15 @@ The ``[]`` array access operator can be overloaded to provide
|
|||
type
|
||||
TVector* = object
|
||||
x, y, z: float
|
||||
|
||||
|
||||
proc `[]=`* (v: var TVector, i: int, value: float) =
|
||||
# setter
|
||||
# setter
|
||||
case i
|
||||
of 0: v.x = value
|
||||
of 1: v.y = value
|
||||
of 2: v.z = value
|
||||
else: assert(false)
|
||||
|
||||
|
||||
proc `[]`* (v: TVector, i: int): float =
|
||||
# getter
|
||||
case i
|
||||
|
|
@ -253,25 +254,29 @@ The ``[]`` array access operator can be overloaded to provide
|
|||
of 1: result = v.y
|
||||
of 2: result = v.z
|
||||
else: assert(false)
|
||||
|
||||
|
||||
The example is silly, since a vector is better modelled by a tuple which
|
||||
already provides ``v[]`` access.
|
||||
|
||||
|
||||
Dynamic binding
|
||||
---------------
|
||||
In Nimrod procedural types are used to implement dynamic binding. 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.
|
||||
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.
|
||||
|
||||
.. code-block:: nimrod
|
||||
type
|
||||
TFigure = object of TObject # abstract base class:
|
||||
draw: proc (my: var TFigure) # concrete classes implement this proc
|
||||
TFigure = object of TObject # abstract base class:
|
||||
fDraw: proc (my: var TFigure) # concrete classes implement this proc
|
||||
|
||||
proc init(f: var TFigure) =
|
||||
f.draw = nil
|
||||
f.fDraw = nil
|
||||
|
||||
proc draw(f: var TFigure) =
|
||||
# ``draw`` dispatches dynamically:
|
||||
f.fDraw(f)
|
||||
|
||||
type
|
||||
TCircle = object of TFigure
|
||||
|
|
@ -282,7 +287,7 @@ provides a constructor, etc.
|
|||
proc init(my: var TCircle) =
|
||||
init(TFigure(my)) # call base constructor
|
||||
my.radius = 5
|
||||
my.draw = drawCircle
|
||||
my.fdraw = drawCircle
|
||||
|
||||
type
|
||||
TRectangle = object of TFigure
|
||||
|
|
@ -294,50 +299,7 @@ provides a constructor, etc.
|
|||
init(TFigure(my)) # call base constructor
|
||||
my.width = 5
|
||||
my.height = 10
|
||||
my.draw = drawRectangle
|
||||
|
||||
# now use these classes:
|
||||
var
|
||||
r: TRectangle
|
||||
c: TCircle
|
||||
init(r)
|
||||
init(c)
|
||||
r.draw(r)
|
||||
c.draw(c)
|
||||
|
||||
The last line shows the syntactical difference between static and dynamic
|
||||
binding: The ``r.draw(r)`` dynamic call refers to ``r`` twice. This difference
|
||||
is not necessarily bad. But if you want to eliminate the somewhat redundant
|
||||
``r``, it can be done by using *closures*:
|
||||
|
||||
.. code-block:: nimrod
|
||||
type
|
||||
TFigure = object of TObject # abstract base class:
|
||||
draw: proc () {.closure.} # concrete classes implement this proc
|
||||
|
||||
proc init(f: var TFigure) =
|
||||
f.draw = nil
|
||||
|
||||
type
|
||||
TCircle = object of TFigure
|
||||
radius: int
|
||||
|
||||
proc init(me: var TCircle) =
|
||||
init(TFigure(me)) # call base constructor
|
||||
me.radius = 5
|
||||
me.draw = lambda () =
|
||||
echo("o " & $me.radius)
|
||||
|
||||
type
|
||||
TRectangle = object of TFigure
|
||||
width, height: int
|
||||
|
||||
proc init(me: var TRectangle) =
|
||||
init(TFigure(me)) # call base constructor
|
||||
me.width = 5
|
||||
me.height = 10
|
||||
me.draw = lambda () =
|
||||
echo("[]")
|
||||
my.fdraw = drawRectangle
|
||||
|
||||
# now use these classes:
|
||||
var
|
||||
|
|
@ -348,10 +310,12 @@ is not necessarily bad. But if you want to eliminate the somewhat redundant
|
|||
r.draw()
|
||||
c.draw()
|
||||
|
||||
The example also introduces `lambda`:idx: expressions: A ``lambda`` expression
|
||||
defines a new proc with the ``closure`` calling convention on the fly.
|
||||
The code uses a ``draw`` procedure that is bound statically, but inside it
|
||||
the dynamic dispatch happens with the help of the ``fdraw`` field. This is
|
||||
slightly more inconvienent than in traditional OOP-languages, but has the
|
||||
advantage of being much more flexible (and somewhat faster). The above approach
|
||||
also allows some form *monkey patching* by modifying the ``fdraw`` field.
|
||||
|
||||
`Version 0.7.4: Closures and lambda expressions are not implemented.`:red:
|
||||
|
||||
|
||||
Exceptions
|
||||
|
|
@ -432,7 +396,7 @@ is not executed (if an exception occurs).
|
|||
Generics
|
||||
========
|
||||
|
||||
`Version 0.7.4: Complex generic types like in the example do not work.`:red:
|
||||
`Version 0.7.6: Complex generic types like in the example do not work.`:red:
|
||||
|
||||
`Generics`:idx: are Nimrod's means to parametrize procs, iterators or types
|
||||
with `type parameters`:idx:. They are most useful for efficient type safe
|
||||
|
|
@ -458,8 +422,8 @@ containers:
|
|||
else:
|
||||
var it = root
|
||||
while it != nil:
|
||||
# compare the data items; uses the generic ``cmd`` proc that works for
|
||||
# any type that has a ``==`` and ``<`` operator
|
||||
# compare the data items; uses the generic ``cmd`` proc
|
||||
# that works for any type that has a ``==`` and ``<`` operator
|
||||
var c = cmp(it.data, n.data)
|
||||
if c < 0:
|
||||
if it.le == nil:
|
||||
|
|
@ -491,7 +455,7 @@ containers:
|
|||
|
||||
var
|
||||
root: PBinaryTree[string] # instantiate a PBinaryTree with ``string``
|
||||
add(root, newNode("hallo")) # instantiates generic procs ``newNode`` and ``add``
|
||||
add(root, newNode("hallo")) # instantiates ``newNode`` and ``add``
|
||||
add(root, "world") # instantiates the second ``add`` proc
|
||||
for str in preorder(root):
|
||||
stdout.writeln(str)
|
||||
|
|
@ -567,6 +531,7 @@ Turning the ``log`` proc into a template solves this problem in an elegant way:
|
|||
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.
|
||||
In later versions, real types will be supported too.
|
||||
|
||||
The template body does not open a new scope. To open a new scope
|
||||
use a ``block`` statement:
|
||||
|
|
@ -587,6 +552,35 @@ use a ``block`` statement:
|
|||
b = 42 # does not work, `b` is unknown
|
||||
|
||||
|
||||
If there is a ``stmt`` parameter it should be the last in the template
|
||||
declaration. The reason is that statements can be passed to a template
|
||||
via a special ``:`` syntax:
|
||||
|
||||
.. code-block:: nimrod
|
||||
|
||||
template withFile(f, filename, mode: expr, actions: stmt): stmt =
|
||||
block:
|
||||
var fn = filename
|
||||
var f: TFile
|
||||
if openFile(f, fn, mode):
|
||||
try:
|
||||
actions
|
||||
finally:
|
||||
closeFile(f)
|
||||
else:
|
||||
quit("cannot open: " & fn)
|
||||
|
||||
withFile(txt, "ttempl3.txt", fmWrite):
|
||||
txt.writeln("line 1")
|
||||
txt.writeln("line 2")
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Macros
|
||||
======
|
||||
|
||||
|
|
@ -658,36 +652,6 @@ The macro call expands to:
|
|||
writeln(stdout, x)
|
||||
|
||||
|
||||
Lets return to the dynamic binding ``r.draw(r)`` notational "problem". Apart
|
||||
from closures, there is another "solution": Define an infix ``!`` macro
|
||||
operator which hides it:
|
||||
|
||||
.. code-block::
|
||||
|
||||
macro `!` (n: expr): expr =
|
||||
result = newNimNode(nnkCall, n)
|
||||
var dot = newNimNode(nnkDotExpr, n)
|
||||
dot.add(n[1]) # obj
|
||||
if n[2].kind == nnkCall:
|
||||
# transforms ``obj!method(arg1, arg2, ...)`` to
|
||||
# ``(obj.method)(obj, arg1, arg2, ...)``
|
||||
dot.add(n[2][0]) # method
|
||||
result.add(dot)
|
||||
result.add(n[1]) # obj
|
||||
for i in 1..n[2].len-1:
|
||||
result.add(n[2][i])
|
||||
else:
|
||||
# transforms ``obj!method`` to
|
||||
# ``(obj.method)(obj)``
|
||||
dot.add(n[2]) # method
|
||||
result.add(dot)
|
||||
result.add(n[1]) # obj
|
||||
|
||||
r!draw(a, b, c) # will be transfomed into ``r.draw(r, a, b, c)``
|
||||
|
||||
Great! 20 lines of complex code to safe a few keystrokes! Obviously, this is
|
||||
exactly you should not do! (But it makes a cool example.)
|
||||
|
||||
|
||||
Statement Macros
|
||||
----------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue