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

@ -182,6 +182,7 @@
'nkModule', # the syntax tree of a module 'nkModule', # the syntax tree of a module
'nkProcDef', # a proc 'nkProcDef', # a proc
'nkMethodDef', # a method
'nkConverterDef', # a converter 'nkConverterDef', # a converter
'nkMacroDef', # a macro 'nkMacroDef', # a macro
'nkTemplateDef', # a template 'nkTemplateDef', # a template
@ -258,6 +259,7 @@
'skConst', # a constant 'skConst', # a constant
'skVar', # a variable 'skVar', # a variable
'skProc', # a proc 'skProc', # a proc
'skMethod', # a method
'skIterator', # an iterator 'skIterator', # an iterator
'skConverter', # a type converter 'skConverter', # a type converter
'skMacro', # a macro 'skMacro', # a macro

View file

@ -165,7 +165,6 @@
'AppendStrCh', 'AppendStrCh',
'AppendStrStr', 'AppendStrStr',
'AppendSeqElem', 'AppendSeqElem',
'AppendSeqSeq',
'InRange', 'InRange',
'InSet', 'InSet',
'Repr', 'Repr',

View file

@ -8,6 +8,8 @@
.. contents:: .. contents::
Abstraction is layering ignorance on top of reality. -- unknown
Directory structure Directory structure
=================== ===================
@ -72,18 +74,6 @@ the same::
./boot [-d:release] ./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 Pascal annotations
================== ==================
There are some annotations that the Pascal sources use so that they can 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 :Author: Andreas Rumpf
:Version: |nimrodversion| :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 Though the Nimrod Standard Library is still evolving, it is already quite
usable. It is divided into *pure libraries*, *impure libraries* and *wrappers*. 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 while impure libraries do. A wrapper is an impure library that is a very
low-level interface to a C library. low-level interface to a C library.
Read this `document <apis.html>`_ for a quick overview of the API design.
Pure libraries Pure libraries
============== ==============
Core
----
* `system <system.html>`_ * `system <system.html>`_
Basic procs and operators that every program needs. It also provides IO Basic procs and operators that every program needs. It also provides IO
facilities for reading and writing text and binary files. It is imported facilities for reading and writing text and binary files. It is imported
@ -25,11 +34,35 @@ Pure libraries
* `macros <macros.html>`_ * `macros <macros.html>`_
Contains the AST API and documentation of Nimrod for writing macros. Contains the AST API and documentation of Nimrod for writing macros.
String handling
---------------
* `strutils <strutils.html>`_ * `strutils <strutils.html>`_
This module contains common string handling operations like converting a This module contains common string handling operations like converting a
string into uppercase, splitting a string into substrings, searching for string into uppercase, splitting a string into substrings, searching for
substrings, replacing substrings. 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>`_ * `os <os.html>`_
Basic operating system facilities like retrieving environment variables, Basic operating system facilities like retrieving environment variables,
reading command line arguments, working with directories, running shell reading command line arguments, working with directories, running shell
@ -37,7 +70,28 @@ Pure libraries
platform independant. platform independant.
* `osproc <osproc.html>`_ * `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>`_ * `math <math.html>`_
Mathematical operations like cosine, square root. Mathematical operations like cosine, square root.
@ -45,11 +99,17 @@ Pure libraries
* `complex <complex.html>`_ * `complex <complex.html>`_
This module implements complex numbers and their mathematical operations. 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>`_ * `parseopt <parseopt.html>`_
The ``parseopt`` module implements a command line option parser. This The ``parseopt`` module implements a command line option parser. This
@ -71,48 +131,32 @@ Pure libraries
* `parsecsv <parsecsv.html>`_ * `parsecsv <parsecsv.html>`_
The ``parsecsv`` module implements a simple high performance CSV parser. 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>`_ * `lexbase <lexbase.html>`_
The ``strtabs`` module implements an efficient hash table that is a mapping This is a low level module that implements an extremely efficient buffering
from strings to strings. Supports a case-sensitive, case-insensitive and scheme for lexers and parsers. This is used by the diverse parsing modules.
style-insensitive mode. An efficient string substitution operator ``%``
for the string table is also provided.
* `streams <streams.html>`_
This module provides a stream interface and two implementations thereof: Code generation
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. * `xmlgen <xmlgen.html>`_
This module implements macros for XML/HTML code generation.
Cryptography and Hashing
------------------------
* `hashes <hashes.html>`_ * `hashes <hashes.html>`_
This module implements efficient computations of hash values for diverse This module implements efficient computations of hash values for diverse
Nimrod types. 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>`_ * `md5 <md5.html>`_
This module implements the MD5 checksum algorithm. This module implements the MD5 checksum algorithm.
* `xmlgen <xmlgen.html>`_
This module implements macros for HTML code generation.
Impure libraries 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``, 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. 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 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`` To suppress the static error in the ordinal case an ``else`` part with a ``nil``
statement can be used. statement can be used.
@ -1604,7 +1604,9 @@ statement is syntactic sugar for a nested block:
continue continue
stmt2 stmt2
# is equivalent to: Is equivalent to:
.. code-block:: nimrod
while expr1: while expr1:
block myBlockName: block myBlockName:
stmt1 stmt1
@ -1669,7 +1671,7 @@ object on the stack and can thus reference a non-existing object.
Procedures 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 called `procedures`:idx: in Nimrod (which is the correct terminology). A
procedure declaration defines an identifier and associates it with a block procedure declaration defines an identifier and associates it with a block
of code. A procedure may call itself recursively. The syntax is:: 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. 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 The generated files that Nimrod produces all go into a subdirectory called
``nimcache`` in your project directory. This makes it easy to delete all ``nimcache`` in your project directory. This makes it easy to delete all
generated files. generated files.
@ -116,7 +116,7 @@ No_decl Pragma
The `no_decl`:idx: pragma can be applied to almost any symbol (variable, proc, The `no_decl`:idx: pragma can be applied to almost any symbol (variable, proc,
type, etc.) and is sometimes useful for interoperability with C: type, etc.) and is sometimes useful for interoperability with C:
It tells Nimrod that it should not generate a declaration for the symbol in 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 .. code-block:: Nimrod
var var
@ -129,12 +129,12 @@ However, the ``header`` pragma is often the better alternative.
Header Pragma Header Pragma
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
The `header`:idx: pragma is very similar to the ``no_decl`` pragma: It can be 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 applied to almost any symbol and specifies that it should not be declared
declared but also that it leads to the inclusion of a given header file: and instead the generated code should contain an ``#include``:
.. code-block:: Nimrod .. code-block:: Nimrod
type 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 # 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 The ``header`` pragma expects always a string constant. The string contant
@ -159,7 +159,7 @@ strings automatically:
Line_dir Option Line_dir Option
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
The `line_dir`:idx: option can be turned on or off. If on the generated C code 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 Stack_trace Option
@ -198,7 +198,7 @@ Register Pragma
The `register`:idx: pragma is for variables only. It declares the variable as 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 ``register``, giving the compiler a hint that the variable should be placed
in a hardware register for faster access. C compilers usually ignore this 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 In highly specific cases (a dispatch loop of an bytecode interpreter for
example) it may provide benefits, though. example) it may provide benefits, though.
@ -228,7 +228,7 @@ memory, but nothing worse happens.
Dead_code_elim Pragma Dead_code_elim Pragma
~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~
The `dead_code_elim`:idx: pragma only applies to whole modules: It tells the 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. pragma appers in.
The ``--dead_code_elim:on`` command line switch has the same effect as marking 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 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 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 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 passed to subroutines. The compiler does not copy strings that are result from
a routine, because a routine returns a new string anyway. Thus it is efficient a procedure call, because the called procedure returns a new string anyway.
to do: Thus it is efficient to do:
.. code-block:: Nimrod .. code-block:: Nimrod
var s = procA() # assignment will not copy the string; procA allocates a new 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 :Author: Andreas Rumpf
:Version: |nimrodversion| :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 The "hallo world" program contains several identifiers that are already
known to the compiler: ``echo``, ``readLine``, etc. These built-in items are 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 Lexical elements
@ -173,7 +174,7 @@ to a storage location:
var x = "abc" # introduces a new variable `x` and assigns a value to it var x = "abc" # introduces a new variable `x` and assigns a value to it
x = "xyz" # assigns a new value to `x` 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 be overloaded, overwritten or forbidden, but this might change in a future
version of Nimrod. version of Nimrod.
@ -196,7 +197,7 @@ constants:
x = 1 x = 1
# a comment can occur here too # a comment can occur here too
y = 2 y = 2
z = y + 5 # simple computations are possible z = y + 5 # computations are possible
Control flow statements 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 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 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 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 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 it is of great help that the compiler checks that you covered any possible
value. value.
While statement While statement
--------------- ---------------
@ -327,7 +327,7 @@ the same:
Echo($i) Echo($i)
inc(i) # increment i by 1 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 .. code-block:: nimrod
Echo("Counting down from 10 to 1: ") 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 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, 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 .. code-block:: nimrod
block myblock: block myblock:
@ -392,7 +392,7 @@ unless the label of a block is given:
Continue statement 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: the next iteration immediately:
.. code-block:: nimrod .. 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 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 ``bool``. ``Bool`` is a built-in type: The only valid values for ``bool`` are
``true`` and ``false``. ``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*, Some terminology: In the example ``question`` is called a (formal) *parameter*,
``"Should I..."`` is called an *argument* that is passed to this 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. from the defaults.
Note that type inference works for parameters with default values, there is 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 Overloaded procedures
@ -857,7 +857,6 @@ loses information, the `EOutOfRange`:idx: exception is raised (if the error
cannot be detected at compile time). cannot be detected at compile time).
Floats Floats
------ ------
Nimrod has these floating point types built-in: ``float float32 float64``. 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 **Note**: The example only works because the memory is initialized with zero
(``alloc0`` instead of ``alloc`` does this): ``d.s`` is thus initialized to (``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 ``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 Procedural type
@ -1240,8 +1239,7 @@ Procedural type
A `procedural type`:idx: is a (somewhat abstract) pointer to a procedure. A `procedural type`:idx: is a (somewhat abstract) pointer to a procedure.
``nil`` is an allowed value for a variable of a procedural type. ``nil`` is an allowed value for a variable of a procedural type.
Nimrod uses procedural types to achieve `functional`:idx: programming Nimrod uses procedural types to achieve `functional`:idx: programming
techniques. Dynamic dispatch for OOP constructs can also be implemented with techniques.
procedural types (details follow in the OOP section).
Example: Example:
@ -1303,9 +1301,9 @@ because then one module cannot be reused without the other.
The algorithm for compiling modules is: The algorithm for compiling modules is:
- Compile the whole module as usual, following import statements recursively - Compile the whole module as usual, following import statements recursively.
- if there is a cycle only import the already parsed symbols (that are - If there is a cycle only import the already parsed symbols (that are
exported); if an unknown identifier occurs then abort exported); if an unknown identifier occurs then abort.
This is best illustrated by an example: 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 :Author: Andreas Rumpf
:Version: |nimrodversion| :Version: |nimrodversion|
@ -73,9 +73,9 @@ section.
Inheritance is done with the ``object of`` syntax. Multiple inheritance is Inheritance is done with the ``object of`` syntax. Multiple inheritance is
currently not supported. If an object type has no suitable ancestor, ``TObject`` 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 (*is-a* relation) for simple code reuse. Since objects are value types in
Nimrod, aggregation is as efficient as inheritance. 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 Objects, tuples and references can model quite complex data structures which
depend on each other. This is called *mutually recursive types*. In Nimrod depend on each other; they are *mutually recursive*. In Nimrod
these types need to be declared within a single type section. Anything else these types can only be declared within a single type section. (Anything else
would require arbitrary symbol lookahead which slows down compilation. would require arbitrary symbol lookahead which slows down compilation.)
Example: Example:
@ -147,7 +147,7 @@ An example:
TNode = object TNode = object
case kind: TNodeKind # the ``kind`` field is the discriminator case kind: TNodeKind # the ``kind`` field is the discriminator
of nkInt: intVal: int of nkInt: intVal: int
of nkFloat: floavVal: float of nkFloat: floatVal: float
of nkString: strVal: string of nkString: strVal: string
of nkAdd, nkSub: of nkAdd, nkSub:
leftOp, rightOp: PNode 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 * Adding a method to a class the programmer has no control over is
impossible or needs ugly workarounds. 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 ``join`` a string method or an array method? Should the complex
``vertexCover`` algorithm really be a method of the ``graph`` class? ``vertexCover`` algorithm really be a method of the ``graph`` class?
Nimrod avoids these problems by not distinguishing between methods and Nimrod avoids these problems by not assigning methods to a class. All methods
procedures. Methods are just ordinary procedures. However, there is a special in Nimrod are `multi-methods`:idx:. As we will see later, multi-methods are
syntactic sugar for calling procedures: The syntax ``obj.method(args)`` can be distinguished from procs only for dynamic binding purposes.
used instead of ``method(obj, args)``. If there are no remaining arguments, the
parentheses can be omitted: ``obj.len`` (instead of ``len(obj)``).
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 This `method call syntax`:idx: is not restricted to objects, it can be used
for any type: for any type:
.. code-block:: nimrod .. code-block:: nimrod
@ -196,9 +203,17 @@ for any type:
echo({'a', 'b', 'c'}.card) echo({'a', 'b', 'c'}.card)
stdout.writeln("Hallo") # the same as write(stdout, "Hallo") stdout.writeln("Hallo") # the same as write(stdout, "Hallo")
If it gives you warm fuzzy feelings, you can even write ``1.`+`(2)`` instead of (Another way to look at the method call syntax is that it provides the missing
``1 + 2`` and claim that Nimrod is a pure object oriented language. (But postfix notation.)
that's not true. :-)
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 Properties
@ -261,60 +276,75 @@ already provides ``v[]`` access.
Dynamic dispatch Dynamic dispatch
---------------- ----------------
In Nimrod procedural types are used to implement dynamic dispatch. The
following example also shows some more conventions: The ``self`` or ``this`` Procedures always use static dispatch. To get dynamic dispatch, replace the
object is named ``my`` (because it is shorter than the alternatives), each ``proc`` keyword by ``method``:
class provides a constructor, etc.
.. code-block:: nimrod .. code-block:: nimrod
type type
TFigure = object of TObject # abstract base class: TExpr = object ## abstract base class for an expression
fDraw: proc (my: var TFigure) # concrete classes implement this proc 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) = proc newPlus(a, b: ref TExpr): ref TPlusExpr =
f.fDraw = nil new(result)
result.a = a
result.b = b
proc draw(f: var TFigure) = echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
# ``draw`` dispatches dynamically:
f.fDraw(f)
type Note that in the example the constructors ``newLit`` and ``newPlus`` are procs
TCircle = object of TFigure because they should use static binding, but ``eval`` is a method because it
radius: int requires dynamic binding.
proc drawCircle(my: var TCircle) = echo("o " & $my.radius) In a multi-method all parameters that have an object type are used for the
dispatching:
proc init(my: var TCircle) =
init(TFigure(my)) # call base constructor .. code-block:: nimrod
my.radius = 5
my.fdraw = drawCircle
type type
TRectangle = object of TFigure TThing = object
width, height: int 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 var
r: TRectangle a, b: TUnit
c: TCircle collide(a, b) # output: 2
init(r)
init(c)
r.draw()
c.draw()
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 As the example demonstrates, invokation of a multi-method cannot be ambiguous:
this solution has its advantages compared to traditional OOP-languages, it is Collide 2 is prefered over collide 1 because the resolution works from left to
a **preliminary** solution. Multimethods are a planned language feature that right. Thus ``TUnit, TThing`` is prefered over ``TThing, TUnit``.
provide a more flexible and efficient mechanism.
**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 Exceptions
@ -497,8 +527,7 @@ simple proc for logging:
debug = True debug = True
proc log(msg: string) {.inline.} = proc log(msg: string) {.inline.} =
if debug: if debug: stdout.writeln(msg)
stdout.writeln(msg)
var var
x = 4 x = 4
@ -506,7 +535,7 @@ simple proc for logging:
This code has a shortcoming: If ``debug`` is set to false someday, the quite This code has a shortcoming: If ``debug`` is set to false someday, the quite
expensive ``$`` and ``&`` operations are still performed! (The argument 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: 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 const
debug = True debug = True
template log(msg: expr): stmt = template log(msg: string) =
if debug: if debug: stdout.writeln(msg)
stdout.writeln(msg)
var var
x = 4 x = 4
log("x has the value: " & $x) log("x has the value: " & $x)
The "types" of templates can be the symbols ``expr`` (stands for *expression*), The parameters' types can be ordinary types or the meta types ``expr``
``stmt`` (stands for *statement*) or ``typedesc`` (stands for *type (stands for *expression*), ``stmt`` (stands for *statement*) or ``typedesc``
description*). These are no real types, they just help the compiler parsing. (stands for *type description*). If the template has no explicit return type,
However, real types are supported too. ``stmt`` is used for consistency with procs and methods.
The template body does not open a new scope. To open a new scope The template body does not open a new scope. To open a new scope use a ``block``
use a ``block`` statement: statement:
.. code-block:: nimrod .. code-block:: nimrod
template declareInScope(x: expr, t: typeDesc): stmt = 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 parameter. The ``withFile`` template contains boilerplate code and helps to
avoid a common bug: To forget to close the file. Note how the avoid a common bug: To forget to close the file. Note how the
``var fn = filename`` statement ensures that ``filename`` is evaluated only ``var fn = filename`` statement ensures that ``filename`` is evaluated only
once. once.
Macros Macros

View file

@ -51,20 +51,21 @@ type
nnkObjDownConv, nnkObjUpConv, nnkChckRangeF, nnkChckRange64, nnkObjDownConv, nnkObjUpConv, nnkChckRangeF, nnkChckRange64,
nnkChckRange, nnkStringToCString, nnkCStringToString, nnkPassAsOpenArray, nnkChckRange, nnkStringToCString, nnkCStringToString, nnkPassAsOpenArray,
nnkAsgn, nnkFastAsgn, nnkGenericParams, nnkFormalParams, nnkAsgn, nnkFastAsgn, nnkGenericParams, nnkFormalParams,
nnkOfInherit, nnkModule, nnkProcDef, nnkConverterDef, nnkOfInherit, nnkModule, nnkProcDef, nnkMethodDef,
nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch, nnkConverterDef, nnkMacroDef, nnkTemplateDef, nnkIteratorDef,
nnkElifBranch, nnkExceptBranch, nnkElse, nnkMacroStmt, nnkOfBranch, nnkElifBranch, nnkExceptBranch, nnkElse,
nnkAsmStmt, nnkPragma, nnkIfStmt, nnkWhenStmt, nnkMacroStmt, nnkAsmStmt, nnkPragma, nnkIfStmt,
nnkForStmt, nnkWhileStmt, nnkCaseStmt, nnkVarSection, nnkWhenStmt, nnkForStmt, nnkWhileStmt, nnkCaseStmt,
nnkConstSection, nnkConstDef, nnkTypeSection, nnkTypeDef, nnkVarSection, nnkConstSection, nnkConstDef, nnkTypeSection,
nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt, nnkTypeDef, nnkYieldStmt, nnkTryStmt, nnkFinally,
nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkRaiseStmt, nnkReturnStmt, nnkBreakStmt, nnkContinueStmt,
nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt, nnkBlockStmt, nnkDiscardStmt, nnkStmtList, nnkImportStmt,
nnkIncludeStmt, nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr, nnkFromStmt, nnkIncludeStmt, nnkCommentStmt, nnkStmtListExpr,
nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy, nnkBlockExpr, nnkStmtListType, nnkBlockType, nnkTypeOfExpr,
nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen, nnkObjectTy, nnkTupleTy, nnkRecList, nnkRecCase,
nnkRefTy, nnkPtrTy, nnkVarTy, nnkDistinctTy, nnkRecWhen, nnkRefTy, nnkPtrTy, nnkVarTy,
nnkProcTy, nnkEnumTy, nnkEnumFieldDef, nnkReturnToken nnkDistinctTy, nnkProcTy, nnkEnumTy, nnkEnumFieldDef,
nnkReturnToken
TNimNodeKinds* = set[TNimrodNodeKind] TNimNodeKinds* = set[TNimrodNodeKind]
TNimrodTypeKind* = enum TNimrodTypeKind* = enum
ntyNone, ntyBool, ntyChar, ntyEmpty, ntyNone, ntyBool, ntyChar, ntyEmpty,
@ -81,9 +82,10 @@ type
TNimrodSymKind* = enum TNimrodSymKind* = enum
nskUnknown, nskConditional, nskDynLib, nskParam, nskUnknown, nskConditional, nskDynLib, nskParam,
nskGenericParam, nskTemp, nskType, nskConst, nskGenericParam, nskTemp, nskType, nskConst,
nskVar, nskProc, nskIterator, nskConverter, nskVar, nskProc, nskMethod, nskIterator,
nskMacro, nskTemplate, nskField, nskEnumField, nskConverter, nskMacro, nskTemplate, nskField,
nskForVar, nskModule, nskLabel, nskStub nskEnumField, nskForVar, nskModule, nskLabel,
nskStub
TNimSymKinds* = set[TNimrodSymKind] TNimSymKinds* = set[TNimrodSymKind]
#[[[end]]] #[[[end]]]

View file

@ -363,7 +363,24 @@ proc `/` * (head, tail: string): string {.noSideEffect.} =
## The same as ``joinPath(head, tail)`` ## The same as ``joinPath(head, tail)``
return joinPath(head, tail) return joinPath(head, tail)
proc SplitPath*(path: string, head, tail: var string) {.noSideEffect.} = proc SplitPath*(path: string, head, tail: var string) {.noSideEffect,
deprecated.} =
## **Deprecated since version 0.8.2**: use the version that returns a tuple
## instead
var
sepPos = -1
for i in countdown(len(path)-1, 0):
if path[i] in {dirsep, altsep}:
sepPos = i
break
if sepPos >= 0:
head = copy(path, 0, sepPos-1)
tail = copy(path, sepPos+1)
else:
head = ""
tail = path # make a string copy here
proc SplitPath*(path: string): tuple[head, tail: string] {.noSideEffect.} =
## Splits a directory into (head, tail), so that ## Splits a directory into (head, tail), so that
## ``JoinPath(head, tail) == path``. ## ``JoinPath(head, tail) == path``.
## ##
@ -378,11 +395,11 @@ proc SplitPath*(path: string, head, tail: var string) {.noSideEffect.} =
sepPos = i sepPos = i
break break
if sepPos >= 0: if sepPos >= 0:
head = copy(path, 0, sepPos-1) result.head = copy(path, 0, sepPos-1)
tail = copy(path, sepPos+1) result.tail = copy(path, sepPos+1)
else: else:
head = "" result.head = ""
tail = path # make a string copy here result.tail = path
proc parentDir*(path: string): string {.noSideEffect.} = proc parentDir*(path: string): string {.noSideEffect.} =
## Returns the parent directory of `path`. ## Returns the parent directory of `path`.
@ -422,25 +439,50 @@ proc searchExtPos(s: string): int =
elif s[i] in {dirsep, altsep}: elif s[i] in {dirsep, altsep}:
break # do not skip over path break # do not skip over path
proc extractDir*(path: string): string {.noSideEffect.} = proc splitFile*(path: string): tuple[dir, name, ext: string] {.noSideEffect.} =
## Splits a filename into (dir, filename, extension).
## `dir` does not end in `DirSep`.
## `extension` includes the leading dot.
##
## Example:
##
## .. code-block:: nimrod
## var (dir, name, ext) = splitFile("usr/local/nimrodc.html")
## assert dir == "usr/local"
## assert name == "nimrodc"
## assert ext == ".html"
## If `path` has no extension, `ext` is the empty string.
## If `path` has no directory component, `dir` is the empty string.
## If `path` has no filename component, `name` and `ext` are empty strings.
if path.len == 0 or path[path.len-1] in {dirSep, altSep}:
result = (path, "", "")
else:
var sepPos = -1
var dotPos = path.len
for i in countdown(len(path)-1, 0):
if path[i] == ExtSep:
if dotPos == path.len: dotPos = i
elif path[i] in {dirsep, altsep}:
sepPos = i
break
result.dir = copy(path, 0, sepPos-1)
result.name = copy(path, sepPos+1, dotPos-1)
result.ext = copy(path, dotPos)
proc extractDir*(path: string): string {.noSideEffect, deprecated.} =
## Extracts the directory of a given path. This is almost the ## Extracts the directory of a given path. This is almost the
## same as the `head` result of `splitPath`, except that ## same as the `head` result of `splitPath`, except that
## ``extractDir("/usr/lib/") == "/usr/lib/"``. ## ``extractDir("/usr/lib/") == "/usr/lib/"``.
if path.len == 0 or path[path.len-1] in {dirSep, altSep}: ## **Deprecated since version 0.8.2**: Use ``splitFile(path).dir`` instead.
result = path result = splitFile(path).dir
else:
var tail: string
splitPath(path, result, tail)
proc extractFilename*(path: string): string {.noSideEffect.} = proc extractFilename*(path: string): string {.noSideEffect.} =
## Extracts the filename of a given `path`. This is almost the ## Extracts the filename of a given `path`. This is the same as
## same as the `tail` result of `splitPath`, except that ## ``name & ext`` from ``splitFile(path)``.
## ``extractFilename("/usr/lib/") == ""``.
if path.len == 0 or path[path.len-1] in {dirSep, altSep}: if path.len == 0 or path[path.len-1] in {dirSep, altSep}:
result = "" result = ""
else: else:
var head: string result = splitPath(path).tail
splitPath(path, head, result)
proc expandFilename*(filename: string): string = proc expandFilename*(filename: string): string =
## Returns the full path of `filename`, raises EOS in case of an error. ## Returns the full path of `filename`, raises EOS in case of an error.
@ -457,13 +499,14 @@ proc expandFilename*(filename: string): string =
c_free(res) c_free(res)
proc SplitFilename*(filename: string, name, extension: var string) {. proc SplitFilename*(filename: string, name, extension: var string) {.
noSideEffect.} = noSideEffect, deprecated.} =
## Splits a filename into (name, extension), so that ## Splits a filename into (name, extension), so that
## ``name & extension == filename``. ## ``name & extension == filename``.
## ##
## Example: After ``SplitFilename("usr/local/nimrodc.html", name, ext)``, ## Example: After ``SplitFilename("usr/local/nimrodc.html", name, ext)``,
## `name` is "usr/local/nimrodc" and `ext` is ".html". ## `name` is "usr/local/nimrodc" and `ext` is ".html".
## It the file has no extension, extention is the empty string. ## If the file has no extension, extension is the empty string.
## **Deprecated since version 0.8.2**: Use ``splitFile(filename)`` instead.
var extPos = searchExtPos(filename) var extPos = searchExtPos(filename)
if extPos >= 0: if extPos >= 0:
name = copy(filename, 0, extPos-1) name = copy(filename, 0, extPos-1)
@ -472,18 +515,19 @@ proc SplitFilename*(filename: string, name, extension: var string) {.
name = filename # make a string copy here name = filename # make a string copy here
extension = "" extension = ""
proc extractFileExt*(filename: string): string {.noSideEffect.} = proc extractFileExt*(filename: string): string {.noSideEffect, deprecated.} =
## Extracts the file extension of a given `filename`. This is the ## Extracts the file extension of a given `filename`. This is the
## same as the `extension` result of `splitFilename`. ## same as the `extension` result of `splitFilename`.
var dummy: string ## **Deprecated since version 0.8.2**: Use ``splitFile(filename).ext``
splitFilename(filename, dummy, result) ## instead.
result = splitFile(filename).ext
proc extractFileTrunk*(filename: string): string {.noSideEffect.} = proc extractFileTrunk*(filename: string): string {.noSideEffect, deprecated.} =
## Extracts the file name of a given `filename`. This removes any ## Extracts the file name of a given `filename`. This removes any
## directory information and the file extension. ## directory information and the file extension.
var dummy: string ## **Deprecated since version 0.8.2**: Use ``splitFile(path).name`` instead.
splitFilename(extractFilename(filename), result, dummy) result = splitFile(filename).name
proc ChangeFileExt*(filename, ext: string): string {.noSideEffect.} = proc ChangeFileExt*(filename, ext: string): string {.noSideEffect.} =
## Changes the file extension to `ext`. ## Changes the file extension to `ext`.
## ##
@ -496,8 +540,8 @@ proc ChangeFileExt*(filename, ext: string): string {.noSideEffect.} =
if extPos < 0: result = filename & normExt(ext) if extPos < 0: result = filename & normExt(ext)
else: result = copy(filename, 0, extPos-1) & normExt(ext) else: result = copy(filename, 0, extPos-1) & normExt(ext)
proc AppendFileExt*(filename, ext: string): string {.noSideEffect.} = proc addFileExt*(filename, ext: string): string {.noSideEffect.} =
## Appends the file extension `ext` to `filename`, unless ## Adds the file extension `ext` to `filename`, unless
## `filename` already has an extension. ## `filename` already has an extension.
## ##
## `Ext` should be given without the leading '.', because some ## `Ext` should be given without the leading '.', because some
@ -505,7 +549,12 @@ proc AppendFileExt*(filename, ext: string): string {.noSideEffect.} =
## (Although I know of none such beast.) ## (Although I know of none such beast.)
var extPos = searchExtPos(filename) var extPos = searchExtPos(filename)
if extPos < 0: result = filename & normExt(ext) if extPos < 0: result = filename & normExt(ext)
else: result = filename #make a string copy here else: result = filename
proc AppendFileExt*(filename, ext: string): string {.
noSideEffect, deprecated.} =
## **Deprecated since version 0.8.2**: Use `addFileExt` instead.
result = addFileExt(filename, ext)
proc cmpPaths*(pathA, pathB: string): int {.noSideEffect.} = proc cmpPaths*(pathA, pathB: string): int {.noSideEffect.} =
## Compares two paths. ## Compares two paths.
@ -610,14 +659,18 @@ proc removeFile*(file: string) =
## Removes the `file`. If this fails, `EOS` is raised. ## Removes the `file`. If this fails, `EOS` is raised.
if cremove(file) != 0'i32: OSError() if cremove(file) != 0'i32: OSError()
proc executeShellCommand*(command: string): int = proc executeShellCommand*(command: string): int {.deprecated.} =
## **Deprecated since version 0.8.2**: Use `execShellCmd` instead.
result = csystem(command)
proc execShellCmd*(command: string): int =
## Executes a shell command. ## Executes a shell command.
## ##
## Command has the form 'program args' where args are the command ## Command has the form 'program args' where args are the command
## line arguments given to program. The proc returns the error code ## line arguments given to program. The proc returns the error code
## of the shell when it has finished. The proc does not return until ## of the shell when it has finished. The proc does not return until
## the process has finished. To execute a program without having a ## the process has finished. To execute a program without having a
## shell involved, use the `executeProcess` proc of the `osproc` ## shell involved, use the `execProcess` proc of the `osproc`
## module. ## module.
result = csystem(command) result = csystem(command)
@ -707,7 +760,16 @@ proc putEnv*(key, val: string) =
if SetEnvironmentVariableA(key, val) == 0'i32: if SetEnvironmentVariableA(key, val) == 0'i32:
OSError() OSError()
iterator iterOverEnvironment*(): tuple[key, value: string] = iterator iterOverEnvironment*(): tuple[key, value: string] {.deprecated.} =
## Iterate over all environments variables. In the first component of the
## tuple is the name of the current variable stored, in the second its value.
## **Deprecated since version 0.8.2**: Use `envPairs` instead.
getEnvVarsC()
for i in 0..high(environment):
var p = find(environment[i], '=')
yield (copy(environment[i], 0, p-1), copy(environment[i], p+1))
iterator envPairs*(): tuple[key, value: string] =
## Iterate over all environments variables. In the first component of the ## Iterate over all environments variables. In the first component of the
## tuple is the name of the current variable stored, in the second its value. ## tuple is the name of the current variable stored, in the second its value.
getEnvVarsC() getEnvVarsC()
@ -1039,7 +1101,6 @@ proc getApplicationFilename*(): string =
proc getApplicationDir*(): string = proc getApplicationDir*(): string =
## Returns the directory of the application's executable. ## Returns the directory of the application's executable.
var tail: string result = splitFile(getApplicationFilename()).dir
splitPath(getApplicationFilename(), result, tail)
{.pop.} {.pop.}

View file

@ -36,16 +36,28 @@ type
## often creates a security whole! ## often creates a security whole!
poStdErrToStdOut ## merge stdout and stderr to the stdout stream poStdErrToStdOut ## merge stdout and stderr to the stdout stream
proc executeProcess*(command: string, proc execProcess*(command: string,
options: set[TProcessOption] = {poStdErrToStdOut, options: set[TProcessOption] = {poStdErrToStdOut,
poUseShell}): string poUseShell}): string
## A convience procedure that executes ``command`` with ``startProcess`` ## A convience procedure that executes ``command`` with ``startProcess``
## and returns its output as a string. ## and returns its output as a string.
proc executeCommand*(command: string): int proc executeProcess*(command: string,
options: set[TProcessOption] = {poStdErrToStdOut,
poUseShell}): string {.
deprecated.} =
## **Deprecated since version 0.8.2**: Use `execProcess` instead.
result = execProcess(command, options)
proc execCmd*(command: string): int
## Executes ``command`` and returns its error code. Standard input, output, ## Executes ``command`` and returns its error code. Standard input, output,
## error streams are inherited from the calling process. ## error streams are inherited from the calling process.
proc executeCommand*(command: string): int {.deprecated.} =
## **Deprecated since version 0.8.2**: Use `execCmd` instead.
result = execCmd(command)
proc startProcess*(command: string, proc startProcess*(command: string,
workingDir: string = "", workingDir: string = "",
args: openarray[string] = [], args: openarray[string] = [],
@ -56,11 +68,12 @@ proc startProcess*(command: string,
## is used. `args` are the command line arguments that are passed to the ## is used. `args` are the command line arguments that are passed to the
## process. On many operating systems, the first command line argument is the ## process. On many operating systems, the first command line argument is the
## name of the executable. `args` should not contain this argument! ## name of the executable. `args` should not contain this argument!
## `startProcess` takes care of that. `env` is the environment that will be ## `env` is the environment that will be passed to the process.
## passed to the process. If ``env == nil`` the environment is inherited of ## If ``env == nil`` the environment is inherited of
## the parent process. `options` are additional flags that may be passed ## the parent process. `options` are additional flags that may be passed
## to `startProcess`. See the documentation of ``TProcessOption`` for the ## to `startProcess`. See the documentation of ``TProcessOption`` for the
## meaning of these flags. ## meaning of these flags.
##
## Return value: The newly created process object. Nil is never returned, ## Return value: The newly created process object. Nil is never returned,
## but ``EOS`` is raised in case of an error. ## but ``EOS`` is raised in case of an error.
@ -104,9 +117,9 @@ proc outputStream*(p: PProcess): PStream
proc errorStream*(p: PProcess): PStream proc errorStream*(p: PProcess): PStream
## returns ``p``'s output stream for reading from ## returns ``p``'s output stream for reading from
proc executeProcess*(command: string, proc execProcess(command: string,
options: set[TProcessOption] = {poStdErrToStdOut, options: set[TProcessOption] = {poStdErrToStdOut,
poUseShell}): string = poUseShell}): string =
var c = parseCmdLine(command) var c = parseCmdLine(command)
var a: seq[string] = @[] # slicing is not yet implemented :-( var a: seq[string] = @[] # slicing is not yet implemented :-(
for i in 1 .. c.len-1: add(a, c[i]) for i in 1 .. c.len-1: add(a, c[i])
@ -208,24 +221,24 @@ when defined(Windows):
SI.dwFlags = STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES SI.dwFlags = STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES
CreatePipeHandles(SI.hStdInput, HI) CreatePipeHandles(SI.hStdInput, HI)
CreatePipeHandles(HO, Si.hStdOutput) CreatePipeHandles(HO, Si.hStdOutput)
#SI.hStdInput = GetStdHandle(STD_INPUT_HANDLE())
#SI.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE())
if poStdErrToStdOut in options: if poStdErrToStdOut in options:
SI.hStdError = SI.hStdOutput SI.hStdError = SI.hStdOutput
HE = HO HE = HO
else: else:
CreatePipeHandles(HE, Si.hStdError) CreatePipeHandles(HE, Si.hStdError)
#SI.hStdError = GetStdHandle(STD_ERROR_HANDLE())
#result.inputHandle = open_osfhandle(HI, O_WRONLY)
#if result.inputHandle == -1'i32: OSError()
result.inputHandle = hi result.inputHandle = hi
result.outputHandle = ho result.outputHandle = ho
result.errorHandle = he result.errorHandle = he
#result.outputHandle = open_osfhandle(HO, O_RDONLY) var cmdl: cstring
#if result.outputHandle == -1'i32: OSError() if poUseShell in options:
#result.errorHandle = open_osfhandle(HE, O_RDONLY) var comspec = getEnv("COMSPEC")
#if result.errorHandle == -1'i32: OSError() var a: seq[string] = @[]
var cmdl = buildCommandLine(command, args) add(a, "/c")
add(a, command)
add(a, args)
cmdl = buildCommandLine(comspec, a)
else:
cmdl = buildCommandLine(command, args)
var wd: cstring = nil var wd: cstring = nil
if len(workingDir) > 0: wd = workingDir if len(workingDir) > 0: wd = workingDir
if env == nil: if env == nil:
@ -239,6 +252,9 @@ when defined(Windows):
dealloc(cmdl) dealloc(cmdl)
if success == 0: if success == 0:
OSError() OSError()
# NEW:
# Close the handles now so anyone waiting is woken.
discard closeHandle(procInfo.hThread)
result.FProcessHandle = procInfo.hProcess result.FProcessHandle = procInfo.hProcess
result.FThreadHandle = procInfo.hThread result.FThreadHandle = procInfo.hThread
result.id = procInfo.dwProcessID result.id = procInfo.dwProcessID
@ -258,7 +274,7 @@ when defined(Windows):
discard TerminateProcess(p.FProcessHandle, 0) discard TerminateProcess(p.FProcessHandle, 0)
proc waitForExit(p: PProcess): int = proc waitForExit(p: PProcess): int =
discard CloseHandle(p.FThreadHandle) #CloseHandle(p.FThreadHandle)
discard WaitForSingleObject(p.FProcessHandle, Infinite) discard WaitForSingleObject(p.FProcessHandle, Infinite)
var res: int32 var res: int32
discard GetExitCodeProcess(p.FProcessHandle, res) discard GetExitCodeProcess(p.FProcessHandle, res)
@ -274,7 +290,7 @@ when defined(Windows):
proc errorStream(p: PProcess): PStream = proc errorStream(p: PProcess): PStream =
result = newFileHandleStream(p.errorHandle) result = newFileHandleStream(p.errorHandle)
proc executeCommand(command: string): int = proc execCmd(command: string): int =
var var
SI: TStartupInfo SI: TStartupInfo
ProcInfo: TProcessInformation ProcInfo: TProcessInformation
@ -420,8 +436,8 @@ else:
proc csystem(cmd: cstring): cint {.nodecl, importc: "system".} proc csystem(cmd: cstring): cint {.nodecl, importc: "system".}
proc executeCommand(command: string): int = proc execCmd(command: string): int =
result = csystem(command) result = csystem(command)
when isMainModule: when isMainModule:
echo executeCommand("gcc -v") echo execCmd("gcc -v")

View file

@ -138,7 +138,7 @@ proc getEscapedChar(c: var TCfgParser, tok: var TToken) =
inc(c.bufpos) # skip '\' inc(c.bufpos) # skip '\'
case c.buf[c.bufpos] case c.buf[c.bufpos]
of 'n', 'N': of 'n', 'N':
add(tok.literal, nl) add(tok.literal, "\n")
Inc(c.bufpos) Inc(c.bufpos)
of 'r', 'R', 'c', 'C': of 'r', 'R', 'c', 'C':
add(tok.literal, '\c') add(tok.literal, '\c')
@ -208,7 +208,7 @@ proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
of '\c', '\L': of '\c', '\L':
pos = HandleCRLF(c, pos) pos = HandleCRLF(c, pos)
buf = c.buf buf = c.buf
add(tok.literal, nl) add(tok.literal, "\n")
of lexbase.EndOfFile: of lexbase.EndOfFile:
tok.kind = tkInvalid tok.kind = tkInvalid
break break
@ -302,7 +302,7 @@ proc rawGetTok(c: var TCfgParser, tok: var TToken) =
proc errorStr(c: TCfgParser, msg: string): string = proc errorStr(c: TCfgParser, msg: string): string =
result = `%`("$1($2, $3) Error: $4", result = `%`("$1($2, $3) Error: $4",
[c.filename, toString(getLine(c)), toString(getColumn(c)), msg]) [c.filename, $getLine(c), $getColumn(c), msg])
proc getKeyValPair(c: var TCfgParser, kind: TCfgEventKind): TCfgEvent = proc getKeyValPair(c: var TCfgParser, kind: TCfgEventKind): TCfgEvent =
if c.tok.kind == tkSymbol: if c.tok.kind == tkSymbol:

View file

@ -32,21 +32,10 @@ type
## or the argument, ``value`` is not "" if ## or the argument, ``value`` is not "" if
## the option was given a value ## the option was given a value
proc init*(cmdline: string = ""): TOptParser proc initOptParser*(cmdline = ""): TOptParser =
## inits the option parser. If ``cmdline == ""``, the real command line ## inits the option parser. If ``cmdline == ""``, the real command line
## (as provided by the ``OS`` module) is taken. ## (as provided by the ``OS`` module) is taken.
result.pos = 0
proc next*(p: var TOptParser)
## parses the first or next option; ``p.kind`` describes what token has been
## parsed. ``p.key`` and ``p.val`` are set accordingly.
proc getRestOfCommandLine*(p: TOptParser): string
## retrieves the rest of the command line that has not been parsed yet.
# implementation
proc init(cmdline: string = ""): TOptParser =
result.pos = strStart
result.inShortState = false result.inShortState = false
if cmdline != "": if cmdline != "":
result.cmd = cmdline result.cmd = cmdline
@ -58,6 +47,10 @@ proc init(cmdline: string = ""): TOptParser =
result.key = "" result.key = ""
result.val = "" result.val = ""
proc init*(cmdline: string = ""): TOptParser {.deprecated.} =
## **Deprecated since version 0.8.2**: Use `initOptParser` instead.
result = initOptParser(cmdline)
proc parseWord(s: string, i: int, w: var string, proc parseWord(s: string, i: int, w: var string,
delim: TCharSet = {'\x09', ' ', '\0'}): int = delim: TCharSet = {'\x09', ' ', '\0'}): int =
result = i result = i
@ -89,7 +82,9 @@ proc handleShortOption(p: var TOptParser) =
if p.cmd[i] == '\0': p.inShortState = false if p.cmd[i] == '\0': p.inShortState = false
p.pos = i p.pos = i
proc next(p: var TOptParser) = proc next*(p: var TOptParser) =
## parses the first or next option; ``p.kind`` describes what token has been
## parsed. ``p.key`` and ``p.val`` are set accordingly.
var i = p.pos var i = p.pos
while p.cmd[i] in {'\x09', ' '}: inc(i) while p.cmd[i] in {'\x09', ' '}: inc(i)
p.pos = i p.pos = i
@ -121,12 +116,17 @@ proc next(p: var TOptParser) =
p.kind = cmdArgument p.kind = cmdArgument
p.pos = parseWord(p.cmd, i, p.key) p.pos = parseWord(p.cmd, i, p.key)
proc getRestOfCommandLine(p: TOptParser): string = proc cmdLineRest*(p: TOptParser): string =
result = strip(copy(p.cmd, p.pos + strStart, len(p.cmd) - 1)) ## retrieves the rest of the command line that has not been parsed yet.
result = strip(copy(p.cmd, p.pos, len(p.cmd) - 1))
proc getRestOfCommandLine*(p: TOptParser): string {.deprecated.} =
## **Deprecated since version 0.8.2**: Use `cmdLineRest` instead.
result = cmdLineRest(p)
iterator getopt*(): tuple[kind: TCmdLineKind, key, val: string] = iterator getopt*(): tuple[kind: TCmdLineKind, key, val: string] =
##this is an convenience iterator for iterating over the command line. ## This is an convenience iterator for iterating over the command line.
##This uses the TOptParser object. Example: ## This uses the TOptParser object. Example:
## ##
## .. code-block:: nimrod ## .. code-block:: nimrod
## var ## var
@ -143,7 +143,7 @@ iterator getopt*(): tuple[kind: TCmdLineKind, key, val: string] =
## if filename == "": ## if filename == "":
## # no filename has been given, so we show the help: ## # no filename has been given, so we show the help:
## writeHelp() ## writeHelp()
var p = init() var p = initOptParser()
while true: while true:
next(p) next(p)
if p.kind == cmdEnd: break if p.kind == cmdEnd: break

View file

@ -483,7 +483,7 @@ proc errorStr(L: TSqlLexer, msg: string): string =
# OR left logical disjunction # OR left logical disjunction
type type
TSqlNodeKind* = enum TSqlNodeKind* = enum ## kind of SQL abstract syntax tree
nkNone, nkNone,
nkIdent, nkIdent,
nkStringLit, nkStringLit,
@ -536,17 +536,18 @@ type
nkEnumDef nkEnumDef
type type
EInvalidSql* = object of EBase EInvalidSql* = object of EBase ## Invalid SQL encountered
PSqlNode* = ref TSqlNode PSqlNode* = ref TSqlNode ## an SQL abstract syntax tree node
TSqlNode* = object TSqlNode* = object ## an SQL abstract syntax tree node
case kind*: TSqlNodeKind case kind*: TSqlNodeKind ## kind of syntax tree
of nkIdent, nkStringLit, nkBitStringLit, nkHexStringLit, of nkIdent, nkStringLit, nkBitStringLit, nkHexStringLit,
nkIntegerLit, nkNumericLit: nkIntegerLit, nkNumericLit:
strVal*: string strVal*: string ## AST leaf: the identifier, numeric literal
## string literal, etc.
else: else:
sons*: seq[PSqlNode] sons*: seq[PSqlNode] ## the node's children
TSqlParser = object of TSqlLexer TSqlParser* = object of TSqlLexer ## SQL parser object
tok: TToken tok: TToken
proc newNode(k: TSqlNodeKind): PSqlNode = proc newNode(k: TSqlNodeKind): PSqlNode =
@ -1070,7 +1071,17 @@ proc parseStmt(p: var TSqlParser): PSqlNode =
else: else:
sqlError(p, "CREATE expected") sqlError(p, "CREATE expected")
proc parse*(p: var TSqlParser): PSqlNode = proc open(p: var TSqlParser, input: PStream, filename: string) =
## opens the parser `p` and assigns the input stream `input` to it.
## `filename` is only used for error messages.
open(TSqlLexer(p), input, filename)
p.tok.kind = tkInvalid
p.tok.literal = ""
getTok(p)
proc parse(p: var TSqlParser): PSqlNode =
## parses the content of `p`'s input stream and returns the SQL AST.
## Syntax errors raise an `EInvalidSql` exception.
result = newNode(nkStmtList) result = newNode(nkStmtList)
while p.tok.kind != tkEof: while p.tok.kind != tkEof:
var s = parseStmt(p) var s = parseStmt(p)
@ -1078,21 +1089,21 @@ proc parse*(p: var TSqlParser): PSqlNode =
result.add(s) result.add(s)
if result.len == 1: if result.len == 1:
result = result.sons[0] result = result.sons[0]
proc open*(p: var TSqlParser, input: PStream, filename: string) =
open(TSqlLexer(p), input, filename)
p.tok.kind = tkInvalid
p.tok.literal = ""
getTok(p)
proc close*(p: var TSqlParser) = proc close(p: var TSqlParser) =
## closes the parser `p`. The associated input stream is closed too.
close(TSqlLexer(p)) close(TSqlLexer(p))
proc parseSQL*(input: PStream, filename: string): PSqlNode = proc parseSQL*(input: PStream, filename: string): PSqlNode =
## parses the SQL from `input` into an AST and returns the AST.
## `filename` is only used for error messages.
## Syntax errors raise an `EInvalidSql` exception.
var p: TSqlParser var p: TSqlParser
open(p, input, filename) open(p, input, filename)
result = parse(p) try:
close(p) result = parse(p)
finally:
close(p)
proc ra(n: PSqlNode, s: var string, indent: int) proc ra(n: PSqlNode, s: var string, indent: int)
@ -1114,7 +1125,7 @@ proc ra(n: PSqlNode, s: var string, indent: int) =
if allCharsInSet(n.strVal, {'\33'..'\127'}): if allCharsInSet(n.strVal, {'\33'..'\127'}):
s.add(n.strVal) s.add(n.strVal)
else: else:
s.add("\"" & replaceStr(n.strVal, "\"", "\"\"") & "\"") s.add("\"" & replace(n.strVal, "\"", "\"\"") & "\"")
of nkStringLit: of nkStringLit:
s.add(escape(n.strVal, "e'", "'")) s.add(escape(n.strVal, "e'", "'"))
of nkBitStringLit: of nkBitStringLit:

View file

@ -297,7 +297,7 @@ proc parseEntity(my: var TXmlParser, dest: var string) =
var buf = my.buf var buf = my.buf
my.kind = xmlCharData my.kind = xmlCharData
if buf[pos] == '#': if buf[pos] == '#':
var r: TRune var r: int
inc(pos) inc(pos)
if buf[pos] == 'x': if buf[pos] == 'x':
inc(pos) inc(pos)
@ -312,7 +312,7 @@ proc parseEntity(my: var TXmlParser, dest: var string) =
while buf[pos] in {'0'..'9'}: while buf[pos] in {'0'..'9'}:
r = r * 10 + (ord(buf[pos]) - ord('0')) r = r * 10 + (ord(buf[pos]) - ord('0'))
inc(pos) inc(pos)
add(dest, toUTF8(r)) add(dest, toUTF8(TRune(r)))
elif buf[pos] == 'l' and buf[pos+1] == 't': elif buf[pos] == 'l' and buf[pos+1] == 't':
add(dest, '<') add(dest, '<')
inc(pos, 2) inc(pos, 2)

View file

@ -8,6 +8,9 @@
# #
## Regular expression support for Nimrod. ## Regular expression support for Nimrod.
## **Deprecated** since version 0.8.2. Use the module `re` instead.
{.deprecated.}
## Currently this module is implemented by providing a wrapper around the ## Currently this module is implemented by providing a wrapper around the
## `PRCE (Perl-Compatible Regular Expressions) <http://www.pcre.org>`_ ## `PRCE (Perl-Compatible Regular Expressions) <http://www.pcre.org>`_
## C library. This means that your application will depend on the PRCE ## C library. This means that your application will depend on the PRCE
@ -131,13 +134,14 @@ template `=~` *(s, pattern: expr): expr =
## echo("comment: ", matches[1]) ## echo("comment: ", matches[1])
## else: ## else:
## echo("syntax error") ## echo("syntax error")
## ##
var matches: array[0..maxSubPatterns-1, string] when not definedInScope(matches):
var matches: array[0..maxSubPatterns-1, string]
match(s, pattern, matches) match(s, pattern, matches)
const ## common regular expressions const ## common regular expressions
reIdentifier* = r"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b" ## describes an identifier reIdentifier* = r"\b[a-zA-Z_][a-zA-Z_0-9]*\b" ## describes an identifier
reNatural* = r"\b\d+\b" ## describes a natural number reNatural* = r"\b\d+\b" ## describes a natural number
reInteger* = r"\b[-+]?\d+\b" ## describes an integer reInteger* = r"\b[-+]?\d+\b" ## describes an integer
reHex* = r"\b0[xX][0-9a-fA-F]+\b" ## describes a hexadecimal number reHex* = r"\b0[xX][0-9a-fA-F]+\b" ## describes a hexadecimal number

View file

@ -45,9 +45,9 @@ const
IdentStartChars* = {'a'..'z', 'A'..'Z', '_'} IdentStartChars* = {'a'..'z', 'A'..'Z', '_'}
## the set of characters an identifier can start with ## the set of characters an identifier can start with
strStart* = 0 ## this is only for bootstraping # strStart* = 0 ## this is only for bootstraping
## XXX: remove this someday # ## XXX: remove this someday
nl* = "\n" ## this is only for bootstraping XXX: remove this somehow # nl* = "\n" ## this is only for bootstraping XXX: remove this someday
proc `%` *(formatstr: string, a: openarray[string]): string {.noSideEffect.} proc `%` *(formatstr: string, a: openarray[string]): string {.noSideEffect.}
## The `substitution`:idx: operator performs string substitutions in ## The `substitution`:idx: operator performs string substitutions in
@ -272,43 +272,39 @@ iterator splitLines*(s: string): string =
else: break # was '\0' else: break # was '\0'
first = last first = last
template iterToProc(iter: expr): stmt =
result = @[]
for x in iter: add(result, x)
proc splitLinesSeq*(s: string): seq[string] {.noSideEffect, deprecated.} = proc splitLinesSeq*(s: string): seq[string] {.noSideEffect, deprecated.} =
## The same as `splitLines`, but is a proc that returns a sequence ## The same as `splitLines`, but is a proc that returns a sequence
## of substrings. ## of substrings.
## **Deprecated since version 0.8.0**: Use `splitLines` instead. ## **Deprecated since version 0.8.0**: Use `splitLines` instead.
iterToProc(splitLines(s)) accumulateResult(splitLines(s))
proc splitSeq*(s: string, seps: set[char] = Whitespace): seq[string] {. proc splitSeq*(s: string, seps: set[char] = Whitespace): seq[string] {.
noSideEffect, deprecated.} = noSideEffect, deprecated.} =
## The same as `split`, but is a proc that returns a sequence of substrings. ## The same as `split`, but is a proc that returns a sequence of substrings.
## **Deprecated since version 0.8.0**: Use `split` instead. ## **Deprecated since version 0.8.0**: Use `split` instead.
iterToProc(split(s, seps)) accumulateResult(split(s, seps))
proc splitSeq*(s: string, sep: char): seq[string] {.noSideEffect, proc splitSeq*(s: string, sep: char): seq[string] {.noSideEffect,
deprecated.} = deprecated.} =
## The same as `split`, but is a proc that returns a sequence of substrings. ## The same as `split`, but is a proc that returns a sequence of substrings.
## **Deprecated since version 0.8.0**: Use `split` instead. ## **Deprecated since version 0.8.0**: Use `split` instead.
iterToProc(split(s, sep)) accumulateResult(split(s, sep))
proc splitLines*(s: string): seq[string] {.noSideEffect.} = proc splitLines*(s: string): seq[string] {.noSideEffect.} =
## The same as the `splitLines` iterator, but is a proc that returns a ## The same as the `splitLines` iterator, but is a proc that returns a
## sequence of substrings. ## sequence of substrings.
iterToProc(splitLines(s)) accumulateResult(splitLines(s))
proc split*(s: string, seps: set[char] = Whitespace): seq[string] {. proc split*(s: string, seps: set[char] = Whitespace): seq[string] {.
noSideEffect.} = noSideEffect.} =
## The same as the `split` iterator, but is a proc that returns a ## The same as the `split` iterator, but is a proc that returns a
## sequence of substrings. ## sequence of substrings.
iterToProc(split(s, seps)) accumulateResult(split(s, seps))
proc split*(s: string, sep: char): seq[string] {.noSideEffect.} = proc split*(s: string, sep: char): seq[string] {.noSideEffect.} =
## The same as the `split` iterator, but is a proc that returns a sequence ## The same as the `split` iterator, but is a proc that returns a sequence
## of substrings. ## of substrings.
iterToProc(split(s, sep)) accumulateResult(split(s, sep))
proc cmpIgnoreCase*(a, b: string): int {.noSideEffect.} proc cmpIgnoreCase*(a, b: string): int {.noSideEffect.}
## Compares two strings in a case insensitive manner. Returns: ## Compares two strings in a case insensitive manner. Returns:
@ -358,8 +354,10 @@ proc ParseFloat*(s: string): float {.noSideEffect.}
## ``INF``, ``-INF`` are also supported (case insensitive comparison). ## ``INF``, ``-INF`` are also supported (case insensitive comparison).
# the stringify and format operators: # the stringify and format operators:
proc toString*[Ty](x: Ty): string proc toString*[Ty](x: Ty): string {.deprecated.}
## This generic proc is the same as the stringify operator `$`. ## This generic proc is the same as the stringify operator `$`.
##
## **Deprecated since version 0.8.2:** Use `$` instead.
proc repeatChar*(count: int, c: Char = ' '): string proc repeatChar*(count: int, c: Char = ' '): string
## Returns a string of length `count` consisting only of ## Returns a string of length `count` consisting only of

View file

@ -12,9 +12,14 @@
{.deadCodeElim: on.} {.deadCodeElim: on.}
type type
TRune* = int ## type that can hold any Unicode character irune = int # underlying type of TRune
TRune16* = int16 ## 16 bit Unicode character TRune* = distinct irune ## type that can hold any Unicode character
TRune16* = distinct int16 ## 16 bit Unicode character
proc `<=%`*(a, b: TRune): bool {.borrow.}
proc `<%`*(a, b: TRune): bool {.borrow.}
proc `==`*(a, b: TRune): bool {.borrow.}
template ones(n: expr): expr = ((1 shl n)-1) template ones(n: expr): expr = ((1 shl n)-1)
proc runeLen*(s: string): int = proc runeLen*(s: string): int =
@ -28,77 +33,71 @@ proc runeLen*(s: string): int =
else: assert(false) else: assert(false)
inc(result) inc(result)
proc runeLenAt*(s: string, i: int): int =
## returns the number of bytes the rune starting at ``s[i]`` takes.
if ord(s[i]) <=% 127: result = 1
elif ord(s[i]) shr 5 == 0b110: result = 2
elif ord(s[i]) shr 4 == 0b1110: result = 3
elif ord(s[i]) shr 3 == 0b11110: result = 4
else: assert(false)
template fastRuneAt*(s: string, i: int, result: expr, doInc = true) =
## Returns the unicode character ``s[i]`` in `result`. If ``doInc == true``
## `i` is incremented by the number of bytes that have been processed.
when not defined(ones):
template ones(n: expr): expr = ((1 shl n)-1)
if ord(s[i]) <=% 127:
result = TRune(ord(s[i]))
when doInc: inc(i)
elif ord(s[i]) shr 5 == 0b110:
assert(ord(s[i+1]) shr 6 == 0b10)
result = TRune((ord(s[i]) and ones(5)) shl 6 or (ord(s[i+1]) and ones(6)))
when doInc: inc(i, 2)
elif ord(s[i]) shr 4 == 0b1110:
assert(ord(s[i+1]) shr 6 == 0b10)
assert(ord(s[i+2]) shr 6 == 0b10)
result = TRune((ord(s[i]) and ones(4)) shl 12 or
(ord(s[i+1]) and ones(6)) shl 6 or
(ord(s[i+2]) and ones(6)))
when doInc: inc(i, 3)
elif ord(s[i]) shr 3 == 0b11110:
assert(ord(s[i+1]) shr 6 == 0b10)
assert(ord(s[i+2]) shr 6 == 0b10)
assert(ord(s[i+3]) shr 6 == 0b10)
result = TRune((ord(s[i]) and ones(3)) shl 18 or
(ord(s[i+1]) and ones(6)) shl 12 or
(ord(s[i+2]) and ones(6)) shl 6 or
(ord(s[i+3]) and ones(6)))
when doInc: inc(i, 4)
else:
assert(false)
proc runeAt*(s: string, i: int): TRune = proc runeAt*(s: string, i: int): TRune =
## returns the unicode character in `s` at byte index `i` ## returns the unicode character in `s` at byte index `i`
if ord(s[i]) <=% 127: fastRuneAt(s, i, result, false)
result = ord(s[i])
elif ord(s[i]) shr 5 == 0b110:
assert(ord(s[i+1]) shr 6 == 0b10)
result = (ord(s[i]) and ones(5)) shl 6 or (ord(s[i+1]) and ones(6))
elif ord(s[i]) shr 4 == 0b1110:
assert(ord(s[i+1]) shr 6 == 0b10)
assert(ord(s[i+2]) shr 6 == 0b10)
result = (ord(s[i]) and ones(4)) shl 12 or
(ord(s[i+1]) and ones(6)) shl 6 or
(ord(s[i+2]) and ones(6))
elif ord(s[i]) shr 3 == 0b11110:
assert(ord(s[i+1]) shr 6 == 0b10)
assert(ord(s[i+2]) shr 6 == 0b10)
assert(ord(s[i+3]) shr 6 == 0b10)
result = (ord(s[i]) and ones(3)) shl 18 or
(ord(s[i+1]) and ones(6)) shl 12 or
(ord(s[i+2]) and ones(6)) shl 6 or
(ord(s[i+3]) and ones(6))
else:
assert(false)
template fastRuneAt(s, i, result: expr): stmt =
if ord(s[i]) <=% 127:
result = ord(s[i])
inc(i)
elif ord(s[i]) shr 5 == 0b110:
assert(ord(s[i+1]) shr 6 == 0b10)
result = (ord(s[i]) and ones(5)) shl 6 or (ord(s[i+1]) and ones(6))
inc(i, 2)
elif ord(s[i]) shr 4 == 0b1110:
assert(ord(s[i+1]) shr 6 == 0b10)
assert(ord(s[i+2]) shr 6 == 0b10)
result = (ord(s[i]) and ones(4)) shl 12 or
(ord(s[i+1]) and ones(6)) shl 6 or
(ord(s[i+2]) and ones(6))
inc(i, 3)
elif ord(s[i]) shr 3 == 0b11110:
assert(ord(s[i+1]) shr 6 == 0b10)
assert(ord(s[i+2]) shr 6 == 0b10)
assert(ord(s[i+3]) shr 6 == 0b10)
result = (ord(s[i]) and ones(3)) shl 18 or
(ord(s[i+1]) and ones(6)) shl 12 or
(ord(s[i+2]) and ones(6)) shl 6 or
(ord(s[i+3]) and ones(6))
inc(i, 4)
else:
assert(false)
proc toUTF8*(c: TRune): string = proc toUTF8*(c: TRune): string =
## converts a character into its UTF8 representation ## converts a rune into its UTF8 representation
if c <=% 127: var i = irune(c)
if i <=% 127:
result = newString(1) result = newString(1)
result[0] = chr(c) result[0] = chr(i)
elif c <=% 0x07FF: elif i <=% 0x07FF:
result = newString(2) result = newString(2)
result[0] = chr(c shr 6 or 0b110_0000) result[0] = chr(i shr 6 or 0b110_0000)
result[1] = chr(c and ones(6) or 0b10_000000) result[1] = chr(i and ones(6) or 0b10_000000)
elif c <=% 0xFFFF: elif i <=% 0xFFFF:
result = newString(3) result = newString(3)
result[0] = chr(c shr 12 or 0b1110_0000) result[0] = chr(i shr 12 or 0b1110_0000)
result[1] = chr(c shr 6 and ones(6) or 0b10_0000_00) result[1] = chr(i shr 6 and ones(6) or 0b10_0000_00)
result[2] = chr(c and ones(6) or 0b10_0000_00) result[2] = chr(i and ones(6) or 0b10_0000_00)
elif c <=% 0x0010FFFF: elif i <=% 0x0010FFFF:
result = newString(4) result = newString(4)
result[0] = chr(c shr 18 or 0b1111_0000) result[0] = chr(i shr 18 or 0b1111_0000)
result[1] = chr(c shr 12 and ones(6) or 0b10_0000_00) result[1] = chr(i shr 12 and ones(6) or 0b10_0000_00)
result[2] = chr(c shr 6 and ones(6) or 0b10_0000_00) result[2] = chr(i shr 6 and ones(6) or 0b10_0000_00)
result[3] = chr(c and ones(6) or 0b10_0000_00) result[3] = chr(i and ones(6) or 0b10_0000_00)
else: else:
assert false assert false
@ -1061,7 +1060,7 @@ const
0x01f1, 501, # DZ Dz 0x01f1, 501, # DZ Dz
0x01f3, 499] # dz Dz 0x01f3, 499] # dz Dz
proc binarySearch(c: TRune, tab: openArray[TRune], len, stride: int): int = proc binarySearch(c: irune, tab: openArray[iRune], len, stride: int): int =
var n = len var n = len
var t = 0 var t = 0
while n > 1: while n > 1:
@ -1078,32 +1077,39 @@ proc binarySearch(c: TRune, tab: openArray[TRune], len, stride: int): int =
proc toLower*(c: TRune): TRune = proc toLower*(c: TRune): TRune =
## Converts `c` into lower case. This works for any Unicode character. ## Converts `c` into lower case. This works for any Unicode character.
## If possible, prefer `toLower` over `toUpper`.
var c = irune(c)
var p = binarySearch(c, tolowerRanges, len(toLowerRanges) div 3, 3) var p = binarySearch(c, tolowerRanges, len(toLowerRanges) div 3, 3)
if p >= 0 and c >= tolowerRanges[p] and c <= tolowerRanges[p+1]: if p >= 0 and c >= tolowerRanges[p] and c <= tolowerRanges[p+1]:
return c + tolowerRanges[p+2] - 500 return TRune(c + tolowerRanges[p+2] - 500)
p = binarySearch(c, toLowerSinglets, len(toLowerSinglets) div 2, 2) p = binarySearch(c, toLowerSinglets, len(toLowerSinglets) div 2, 2)
if p >= 0 and c == toLowerSinglets[p]: if p >= 0 and c == toLowerSinglets[p]:
return c + toLowerSinglets[p+1] - 500 return TRune(c + toLowerSinglets[p+1] - 500)
return c return TRune(c)
proc toUpper*(c: TRune): TRune = proc toUpper*(c: TRune): TRune =
## Converts `c` into upper case. This works for any Unicode character. ## Converts `c` into upper case. This works for any Unicode character.
## If possible, prefer `toLower` over `toUpper`.
var c = irune(c)
var p = binarySearch(c, toUpperRanges, len(toUpperRanges) div 3, 3) var p = binarySearch(c, toUpperRanges, len(toUpperRanges) div 3, 3)
if p >= 0 and c >= toUpperRanges[p] and c <= toUpperRanges[p+1]: if p >= 0 and c >= toUpperRanges[p] and c <= toUpperRanges[p+1]:
return c + toUpperRanges[p+2] - 500 return TRune(c + toUpperRanges[p+2] - 500)
p = binarySearch(c, toUpperSinglets, len(toUpperSinglets) div 2, 2) p = binarySearch(c, toUpperSinglets, len(toUpperSinglets) div 2, 2)
if p >= 0 and c == toUpperSinglets[p]: if p >= 0 and c == toUpperSinglets[p]:
return c + toUpperSinglets[p+1] - 500 return TRune(c + toUpperSinglets[p+1] - 500)
return c return TRune(c)
proc toTitle*(c: TRune): TRune = proc toTitle*(c: TRune): TRune =
var c = irune(c)
var p = binarySearch(c, toTitleSinglets, len(toTitleSinglets) div 2, 2) var p = binarySearch(c, toTitleSinglets, len(toTitleSinglets) div 2, 2)
if p >= 0 and c == toTitleSinglets[p]: if p >= 0 and c == toTitleSinglets[p]:
return c + toTitleSinglets[p+1] - 500 return TRune(c + toTitleSinglets[p+1] - 500)
return c return TRune(c)
proc isLower*(c: TRune): bool = proc isLower*(c: TRune): bool =
## returns true iff `c` is a lower case Unicode character ## returns true iff `c` is a lower case Unicode character
## If possible, prefer `isLower` over `isUpper`.
var c = irune(c)
# Note: toUpperRanges is correct here! # Note: toUpperRanges is correct here!
var p = binarySearch(c, toUpperRanges, len(toUpperRanges) div 3, 3) var p = binarySearch(c, toUpperRanges, len(toUpperRanges) div 3, 3)
if p >= 0 and c >= toUpperRanges[p] and c <= toUpperRanges[p+1]: if p >= 0 and c >= toUpperRanges[p] and c <= toUpperRanges[p+1]:
@ -1114,6 +1120,8 @@ proc isLower*(c: TRune): bool =
proc isUpper*(c: TRune): bool = proc isUpper*(c: TRune): bool =
## returns true iff `c` is a upper case Unicode character ## returns true iff `c` is a upper case Unicode character
## If possible, prefer `isLower` over `isUpper`.
var c = irune(c)
# Note: toLowerRanges is correct here! # Note: toLowerRanges is correct here!
var p = binarySearch(c, toLowerRanges, len(toLowerRanges) div 3, 3) var p = binarySearch(c, toLowerRanges, len(toLowerRanges) div 3, 3)
if p >= 0 and c >= toLowerRanges[p] and c <= toLowerRanges[p+1]: if p >= 0 and c >= toLowerRanges[p] and c <= toLowerRanges[p+1]:
@ -1126,6 +1134,7 @@ proc isAlpha*(c: TRune): bool =
## returns true iff `c` is an *alpha* Unicode character (i.e. a letter) ## returns true iff `c` is an *alpha* Unicode character (i.e. a letter)
if isUpper(c) or isLower(c): if isUpper(c) or isLower(c):
return true return true
var c = irune(c)
var p = binarySearch(c, alphaRanges, len(alphaRanges) div 2, 2) var p = binarySearch(c, alphaRanges, len(alphaRanges) div 2, 2)
if p >= 0 and c >= alphaRanges[p] and c <= alphaRanges[p+1]: if p >= 0 and c >= alphaRanges[p] and c <= alphaRanges[p+1]:
return true return true
@ -1138,6 +1147,7 @@ proc isTitle*(c: TRune): bool =
proc isWhiteSpace*(c: TRune): bool = proc isWhiteSpace*(c: TRune): bool =
## returns true iff `c` is a Unicode whitespace character ## returns true iff `c` is a Unicode whitespace character
var c = irune(c)
var p = binarySearch(c, spaceRanges, len(spaceRanges) div 2, 2) var p = binarySearch(c, spaceRanges, len(spaceRanges) div 2, 2)
if p >= 0 and c >= spaceRanges[p] and c <= spaceRanges[p+1]: if p >= 0 and c >= spaceRanges[p] and c <= spaceRanges[p+1]:
return true return true
@ -1148,7 +1158,7 @@ iterator runes*(s: string): TRune =
i = 0 i = 0
result: TRune result: TRune
while i < len(s): while i < len(s):
fastRuneAt(s, i, result) fastRuneAt(s, i, result, true)
yield result yield result
proc cmpRunesIgnoreCase*(a, b: string): int = proc cmpRunesIgnoreCase*(a, b: string): int =
@ -1164,7 +1174,7 @@ proc cmpRunesIgnoreCase*(a, b: string): int =
# slow path: # slow path:
fastRuneAt(a, i, ar) fastRuneAt(a, i, ar)
fastRuneAt(b, j, br) fastRuneAt(b, j, br)
result = toLower(ar) - toLower(br) result = irune(toLower(ar)) - irune(toLower(br))
if result != 0: return if result != 0: return
result = a.len - b.len result = a.len - b.len

View file

@ -13,9 +13,6 @@
## Each module implicitly imports the System module; it may not be listed ## Each module implicitly imports the System module; it may not be listed
## explicitly. Because of this there can not be a user-defined module named ## explicitly. Because of this there can not be a user-defined module named
## ``system``. ## ``system``.
##
## *The good thing about reinventing the wheel is that you can get a
## round one.*
{.push hints: off.} {.push hints: off.}
@ -58,11 +55,10 @@ proc defined*[T](x: T): bool {.magic: "Defined", noSideEffect.}
## # provide our own toUpper proc here, because strutils is ## # provide our own toUpper proc here, because strutils is
## # missing it. ## # missing it.
proc definedInScope*[T](x: T, scope=0): bool {. proc definedInScope*[T](x: T): bool {.
magic: "DefinedInScope", noSideEffect.} magic: "DefinedInScope", noSideEffect.}
## Special comile-time procedure that checks whether `x` is ## Special comile-time procedure that checks whether `x` is
## defined in the scope `scope`. `x` has to be an identifier. ## defined in the current scope. `x` has to be an identifier.
## 0 means the current scope, 1 means the scope above the current scope, etc.
# these require compiler magic: # these require compiler magic:
proc `not` *(x: bool): bool {.magic: "Not", noSideEffect.} proc `not` *(x: bool): bool {.magic: "Not", noSideEffect.}
@ -617,6 +613,27 @@ proc `@` * [IDX, T](a: array[IDX, T]): seq[T] {.
## sequences with the array constructor: ``@[1, 2, 3]`` has the type ## sequences with the array constructor: ``@[1, 2, 3]`` has the type
## ``seq[int]``, while ``[1, 2, 3]`` has the type ``array[0..2, int]``. ## ``seq[int]``, while ``[1, 2, 3]`` has the type ``array[0..2, int]``.
proc setLen*[T](s: var seq[T], newlen: int) {.
magic: "SetLengthSeq", noSideEffect.}
## sets the length of `s` to `newlen`.
## ``T`` may be any sequence type.
## If the current length is greater than the new length,
## ``s`` will be truncated.
proc setLen*(s: var string, newlen: int) {.
magic: "SetLengthStr", noSideEffect.}
## sets the length of `s` to `newlen`.
## If the current length is greater than the new length,
## ``s`` will be truncated.
proc newString*(len: int): string {.
magic: "NewString", importc: "mnewString", noSideEffect.}
## returns a new string of length ``len`` but with uninitialized
## content. One needs to fill the string character after character
## with the index operator ``s[i]``. This procedure exists only for
## optimization purposes; the same effect can be achieved with the
## ``&`` operator.
# concat operator: # concat operator:
proc `&` * (x: string, y: char): string {. proc `&` * (x: string, y: char): string {.
magic: "ConStrStr", noSideEffect, merge.} magic: "ConStrStr", noSideEffect, merge.}
@ -651,12 +668,15 @@ else:
""" """
proc add *[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.} proc add *[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.}
proc add *[T](x: var seq[T], y: seq[T]) {.magic: "AppendSeqSeq", noSideEffect.} proc add *[T](x: var seq[T], y: seq[T]) {.noSideEffect.} =
## Generic proc for adding a data item `y` to a container `x`. ## Generic proc for adding a data item `y` to a container `x`.
## For containers that have an order, `add` means *append*. New generic ## For containers that have an order, `add` means *append*. New generic
## containers should also call their adding proc `add` for consistency. ## containers should also call their adding proc `add` for consistency.
## Generic code becomes much easier to write if the Nimrod naming scheme is ## Generic code becomes much easier to write if the Nimrod naming scheme is
## respected. ## respected.
var xl = x.len
setLen(x, xl + y.len)
for i in 0..high(y): x[xl+i] = y[i]
proc repr*[T](x: T): string {.magic: "Repr", noSideEffect.} proc repr*[T](x: T): string {.magic: "Repr", noSideEffect.}
## takes any Nimrod variable and returns its string representation. It ## takes any Nimrod variable and returns its string representation. It
@ -798,20 +818,6 @@ proc copy*(s: string, first, last: int): string {.
## the first and last characters that shall be copied. If ``last`` ## the first and last characters that shall be copied. If ``last``
## is omitted, it is treated as ``high(s)``. ## is omitted, it is treated as ``high(s)``.
proc setLen*(s: var string, newlen: int) {.
magic: "SetLengthStr", noSideEffect.}
## sets the length of `s` to `newlen`.
## If the current length is greater than the new length,
## ``s`` will be truncated.
proc newString*(len: int): string {.
magic: "NewString", importc: "mnewString", noSideEffect.}
## returns a new string of length ``len`` but with uninitialized
## content. One needs to fill the string character after character
## with the index operator ``s[i]``. This procedure exists only for
## optimization purposes; the same effect can be achieved with the
## ``&`` operator.
proc zeroMem*(p: Pointer, size: int) {.importc, noDecl.} proc zeroMem*(p: Pointer, size: int) {.importc, noDecl.}
## overwrites the contents of the memory at ``p`` with the value 0. ## overwrites the contents of the memory at ``p`` with the value 0.
## Exactly ``size`` bytes will be overwritten. Like any procedure ## Exactly ``size`` bytes will be overwritten. Like any procedure
@ -861,13 +867,6 @@ proc dealloc*(p: Pointer) {.noconv.}
## or other memory may be corrupted. So this procedure is really ## or other memory may be corrupted. So this procedure is really
## *unsafe*. ## *unsafe*.
proc setLen*[T](s: var seq[T], newlen: int) {.
magic: "SetLengthSeq", noSideEffect.}
## sets the length of `s` to `newlen`.
## ``T`` may be any sequence type.
## If the current length is greater than the new length,
## ``s`` will be truncated.
proc assert*(cond: bool) {.magic: "Assert", noSideEffect.} proc assert*(cond: bool) {.magic: "Assert", noSideEffect.}
## provides a means to implement `programming by contracts` in Nimrod. ## provides a means to implement `programming by contracts` in Nimrod.
## ``assert`` evaluates expression ``cond`` and if ``cond`` is false, it ## ``assert`` evaluates expression ``cond`` and if ``cond`` is false, it
@ -1195,6 +1194,10 @@ proc GC_unref*[T](x: seq[T]) {.magic: "GCunref".}
proc GC_unref*(x: string) {.magic: "GCunref".} proc GC_unref*(x: string) {.magic: "GCunref".}
## see the documentation of `GC_ref`. ## see the documentation of `GC_ref`.
template accumulateResult*(iter: expr) =
## helps to convert an iterator to a proc.
result = @[]
for x in iter: add(result, x)
{.push checks: off, line_dir: off, debugger: off.} {.push checks: off, line_dir: off, debugger: off.}
# obviously we cannot generate checking operations here :-) # obviously we cannot generate checking operations here :-)

View file

@ -74,26 +74,16 @@ proc hashString(s: string): int {.compilerproc.} =
h = h +% h shl 15 h = h +% h shl 15
result = h result = h
# copy(s: string, start = 0): string
# {.extern: "copyStr", noDecl, noSideEffect.}
# copy(s: string, start, len: int): string
# {.extern: "copyStrLen", noDecl, noSideEffect.}
#
# setLength(var s: string, newlen: int)
# {.extern: "setLengthStr", noDecl, noSideEffect.}
proc copyStrLast(s: NimString, start, last: int): NimString {.exportc.} = proc copyStrLast(s: NimString, start, last: int): NimString {.exportc.} =
var var start = max(start, 0)
len: int var len = min(last, s.len-1) - start + 1
if start >= s.len: return mnewString(0) # BUGFIX if len > 0:
if last >= s.len: result = rawNewString(len)
len = s.len - start # - 1 + 1 result.len = len
c_memcpy(result.data, addr(s.data[start]), len * sizeof(Char))
result.data[len] = '\0'
else: else:
len = last - start + 1 result = mnewString(0)
result = rawNewString(len)
result.len = len
c_memcpy(result.data, addr(s.data[start]), len * sizeof(Char))
result.data[len] = '\0'
proc copyStr(s: NimString, start: int): NimString {.exportc.} = proc copyStr(s: NimString, start: int): NimString {.exportc.} =
return copyStrLast(s, start, s.len-1) return copyStrLast(s, start, s.len-1)

View file

@ -1,110 +1,110 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2009 Andreas Rumpf # (c) Copyright 2009 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## This module implements a small wrapper for some needed Win API procedures, ## This module implements a small wrapper for some needed Win API procedures,
## so that the Nimrod compiler does not depend on the huge Windows module. ## so that the Nimrod compiler does not depend on the huge Windows module.
type type
THandle* = int THandle* = int
WINBOOL* = int32 WINBOOL* = int32
TSECURITY_ATTRIBUTES* {.final, pure.} = object TSECURITY_ATTRIBUTES* {.final, pure.} = object
nLength*: int32 nLength*: int32
lpSecurityDescriptor*: pointer lpSecurityDescriptor*: pointer
bInheritHandle*: WINBOOL bInheritHandle*: WINBOOL
TSTARTUPINFO* {.final, pure.} = object TSTARTUPINFO* {.final, pure.} = object
cb*: int32 cb*: int32
lpReserved*: cstring lpReserved*: cstring
lpDesktop*: cstring lpDesktop*: cstring
lpTitle*: cstring lpTitle*: cstring
dwX*: int32 dwX*: int32
dwY*: int32 dwY*: int32
dwXSize*: int32 dwXSize*: int32
dwYSize*: int32 dwYSize*: int32
dwXCountChars*: int32 dwXCountChars*: int32
dwYCountChars*: int32 dwYCountChars*: int32
dwFillAttribute*: int32 dwFillAttribute*: int32
dwFlags*: int32 dwFlags*: int32
wShowWindow*: int16 wShowWindow*: int16
cbReserved2*: int16 cbReserved2*: int16
lpReserved2*: pointer lpReserved2*: pointer
hStdInput*: THANDLE hStdInput*: THANDLE
hStdOutput*: THANDLE hStdOutput*: THANDLE
hStdError*: THANDLE hStdError*: THANDLE
TPROCESS_INFORMATION* {.final, pure.} = object TPROCESS_INFORMATION* {.final, pure.} = object
hProcess*: THANDLE hProcess*: THANDLE
hThread*: THANDLE hThread*: THANDLE
dwProcessId*: int32 dwProcessId*: int32
dwThreadId*: int32 dwThreadId*: int32
const const
STARTF_USESHOWWINDOW* = 1'i32 STARTF_USESHOWWINDOW* = 1'i32
STARTF_USESTDHANDLES* = 256'i32 STARTF_USESTDHANDLES* = 256'i32
HIGH_PRIORITY_CLASS* = 128'i32 HIGH_PRIORITY_CLASS* = 128'i32
IDLE_PRIORITY_CLASS* = 64'i32 IDLE_PRIORITY_CLASS* = 64'i32
NORMAL_PRIORITY_CLASS* = 32'i32 NORMAL_PRIORITY_CLASS* = 32'i32
REALTIME_PRIORITY_CLASS* = 256'i32 REALTIME_PRIORITY_CLASS* = 256'i32
WAIT_TIMEOUT* = 0x00000102'i32 WAIT_TIMEOUT* = 0x00000102'i32
INFINITE* = -1'i32 INFINITE* = -1'i32
STD_INPUT_HANDLE* = -10'i32 STD_INPUT_HANDLE* = -10'i32
STD_OUTPUT_HANDLE* = -11'i32 STD_OUTPUT_HANDLE* = -11'i32
STD_ERROR_HANDLE* = -12'i32 STD_ERROR_HANDLE* = -12'i32
proc CloseHandle*(hObject: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32", proc CloseHandle*(hObject: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32",
importc: "CloseHandle".} importc: "CloseHandle".}
proc ReadFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToRead: int32, proc ReadFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToRead: int32,
lpNumberOfBytesRead: var int32, lpOverlapped: pointer): WINBOOL{. lpNumberOfBytesRead: var int32, lpOverlapped: pointer): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "ReadFile".} stdcall, dynlib: "kernel32", importc: "ReadFile".}
proc WriteFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToWrite: int32, proc WriteFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToWrite: int32,
lpNumberOfBytesWritten: var int32, lpNumberOfBytesWritten: var int32,
lpOverlapped: pointer): WINBOOL{. lpOverlapped: pointer): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "WriteFile".} stdcall, dynlib: "kernel32", importc: "WriteFile".}
proc CreatePipe*(hReadPipe, hWritePipe: var THandle, proc CreatePipe*(hReadPipe, hWritePipe: var THandle,
lpPipeAttributes: var TSECURITY_ATTRIBUTES, lpPipeAttributes: var TSECURITY_ATTRIBUTES,
nSize: int32): WINBOOL{. nSize: int32): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "CreatePipe".} stdcall, dynlib: "kernel32", importc: "CreatePipe".}
proc CreateProcess*(lpApplicationName, lpCommandLine: cstring, proc CreateProcess*(lpApplicationName, lpCommandLine: cstring,
lpProcessAttributes: ptr TSECURITY_ATTRIBUTES, lpProcessAttributes: ptr TSECURITY_ATTRIBUTES,
lpThreadAttributes: ptr TSECURITY_ATTRIBUTES, lpThreadAttributes: ptr TSECURITY_ATTRIBUTES,
bInheritHandles: WINBOOL, dwCreationFlags: int32, bInheritHandles: WINBOOL, dwCreationFlags: int32,
lpEnvironment: pointer, lpCurrentDirectory: cstring, lpEnvironment: pointer, lpCurrentDirectory: cstring,
lpStartupInfo: var TSTARTUPINFO, lpStartupInfo: var TSTARTUPINFO,
lpProcessInformation: var TPROCESS_INFORMATION): WINBOOL{. lpProcessInformation: var TPROCESS_INFORMATION): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "CreateProcessA".} stdcall, dynlib: "kernel32", importc: "CreateProcessA".}
proc SuspendThread*(hThread: THANDLE): int32 {.stdcall, dynlib: "kernel32", proc SuspendThread*(hThread: THANDLE): int32 {.stdcall, dynlib: "kernel32",
importc: "SuspendThread".} importc: "SuspendThread".}
proc ResumeThread*(hThread: THANDLE): int32 {.stdcall, dynlib: "kernel32", proc ResumeThread*(hThread: THANDLE): int32 {.stdcall, dynlib: "kernel32",
importc: "ResumeThread".} importc: "ResumeThread".}
proc WaitForSingleObject*(hHandle: THANDLE, dwMilliseconds: int32): int32 {. proc WaitForSingleObject*(hHandle: THANDLE, dwMilliseconds: int32): int32 {.
stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".} stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".}
proc TerminateProcess*(hProcess: THANDLE, uExitCode: int): WINBOOL {.stdcall, proc TerminateProcess*(hProcess: THANDLE, uExitCode: int): WINBOOL {.stdcall,
dynlib: "kernel32", importc: "TerminateProcess".} dynlib: "kernel32", importc: "TerminateProcess".}
proc GetExitCodeProcess*(hProcess: THANDLE, lpExitCode: var int32): WINBOOL {. proc GetExitCodeProcess*(hProcess: THANDLE, lpExitCode: var int32): WINBOOL {.
stdcall, dynlib: "kernel32", importc: "GetExitCodeProcess".} stdcall, dynlib: "kernel32", importc: "GetExitCodeProcess".}
proc GetStdHandle*(nStdHandle: int32): THANDLE {.stdcall, dynlib: "kernel32", proc GetStdHandle*(nStdHandle: int32): THANDLE {.stdcall, dynlib: "kernel32",
importc: "GetStdHandle".} importc: "GetStdHandle".}
proc SetStdHandle*(nStdHandle: int32, hHandle: THANDLE): WINBOOL {.stdcall, proc SetStdHandle*(nStdHandle: int32, hHandle: THANDLE): WINBOOL {.stdcall,
dynlib: "kernel32", importc: "SetStdHandle".} dynlib: "kernel32", importc: "SetStdHandle".}
proc FlushFileBuffers*(hFile: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32", proc FlushFileBuffers*(hFile: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32",
importc: "FlushFileBuffers".} importc: "FlushFileBuffers".}
proc GetLastError*(): int32 {.importc, stdcall, dynlib: "kernel32".} proc GetLastError*(): int32 {.importc, stdcall, dynlib: "kernel32".}
proc FormatMessageA*(dwFlags: int32, lpSource: pointer, proc FormatMessageA*(dwFlags: int32, lpSource: pointer,

259
lib/wrappers/tre/config.h Normal file
View file

@ -0,0 +1,259 @@
/* config.h. Generated from config.h.in by configure. */
/* config.h.in. Generated from configure.ac by autoheader. */
/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
systems. This function is required for `alloca.c' support on those systems.
*/
/* #undef CRAY_STACKSEG_END */
/* Define to 1 if using `alloca.c'. */
/* #undef C_ALLOCA */
/* Define to 1 if translation of program messages to the user's native
language is requested. */
#define ENABLE_NLS 1
/* Define to 1 if you have `alloca', as a function or macro. */
/* #undef HAVE_ALLOCA */
/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
*/
/* #undef HAVE_ALLOCA_H */
/* Define to 1 if you have the MacOS X function CFLocaleCopyCurrent in the
CoreFoundation framework. */
#define HAVE_CFLOCALECOPYCURRENT 1
/* Define to 1 if you have the MacOS X function CFPreferencesCopyAppValue in
the CoreFoundation framework. */
#define HAVE_CFPREFERENCESCOPYAPPVALUE 1
/* Define if the GNU dcgettext() function is already present or preinstalled.
*/
#define HAVE_DCGETTEXT 1
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1
/* Define to 1 if you have the <getopt.h> header file. */
#define HAVE_GETOPT_H 1
/* Define to 1 if you have the `getopt_long' function. */
#define HAVE_GETOPT_LONG 1
/* Define if the GNU gettext() function is already present or preinstalled. */
#define HAVE_GETTEXT 1
/* Define if you have the iconv() function and it works. */
#define HAVE_ICONV 1
/* Define to 1 if you have the <inttypes.h> header file. */
/* #undef HAVE_INTTYPES_H */
/* Define to 1 if you have the `isascii' function. */
#define HAVE_ISASCII 1
/* Define to 1 if you have the `isblank' function. */
#define HAVE_ISBLANK 1
/* Define to 1 if you have the `iswascii' function or macro. */
/* #undef HAVE_ISWASCII */
/* Define to 1 if you have the `iswblank' function or macro. */
/* #undef HAVE_ISWBLANK */
/* Define to 1 if you have the `iswctype' function or macro. */
/* #undef HAVE_ISWCTYPE */
/* Define to 1 if you have the `iswlower' function or macro. */
/* #undef HAVE_ISWLOWER */
/* Define to 1 if you have the `iswupper' function or macro. */
/* #undef HAVE_ISWUPPER */
/* Define to 1 if you have the <libutf8.h> header file. */
/* #undef HAVE_LIBUTF8_H */
/* Define to 1 if you have the `mbrtowc' function or macro. */
/* #undef HAVE_MBRTOWC */
/* Define to 1 if the system has the type `mbstate_t'. */
/* #undef HAVE_MBSTATE_T */
/* Define to 1 if you have the `mbtowc' function or macro. */
/* #undef HAVE_MBTOWC */
/* Define to 1 if you have the <memory.h> header file. */
/* #undef HAVE_MEMORY_H */
/* Define to 1 if you have the <regex.h> header file. */
/* #undef HAVE_REGEX_H */
/* Define to 1 if the system has the type `reg_errcode_t'. */
/* #undef HAVE_REG_ERRCODE_T */
/* Define to 1 if you have the <stdint.h> header file. */
/* #undef HAVE_STDINT_H */
/* Define to 1 if you have the <stdlib.h> header file. */
/* #undef HAVE_STDLIB_H */
/* Define to 1 if you have the <strings.h> header file. */
/* #undef HAVE_STRINGS_H */
/* Define to 1 if you have the <string.h> header file. */
/* #undef HAVE_STRING_H */
/* Define to 1 if you have the <sys/stat.h> header file. */
/* #undef HAVE_SYS_STAT_H */
/* Define to 1 if you have the <sys/types.h> header file. */
/* #undef HAVE_SYS_TYPES_H */
/* Define to 1 if you have the `towlower' function or macro. */
/* #undef HAVE_TOWLOWER */
/* Define to 1 if you have the `towupper' function or macro. */
/* #undef HAVE_TOWUPPER */
/* Define to 1 if you have the <unistd.h> header file. */
/* #undef HAVE_UNISTD_H */
/* Define to 1 if you have the <wchar.h> header file. */
/* #undef HAVE_WCHAR_H */
/* Define to 1 if the system has the type `wchar_t'. */
/* #undef HAVE_WCHAR_T */
/* Define to 1 if you have the `wcschr' function or macro. */
/* #undef HAVE_WCSCHR */
/* Define to 1 if you have the `wcscpy' function or macro. */
/* #undef HAVE_WCSCPY */
/* Define to 1 if you have the `wcslen' function or macro. */
/* #undef HAVE_WCSLEN */
/* Define to 1 if you have the `wcsncpy' function or macro. */
/* #undef HAVE_WCSNCPY */
/* Define to 1 if you have the `wcsrtombs' function or macro. */
/* #undef HAVE_WCSRTOMBS */
/* Define to 1 if you have the `wcstombs' function or macro. */
/* #undef HAVE_WCSTOMBS */
/* Define to 1 if you have the `wctype' function or macro. */
/* #undef HAVE_WCTYPE */
/* Define to 1 if you have the <wctype.h> header file. */
/* #undef HAVE_WCTYPE_H */
/* Define to 1 if the system has the type `wint_t'. */
/* #undef HAVE_WINT_T */
/* Define if you want to disable debug assertions. */
#define NDEBUG 1
/* Define to 1 if your C compiler doesn't accept -c and -o together. */
/* #undef NO_MINUS_C_MINUS_O */
/* Name of package */
#define PACKAGE "tre"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "tre-general@lists.laurikari.net"
/* Define to the full name of this package. */
#define PACKAGE_NAME "TRE"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "TRE 0.7.6"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "tre"
/* Define to the version of this package. */
#define PACKAGE_VERSION "0.7.6"
/* If using the C implementation of alloca, define if you know the
direction of stack growth for your system; otherwise it will be
automatically deduced at runtime.
STACK_DIRECTION > 0 => grows toward higher addresses
STACK_DIRECTION < 0 => grows toward lower addresses
STACK_DIRECTION = 0 => direction of growth unknown */
/* #undef STACK_DIRECTION */
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Define if you want to enable approximate matching functionality. */
/* #undef TRE_APPROX */
/* Define if you want TRE to print debug messages to stdout. */
/* #undef TRE_DEBUG */
/* Define to enable multibyte character set support. */
/* #undef TRE_MULTIBYTE */
/* Define to a field in the regex_t struct where TRE should store a pointer to
the internal tre_tnfa_t structure */
#define TRE_REGEX_T_FIELD value
/* Define to the absolute path to the system regex.h */
/* #undef TRE_SYSTEM_REGEX_H_PATH */
/* Define if you want TRE to use alloca() instead of malloc() when allocating
memory needed for regexec operations. */
/* #undef TRE_USE_ALLOCA */
/* Define to include the system regex.h from TRE regex.h */
/* #undef TRE_USE_SYSTEM_REGEX_H */
/* TRE version string. */
#define TRE_VERSION "0.7.6"
/* TRE version level 1. */
#define TRE_VERSION_1 0
/* TRE version level 2. */
#define TRE_VERSION_2 7
/* TRE version level 3. */
#define TRE_VERSION_3 6
/* Define to enable wide character (wchar_t) support. */
/* #undef TRE_WCHAR */
/* Version number of package */
#define VERSION "0.7.6"
/* Define to the maximum value of wchar_t if not already defined elsewhere */
/* #undef WCHAR_MAX */
/* Define if wchar_t is signed */
/* #undef WCHAR_T_SIGNED */
/* Define if wchar_t is unsigned */
/* #undef WCHAR_T_UNSIGNED */
/* Number of bits in a file offset, on hosts where this is settable. */
/* #undef _FILE_OFFSET_BITS */
/* Define to enable GNU extensions in glibc */
#define _GNU_SOURCE 1
/* Define for large files, on AIX-style hosts. */
/* #undef _LARGE_FILES */
/* Define on IRIX */
/* #undef _REGCOMP_INTERNAL */
/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const */
/* Define to `__inline__' or `__inline' if that's what the C compiler
calls it, or to nothing if 'inline' is not supported under any name. */
#ifndef __cplusplus
/* #undef inline */
#endif

8873
lib/wrappers/tre/tre_all.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1 @@
0.7.6

View file

@ -84,20 +84,21 @@ type
nkObjDownConv, nkObjUpConv, nkChckRangeF, nkChckRange64, nkObjDownConv, nkObjUpConv, nkChckRangeF, nkChckRange64,
nkChckRange, nkStringToCString, nkCStringToString, nkPassAsOpenArray, nkChckRange, nkStringToCString, nkCStringToString, nkPassAsOpenArray,
nkAsgn, nkFastAsgn, nkGenericParams, nkFormalParams, nkAsgn, nkFastAsgn, nkGenericParams, nkFormalParams,
nkOfInherit, nkModule, nkProcDef, nkConverterDef, nkOfInherit, nkModule, nkProcDef, nkMethodDef,
nkMacroDef, nkTemplateDef, nkIteratorDef, nkOfBranch, nkConverterDef, nkMacroDef, nkTemplateDef, nkIteratorDef,
nkElifBranch, nkExceptBranch, nkElse, nkMacroStmt, nkOfBranch, nkElifBranch, nkExceptBranch, nkElse,
nkAsmStmt, nkPragma, nkIfStmt, nkWhenStmt, nkMacroStmt, nkAsmStmt, nkPragma, nkIfStmt,
nkForStmt, nkWhileStmt, nkCaseStmt, nkVarSection, nkWhenStmt, nkForStmt, nkWhileStmt, nkCaseStmt,
nkConstSection, nkConstDef, nkTypeSection, nkTypeDef, nkVarSection, nkConstSection, nkConstDef, nkTypeSection,
nkYieldStmt, nkTryStmt, nkFinally, nkRaiseStmt, nkTypeDef, nkYieldStmt, nkTryStmt, nkFinally,
nkReturnStmt, nkBreakStmt, nkContinueStmt, nkBlockStmt, nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt,
nkDiscardStmt, nkStmtList, nkImportStmt, nkFromStmt, nkBlockStmt, nkDiscardStmt, nkStmtList, nkImportStmt,
nkIncludeStmt, nkCommentStmt, nkStmtListExpr, nkBlockExpr, nkFromStmt, nkIncludeStmt, nkCommentStmt, nkStmtListExpr,
nkStmtListType, nkBlockType, nkTypeOfExpr, nkObjectTy, nkBlockExpr, nkStmtListType, nkBlockType, nkTypeOfExpr,
nkTupleTy, nkRecList, nkRecCase, nkRecWhen, nkObjectTy, nkTupleTy, nkRecList, nkRecCase,
nkRefTy, nkPtrTy, nkVarTy, nkDistinctTy, nkRecWhen, nkRefTy, nkPtrTy, nkVarTy,
nkProcTy, nkEnumTy, nkEnumFieldDef, nkReturnToken); nkDistinctTy, nkProcTy, nkEnumTy, nkEnumFieldDef,
nkReturnToken);
TNodeKinds = set of TNodeKind; TNodeKinds = set of TNodeKind;
const const
NodeKindToStr: array [TNodeKind] of string = ( NodeKindToStr: array [TNodeKind] of string = (
@ -118,20 +119,21 @@ const
'nkObjDownConv', 'nkObjUpConv', 'nkChckRangeF', 'nkChckRange64', 'nkObjDownConv', 'nkObjUpConv', 'nkChckRangeF', 'nkChckRange64',
'nkChckRange', 'nkStringToCString', 'nkCStringToString', 'nkPassAsOpenArray', 'nkChckRange', 'nkStringToCString', 'nkCStringToString', 'nkPassAsOpenArray',
'nkAsgn', 'nkFastAsgn', 'nkGenericParams', 'nkFormalParams', 'nkAsgn', 'nkFastAsgn', 'nkGenericParams', 'nkFormalParams',
'nkOfInherit', 'nkModule', 'nkProcDef', 'nkConverterDef', 'nkOfInherit', 'nkModule', 'nkProcDef', 'nkMethodDef',
'nkMacroDef', 'nkTemplateDef', 'nkIteratorDef', 'nkOfBranch', 'nkConverterDef', 'nkMacroDef', 'nkTemplateDef', 'nkIteratorDef',
'nkElifBranch', 'nkExceptBranch', 'nkElse', 'nkMacroStmt', 'nkOfBranch', 'nkElifBranch', 'nkExceptBranch', 'nkElse',
'nkAsmStmt', 'nkPragma', 'nkIfStmt', 'nkWhenStmt', 'nkMacroStmt', 'nkAsmStmt', 'nkPragma', 'nkIfStmt',
'nkForStmt', 'nkWhileStmt', 'nkCaseStmt', 'nkVarSection', 'nkWhenStmt', 'nkForStmt', 'nkWhileStmt', 'nkCaseStmt',
'nkConstSection', 'nkConstDef', 'nkTypeSection', 'nkTypeDef', 'nkVarSection', 'nkConstSection', 'nkConstDef', 'nkTypeSection',
'nkYieldStmt', 'nkTryStmt', 'nkFinally', 'nkRaiseStmt', 'nkTypeDef', 'nkYieldStmt', 'nkTryStmt', 'nkFinally',
'nkReturnStmt', 'nkBreakStmt', 'nkContinueStmt', 'nkBlockStmt', 'nkRaiseStmt', 'nkReturnStmt', 'nkBreakStmt', 'nkContinueStmt',
'nkDiscardStmt', 'nkStmtList', 'nkImportStmt', 'nkFromStmt', 'nkBlockStmt', 'nkDiscardStmt', 'nkStmtList', 'nkImportStmt',
'nkIncludeStmt', 'nkCommentStmt', 'nkStmtListExpr', 'nkBlockExpr', 'nkFromStmt', 'nkIncludeStmt', 'nkCommentStmt', 'nkStmtListExpr',
'nkStmtListType', 'nkBlockType', 'nkTypeOfExpr', 'nkObjectTy', 'nkBlockExpr', 'nkStmtListType', 'nkBlockType', 'nkTypeOfExpr',
'nkTupleTy', 'nkRecList', 'nkRecCase', 'nkRecWhen', 'nkObjectTy', 'nkTupleTy', 'nkRecList', 'nkRecCase',
'nkRefTy', 'nkPtrTy', 'nkVarTy', 'nkDistinctTy', 'nkRecWhen', 'nkRefTy', 'nkPtrTy', 'nkVarTy',
'nkProcTy', 'nkEnumTy', 'nkEnumFieldDef', 'nkReturnToken'); 'nkDistinctTy', 'nkProcTy', 'nkEnumTy', 'nkEnumFieldDef',
'nkReturnToken');
type type
TSymFlag = ( TSymFlag = (
sfUsed, sfStar, sfMinus, sfInInterface, sfUsed, sfStar, sfMinus, sfInInterface,
@ -200,17 +202,19 @@ type
TSymKind = ( TSymKind = (
skUnknown, skConditional, skDynLib, skParam, skUnknown, skConditional, skDynLib, skParam,
skGenericParam, skTemp, skType, skConst, skGenericParam, skTemp, skType, skConst,
skVar, skProc, skIterator, skConverter, skVar, skProc, skMethod, skIterator,
skMacro, skTemplate, skField, skEnumField, skConverter, skMacro, skTemplate, skField,
skForVar, skModule, skLabel, skStub); skEnumField, skForVar, skModule, skLabel,
skStub);
TSymKinds = set of TSymKind; TSymKinds = set of TSymKind;
const const
SymKindToStr: array [TSymKind] of string = ( SymKindToStr: array [TSymKind] of string = (
'skUnknown', 'skConditional', 'skDynLib', 'skParam', 'skUnknown', 'skConditional', 'skDynLib', 'skParam',
'skGenericParam', 'skTemp', 'skType', 'skConst', 'skGenericParam', 'skTemp', 'skType', 'skConst',
'skVar', 'skProc', 'skIterator', 'skConverter', 'skVar', 'skProc', 'skMethod', 'skIterator',
'skMacro', 'skTemplate', 'skField', 'skEnumField', 'skConverter', 'skMacro', 'skTemplate', 'skField',
'skForVar', 'skModule', 'skLabel', 'skStub'); 'skEnumField', 'skForVar', 'skModule', 'skLabel',
'skStub');
{[[[end]]]} {[[[end]]]}
type type
@ -223,9 +227,9 @@ type
// if (i+1) % 6 == 0: cog.outl("") // if (i+1) % 6 == 0: cog.outl("")
//cog.outl("m" + magics[-1]) //cog.outl("m" + magics[-1])
//]]] //]]]
mNone, mDefined, mLow, mHigh, mSizeOf, mIs, mNone, mDefined, mDefinedInScope, mLow, mHigh, mSizeOf,
mEcho, mSucc, mPred, mInc, mDec, mOrd, mIs, mEcho, mSucc, mPred, mInc, mDec,
mNew, mNewFinalize, mNewSeq, mRegisterFinalizer, mLengthOpenArray, mLengthStr, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray, mLengthStr,
mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr,
mGCref, mGCunref, mAddI, mSubI, mMulI, mDivI, mGCref, mGCunref, mAddI, mSubI, mMulI, mDivI,
mModI, mAddI64, mSubI64, mMulI64, mDivI64, mModI64, mModI, mAddI64, mSubI64, mMulI64, mDivI64, mModI64,
@ -247,21 +251,20 @@ type
mStrToStr, mEnumToStr, mAnd, mOr, mEqStr, mLeStr, mStrToStr, mEnumToStr, mAnd, mOr, mEqStr, mLeStr,
mLtStr, mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet,
mMinusSet, mSymDiffSet, mConStrStr, mConArrArr, mConArrT, mConTArr, mMinusSet, mSymDiffSet, mConStrStr, mConArrArr, mConArrT, mConTArr,
mConTT, mSlice, mAppendStrCh, mAppendStrStr, mAppendSeqElem, mAppendSeqSeq, mConTT, mSlice, mAppendStrCh, mAppendStrStr, mAppendSeqElem, mInRange,
mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq, mAssert,
mAssert, mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mNewString,
mNewString, mArray, mOpenArray, mRange, mSet, mSeq, mArray, mOpenArray, mRange, mSet, mSeq, mOrdinal,
mOrdinal, mInt, mInt8, mInt16, mInt32, mInt64, mInt, mInt8, mInt16, mInt32, mInt64, mFloat,
mFloat, mFloat32, mFloat64, mBool, mChar, mString, mFloat32, mFloat64, mBool, mChar, mString, mCstring,
mCstring, mPointer, mEmptySet, mIntSetBaseType, mNil, mExpr, mPointer, mEmptySet, mIntSetBaseType, mNil, mExpr, mStmt,
mStmt, mTypeDesc, mIsMainModule, mCompileDate, mCompileTime, mNimrodVersion, mTypeDesc, mIsMainModule, mCompileDate, mCompileTime, mNimrodVersion, mNimrodMajor,
mNimrodMajor, mNimrodMinor, mNimrodPatch, mCpuEndian, mHostOS, mHostCPU, mNimrodMinor, mNimrodPatch, mCpuEndian, mHostOS, mHostCPU, mNaN,
mNaN, mInf, mNegInf, mNLen, mNChild, mNSetChild, mInf, mNegInf, mNLen, mNChild, mNSetChild, mNAdd,
mNAdd, mNAddMultiple, mNDel, mNKind, mNIntVal, mNFloatVal, mNAddMultiple, mNDel, mNKind, mNIntVal, mNFloatVal, mNSymbol,
mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal, mNSetFloatVal, mNIdent, mNGetType, mNStrVal, mNSetIntVal, mNSetFloatVal, mNSetSymbol,
mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNNewNimNode, mNCopyNimNode, mNSetIdent, mNSetType, mNSetStrVal, mNNewNimNode, mNCopyNimNode, mNCopyNimTree,
mNCopyNimTree, mStrToIdent, mIdentToStr, mEqIdent, mNHint, mNWarning, mStrToIdent, mIdentToStr, mEqIdent, mNHint, mNWarning, mNError
mNError
//[[[end]]] //[[[end]]]
); );
@ -472,7 +475,7 @@ type
end; end;
const const
OverloadableSyms = {@set}[skProc, skIterator, skConverter]; OverloadableSyms = {@set}[skProc, skMethod, skIterator, skConverter];
const // "MagicToStr" array: const // "MagicToStr" array:
MagicToStr: array [TMagic] of string = ( MagicToStr: array [TMagic] of string = (
@ -482,9 +485,9 @@ const // "MagicToStr" array:
// if (i+1) % 6 == 0: cog.outl("") // if (i+1) % 6 == 0: cog.outl("")
//cog.outl("'%s'" % magics[-1]) //cog.outl("'%s'" % magics[-1])
//]]] //]]]
'None', 'Defined', 'Low', 'High', 'SizeOf', 'Is', 'None', 'Defined', 'DefinedInScope', 'Low', 'High', 'SizeOf',
'Echo', 'Succ', 'Pred', 'Inc', 'Dec', 'Ord', 'Is', 'Echo', 'Succ', 'Pred', 'Inc', 'Dec',
'New', 'NewFinalize', 'NewSeq', 'RegisterFinalizer', 'LengthOpenArray', 'LengthStr', 'Ord', 'New', 'NewFinalize', 'NewSeq', 'LengthOpenArray', 'LengthStr',
'LengthArray', 'LengthSeq', 'Incl', 'Excl', 'Card', 'Chr', 'LengthArray', 'LengthSeq', 'Incl', 'Excl', 'Card', 'Chr',
'GCref', 'GCunref', 'AddI', 'SubI', 'MulI', 'DivI', 'GCref', 'GCunref', 'AddI', 'SubI', 'MulI', 'DivI',
'ModI', 'AddI64', 'SubI64', 'MulI64', 'DivI64', 'ModI64', 'ModI', 'AddI64', 'SubI64', 'MulI64', 'DivI64', 'ModI64',
@ -506,21 +509,20 @@ const // "MagicToStr" array:
'StrToStr', 'EnumToStr', 'And', 'Or', 'EqStr', 'LeStr', 'StrToStr', 'EnumToStr', 'And', 'Or', 'EqStr', 'LeStr',
'LtStr', 'EqSet', 'LeSet', 'LtSet', 'MulSet', 'PlusSet', 'LtStr', 'EqSet', 'LeSet', 'LtSet', 'MulSet', 'PlusSet',
'MinusSet', 'SymDiffSet', 'ConStrStr', 'ConArrArr', 'ConArrT', 'ConTArr', 'MinusSet', 'SymDiffSet', 'ConStrStr', 'ConArrArr', 'ConArrT', 'ConTArr',
'ConTT', 'Slice', 'AppendStrCh', 'AppendStrStr', 'AppendSeqElem', 'AppendSeqSeq', 'ConTT', 'Slice', 'AppendStrCh', 'AppendStrStr', 'AppendSeqElem', 'InRange',
'InRange', 'InSet', 'Repr', 'Exit', 'SetLengthStr', 'SetLengthSeq', 'InSet', 'Repr', 'Exit', 'SetLengthStr', 'SetLengthSeq', 'Assert',
'Assert', 'Swap', 'IsNil', 'ArrToSeq', 'CopyStr', 'CopyStrLast', 'Swap', 'IsNil', 'ArrToSeq', 'CopyStr', 'CopyStrLast', 'NewString',
'NewString', 'Array', 'OpenArray', 'Range', 'Set', 'Seq', 'Array', 'OpenArray', 'Range', 'Set', 'Seq', 'Ordinal',
'Ordinal', 'Int', 'Int8', 'Int16', 'Int32', 'Int64', 'Int', 'Int8', 'Int16', 'Int32', 'Int64', 'Float',
'Float', 'Float32', 'Float64', 'Bool', 'Char', 'String', 'Float32', 'Float64', 'Bool', 'Char', 'String', 'Cstring',
'Cstring', 'Pointer', 'EmptySet', 'IntSetBaseType', 'Nil', 'Expr', 'Pointer', 'EmptySet', 'IntSetBaseType', 'Nil', 'Expr', 'Stmt',
'Stmt', 'TypeDesc', 'IsMainModule', 'CompileDate', 'CompileTime', 'NimrodVersion', 'TypeDesc', 'IsMainModule', 'CompileDate', 'CompileTime', 'NimrodVersion', 'NimrodMajor',
'NimrodMajor', 'NimrodMinor', 'NimrodPatch', 'CpuEndian', 'HostOS', 'HostCPU', 'NimrodMinor', 'NimrodPatch', 'CpuEndian', 'HostOS', 'HostCPU', 'NaN',
'NaN', 'Inf', 'NegInf', 'NLen', 'NChild', 'NSetChild', 'Inf', 'NegInf', 'NLen', 'NChild', 'NSetChild', 'NAdd',
'NAdd', 'NAddMultiple', 'NDel', 'NKind', 'NIntVal', 'NFloatVal', 'NAddMultiple', 'NDel', 'NKind', 'NIntVal', 'NFloatVal', 'NSymbol',
'NSymbol', 'NIdent', 'NGetType', 'NStrVal', 'NSetIntVal', 'NSetFloatVal', 'NIdent', 'NGetType', 'NStrVal', 'NSetIntVal', 'NSetFloatVal', 'NSetSymbol',
'NSetSymbol', 'NSetIdent', 'NSetType', 'NSetStrVal', 'NNewNimNode', 'NCopyNimNode', 'NSetIdent', 'NSetType', 'NSetStrVal', 'NNewNimNode', 'NCopyNimNode', 'NCopyNimTree',
'NCopyNimTree', 'StrToIdent', 'IdentToStr', 'EqIdent', 'NHint', 'NWarning', 'StrToIdent', 'IdentToStr', 'EqIdent', 'NHint', 'NWarning', 'NError'
'NError'
//[[[end]]] //[[[end]]]
); );
@ -553,7 +555,7 @@ const
tyFloat..tyFloat128 tyFloat..tyFloat128
]; ];
ConstantDataTypes: TTypeKinds = {@set}[tyArray, tySet, tyTuple]; ConstantDataTypes: TTypeKinds = {@set}[tyArray, tySet, tyTuple];
ExportableSymKinds = {@set}[skVar, skConst, skProc, skType, ExportableSymKinds = {@set}[skVar, skConst, skProc, skMethod, skType,
skIterator, skMacro, skTemplate, skConverter, skIterator, skMacro, skTemplate, skConverter,
skStub]; skStub];
PersistentNodeFlags: TNodeFlags = {@set}[ PersistentNodeFlags: TNodeFlags = {@set}[
@ -564,6 +566,7 @@ const
pragmasPos = 3; pragmasPos = 3;
codePos = 4; codePos = 4;
resultPos = 5; resultPos = 5;
dispatcherPos = 6;
var var
gId: int; gId: int;

View file

@ -1285,14 +1285,14 @@ begin
genAssignment(p, a, c, {@set}[]); genAssignment(p, a, c, {@set}[]);
end; end;
procedure genIs(p: BProc; n: PNode; var d: TLoc); procedure genIs(p: BProc; x: PNode; typ: PType; var d: TLoc); overload;
var var
a: TLoc; a: TLoc;
dest, t: PType; dest, t: PType;
r, nilcheck: PRope; r, nilcheck: PRope;
begin begin
initLocExpr(p, n.sons[1], a); initLocExpr(p, x, a);
dest := skipTypes(n.sons[2].typ, abstractPtrs); dest := skipTypes(typ, abstractPtrs);
useMagic(p.module, 'isObj'); useMagic(p.module, 'isObj');
r := rdLoc(a); r := rdLoc(a);
nilCheck := nil; nilCheck := nil;
@ -1313,7 +1313,12 @@ begin
else else
r := ropef('isObj($1.m_type, $2)', r := ropef('isObj($1.m_type, $2)',
[r, genTypeInfo(p.module, dest)]); [r, genTypeInfo(p.module, dest)]);
putIntoDest(p, d, n.typ, r); putIntoDest(p, d, getSysType(tyBool), r);
end;
procedure genIs(p: BProc; n: PNode; var d: TLoc); overload;
begin
genIs(p, n.sons[1], n.sons[2].typ, d);
end; end;
procedure genNewFinalize(p: BProc; e: PNode); procedure genNewFinalize(p: BProc; e: PNode);
@ -1330,14 +1335,14 @@ begin
// This is a little hack: // This is a little hack:
// XXX this is also a bug, if the finalizer expression produces side-effects // XXX this is also a bug, if the finalizer expression produces side-effects
oldModule := p.module; oldModule := p.module;
p.module := gmti; p.module := gNimDat;
InitLocExpr(p, e.sons[2], f); InitLocExpr(p, e.sons[2], f);
p.module := oldModule; p.module := oldModule;
initLoc(b, locExpr, a.t, OnHeap); initLoc(b, locExpr, a.t, OnHeap);
ti := genTypeInfo(p.module, refType); ti := genTypeInfo(p.module, refType);
appf(gmti.s[cfsTypeInit3], '$1->finalizer = (void*)$2;$n', [ appf(gNimDat.s[cfsTypeInit3], '$1->finalizer = (void*)$2;$n', [
ti, rdLoc(f)]); ti, rdLoc(f)]);
b.r := ropef('($1) newObj($2, sizeof($3))', b.r := ropef('($1) newObj($2, sizeof($3))',
[getTypeDesc(p.module, refType), ti, [getTypeDesc(p.module, refType), ti,
@ -2164,6 +2169,16 @@ begin
nkSym: begin nkSym: begin
sym := e.sym; sym := e.sym;
case sym.Kind of case sym.Kind of
skMethod: begin
if sym.ast.sons[codePos] = nil then begin
// we cannot produce code for the dispatcher yet:
fillProcLoc(sym);
genProcPrototype(p.module, sym);
end
else
genProc(p.module, sym);
putLocIntoDest(p, d, sym.loc);
end;
skProc, skConverter: begin skProc, skConverter: begin
genProc(p.module, sym); genProc(p.module, sym);
if ((sym.loc.r = nil) or (sym.loc.t = nil)) then if ((sym.loc.r = nil) or (sym.loc.t = nil)) then

View file

@ -326,7 +326,7 @@ begin
nkStrLit..nkTripleStrLit: app(s, t.sons[i].strVal); nkStrLit..nkTripleStrLit: app(s, t.sons[i].strVal);
nkSym: begin nkSym: begin
sym := t.sons[i].sym; sym := t.sons[i].sym;
if sym.kind = skProc then begin if sym.kind in [skProc, skMethod] then begin
initLocExpr(p, t.sons[i], a); initLocExpr(p, t.sons[i], a);
app(s, rdLoc(a)); app(s, rdLoc(a));
end end
@ -970,12 +970,13 @@ begin
nkCommentStmt, nkNilLit, nkIteratorDef, nkIncludeStmt, nkImportStmt, nkCommentStmt, nkNilLit, nkIteratorDef, nkIncludeStmt, nkImportStmt,
nkFromStmt, nkTemplateDef, nkMacroDef: begin end; nkFromStmt, nkTemplateDef, nkMacroDef: begin end;
nkPragma: genPragma(p, t); nkPragma: genPragma(p, t);
nkProcDef, nkConverterDef: begin nkProcDef, nkMethodDef, nkConverterDef: begin
if (t.sons[genericParamsPos] = nil) then begin if (t.sons[genericParamsPos] = nil) then begin
prc := t.sons[namePos].sym; prc := t.sons[namePos].sym;
if not (optDeadCodeElim in gGlobalOptions) and if not (optDeadCodeElim in gGlobalOptions) and
not (sfDeadCodeElim in getModule(prc).flags) not (sfDeadCodeElim in getModule(prc).flags)
or ([sfExportc, sfCompilerProc] * prc.flags = [sfExportc]) then begin or ([sfExportc, sfCompilerProc] * prc.flags = [sfExportc])
or (prc.kind = skMethod) then begin
if (t.sons[codePos] <> nil) or (lfDynamicLib in prc.loc.flags) then begin if (t.sons[codePos] <> nil) or (lfDynamicLib in prc.loc.flags) then begin
genProc(p.module, prc) genProc(p.module, prc)
end end

View file

@ -47,7 +47,7 @@ begin
if result = nil then begin if result = nil then begin
if gCmd = cmdCompileToLLVM then begin if gCmd = cmdCompileToLLVM then begin
case s.kind of case s.kind of
skProc, skConverter, skConst: result := toRope('@'+''); skProc, skMethod, skConverter, skConst: result := toRope('@'+'');
skVar: begin skVar: begin
if (sfGlobal in s.flags) then result := toRope('@'+'') if (sfGlobal in s.flags) then result := toRope('@'+'')
else result := toRope('%'+''); else result := toRope('%'+'');
@ -1026,15 +1026,15 @@ begin
tyEmpty: result := toRope('0'+''); tyEmpty: result := toRope('0'+'');
tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyPointer, tyProc, tyBool, tyChar, tyCString, tyString,
tyInt..tyFloat128, tyVar: tyInt..tyFloat128, tyVar:
genTypeInfoAuxBase(gmti, t, result, toRope('0'+'')); genTypeInfoAuxBase(gNimDat, t, result, toRope('0'+''));
tyRef, tyPtr, tySequence, tyRange: genTypeInfoAux(gmti, t, result); tyRef, tyPtr, tySequence, tyRange: genTypeInfoAux(gNimDat, t, result);
tyArrayConstr, tyArray: genArrayInfo(gmti, t, result); tyArrayConstr, tyArray: genArrayInfo(gNimDat, t, result);
tySet: genSetInfo(gmti, t, result); tySet: genSetInfo(gNimDat, t, result);
tyEnum: genEnumInfo(gmti, t, result); tyEnum: genEnumInfo(gNimDat, t, result);
tyObject: genObjectInfo(gmti, t, result); tyObject: genObjectInfo(gNimDat, t, result);
tyTuple: begin tyTuple: begin
if t.n <> nil then genObjectInfo(gmti, t, result) if t.n <> nil then genObjectInfo(gNimDat, t, result)
else genTupleInfo(gmti, t, result); else genTupleInfo(gNimDat, t, result);
end; end;
else InternalError('genTypeInfo(' + typekindToStr[t.kind] + ')'); else InternalError('genTypeInfo(' + typekindToStr[t.kind] + ')');
end end

View file

@ -19,7 +19,7 @@ uses
nsystem, ast, astalgo, strutils, nhashes, trees, platform, magicsys, nsystem, ast, astalgo, strutils, nhashes, trees, platform, magicsys,
extccomp, options, nversion, nimsets, msgs, crc, bitsets, idents, extccomp, options, nversion, nimsets, msgs, crc, bitsets, idents,
lists, types, ccgutils, nos, ntime, ropes, nmath, passes, rodread, lists, types, ccgutils, nos, ntime, ropes, nmath, passes, rodread,
wordrecg, rnimsyn, treetab; wordrecg, rnimsyn, treetab, cgmeth;
function cgenPass(): TPass; function cgenPass(): TPass;
@ -131,7 +131,7 @@ var
gPendingModules: array of BModule = {@ignore} nil {@emit @[]}; gPendingModules: array of BModule = {@ignore} nil {@emit @[]};
// list of modules that are not finished with code generation // list of modules that are not finished with code generation
gForwardedProcsCounter: int = 0; gForwardedProcsCounter: int = 0;
gmti: BModule; // generated type info: no need to initialize: defaults fit gNimDat: BModule; // generated global data
function ropeff(const cformat, llvmformat: string; function ropeff(const cformat, llvmformat: string;
const args: array of PRope): PRope; const args: array of PRope): PRope;
@ -557,7 +557,7 @@ begin
sym := magicsys.getCompilerProc(name); sym := magicsys.getCompilerProc(name);
if sym <> nil then if sym <> nil then
case sym.kind of case sym.kind of
skProc, skConverter: genProc(m, sym); skProc, skMethod, skConverter: genProc(m, sym);
skVar: genVarPrototype(m, sym); skVar: genVarPrototype(m, sym);
skType: {@discard} getTypeDesc(m, sym.typ); skType: {@discard} getTypeDesc(m, sym.typ);
else InternalError('useMagic: ' + name) else InternalError('useMagic: ' + name)
@ -730,6 +730,7 @@ begin
app(generatedProc, '}' + tnl); app(generatedProc, '}' + tnl);
end; end;
app(m.s[cfsProcs], generatedProc); app(m.s[cfsProcs], generatedProc);
//if prc.kind = skMethod then addMethodToCompile(gNimDat, prc);
end; end;
procedure genProcPrototype(m: BModule; sym: PSym); procedure genProcPrototype(m: BModule; sym: PSym);
@ -746,8 +747,9 @@ begin
end end
end end
else begin else begin
if not IntSetContainsOrIncl(m.declaredProtos, sym.id) then if not IntSetContainsOrIncl(m.declaredProtos, sym.id) then begin
appf(m.s[cfsProcHeaders], '$1;$n', [genProcHeader(m, sym)]); appf(m.s[cfsProcHeaders], '$1;$n', [genProcHeader(m, sym)]);
end
end end
end; end;
@ -836,18 +838,18 @@ function getFileHeader(const cfilenoext: string): PRope;
begin begin
if optCompileOnly in gGlobalOptions then if optCompileOnly in gGlobalOptions then
result := ropeff( result := ropeff(
'/* Generated by the Nimrod Compiler v$1 */$n' + '/* Generated by Nimrod Compiler v$1 */$n' +
'/* (c) 2009 Andreas Rumpf */$n', '/* (c) 2009 Andreas Rumpf */$n',
'; Generated by the Nimrod Compiler v$1$n' + '; Generated by Nimrod Compiler v$1$n' +
'; (c) 2009 Andreas Rumpf$n', '; (c) 2009 Andreas Rumpf$n',
[toRope(versionAsString)]) [toRope(versionAsString)])
else else
result := ropeff( result := ropeff(
'/* Generated by the Nimrod Compiler v$1 */$n' + '/* Generated by Nimrod Compiler v$1 */$n' +
'/* (c) 2009 Andreas Rumpf */$n' + '/* (c) 2009 Andreas Rumpf */$n' +
'/* Compiled for: $2, $3, $4 */$n' + '/* Compiled for: $2, $3, $4 */$n' +
'/* Command for C compiler:$n $5 */$n', '/* Command for C compiler:$n $5 */$n',
'; Generated by the Nimrod Compiler v$1$n' + '; Generated by Nimrod Compiler v$1$n' +
'; (c) 2009 Andreas Rumpf$n' + '; (c) 2009 Andreas Rumpf$n' +
'; Compiled for: $2, $3, $4$n' + '; Compiled for: $2, $3, $4$n' +
'; Command for LLVM compiler:$n $5$n', '; Command for LLVM compiler:$n $5$n',
@ -1115,15 +1117,15 @@ var
s: PSym; s: PSym;
begin begin
s := NewSym(skModule, getIdent(moduleName), nil); s := NewSym(skModule, getIdent(moduleName), nil);
gmti := rawNewModule(s, joinPath(options.projectPath, moduleName)+'.nim'); gNimDat := rawNewModule(s, joinPath(options.projectPath, moduleName)+'.nim');
addPendingModule(gmti); addPendingModule(gNimDat);
appff(mainModProcs, 'N_NOINLINE(void, $1)(void);$n', appff(mainModProcs, 'N_NOINLINE(void, $1)(void);$n',
'declare void $1() noinline$n', [getInitName(s)]); 'declare void $1() noinline$n', [getInitName(s)]);
end; end;
function myOpen(module: PSym; const filename: string): PPassContext; function myOpen(module: PSym; const filename: string): PPassContext;
begin begin
if gmti = nil then registerTypeInfoModule(); if gNimDat = nil then registerTypeInfoModule();
result := newModule(module, filename); result := newModule(module, filename);
end; end;
@ -1132,7 +1134,7 @@ function myOpenCached(module: PSym; const filename: string;
var var
cfile, cfilenoext, objFile: string; cfile, cfilenoext, objFile: string;
begin begin
if gmti = nil then registerTypeInfoModule(); if gNimDat = nil then registerTypeInfoModule();
//MessageOut('cgen.myOpenCached has been called ' + filename); //MessageOut('cgen.myOpenCached has been called ' + filename);
cfile := changeFileExt(completeCFilePath(filename), cExt); cfile := changeFileExt(completeCFilePath(filename), cExt);
cfilenoext := changeFileExt(cfile, ''); cfilenoext := changeFileExt(cfile, '');
@ -1177,7 +1179,7 @@ begin
i := 0; i := 0;
while i <= high(m.forwardedProcs) do begin while i <= high(m.forwardedProcs) do begin
// Note: ``genProc`` may add to ``m.forwardedProcs``, so we cannot use // Note: ``genProc`` may add to ``m.forwardedProcs``, so we cannot use
// a for loop here // a ``for`` loop here
prc := m.forwardedProcs[i]; prc := m.forwardedProcs[i];
if sfForward in prc.flags then InternalError(prc.info, 'still forwarded'); if sfForward in prc.flags then InternalError(prc.info, 'still forwarded');
genProcNoForward(m, prc); genProcNoForward(m, prc);
@ -1214,6 +1216,7 @@ function myClose(b: PPassContext; n: PNode): PNode;
var var
m: BModule; m: BModule;
i: int; i: int;
disp: PNode;
begin begin
result := n; result := n;
if b = nil then exit; if b = nil then exit;
@ -1227,6 +1230,8 @@ begin
not (sfDeadCodeElim in m.module.flags) then not (sfDeadCodeElim in m.module.flags) then
finishModule(m); finishModule(m);
if sfMainModule in m.module.flags then begin if sfMainModule in m.module.flags then begin
disp := generateMethodDispatchers();
for i := 0 to sonsLen(disp)-1 do genProcAux(gNimDat, disp.sons[i].sym);
genMainProc(m); genMainProc(m);
// we need to process the transitive closure because recursive module // we need to process the transitive closure because recursive module
// deps are allowed (and the system module is processed in the wrong // deps are allowed (and the system module is processed in the wrong
@ -1241,7 +1246,7 @@ begin
not (sfDeadCodeElim in m.module.flags) then not (sfDeadCodeElim in m.module.flags) then
writeModule(m); writeModule(m);
if sfMainModule in m.module.flags then if sfMainModule in m.module.flags then
writeMapping(gMapping); writeMapping(gMapping);
end; end;
function cgenPass(): TPass; function cgenPass(): TPass;

269
nim/cgmeth.pas Normal file
View file

@ -0,0 +1,269 @@
//
//
// The Nimrod Compiler
// (c) Copyright 2009 Andreas Rumpf
//
// See the file "copying.txt", included in this
// distribution, for details about the copyright.
//
unit cgmeth;
// This module implements code generation for multi methods.
interface
{$include 'config.inc'}
uses
sysutils, nsystem,
options, ast, astalgo, msgs, idents, rnimsyn, types, magicsys;
procedure methodDef(s: PSym);
function methodCall(n: PNode): PNode;
function generateMethodDispatchers(): PNode;
implementation
const
skipPtrs = {@set}[tyVar, tyPtr, tyRef, tyGenericInst];
function genConv(n: PNode; d: PType; downcast: bool): PNode;
var
dest, source: PType;
diff: int;
begin
dest := skipTypes(d, abstractPtrs);
source := skipTypes(n.typ, abstractPtrs);
if (source.kind = tyObject) and (dest.kind = tyObject) then begin
diff := inheritanceDiff(dest, source);
if diff = high(int) then InternalError(n.info, 'cgmeth.genConv');
if diff < 0 then begin
result := newNodeIT(nkObjUpConv, n.info, d);
addSon(result, n);
if downCast then
InternalError(n.info, 'cgmeth.genConv: no upcast allowed');
end
else if diff > 0 then begin
result := newNodeIT(nkObjDownConv, n.info, d);
addSon(result, n);
if not downCast then
InternalError(n.info, 'cgmeth.genConv: no downcast allowed');
end
else result := n
end
else result := n
end;
function methodCall(n: PNode): PNode;
var
disp: PSym;
i: int;
begin
result := n;
disp := lastSon(result.sons[0].sym.ast).sym;
result.sons[0].sym := disp;
for i := 1 to sonsLen(result)-1 do
result.sons[i] := genConv(result.sons[i], disp.typ.sons[i], true)
end;
var
gMethods: array of TSymSeq;
function sameMethodBucket(a, b: PSym): bool;
var
i: int;
aa, bb: PType;
begin
result := false;
if a.name.id <> b.name.id then exit;
if sonsLen(a.typ) <> sonsLen(b.typ) then exit;
// check for return type:
if not sameTypeOrNil(a.typ.sons[0], b.typ.sons[0]) then exit;
for i := 1 to sonsLen(a.typ)-1 do begin
aa := a.typ.sons[i];
bb := b.typ.sons[i];
while true do begin
aa := skipTypes(aa, {@set}[tyGenericInst]);
bb := skipTypes(bb, {@set}[tyGenericInst]);
if (aa.kind = bb.kind) and (aa.kind in [tyVar, tyPtr, tyRef]) then begin
aa := aa.sons[0];
bb := bb.sons[0];
end
else
break
end;
if sameType(aa, bb)
or (aa.kind = tyObject) and (bb.kind = tyObject)
and (inheritanceDiff(bb, aa) < 0) then begin end
else exit;
end;
result := true
end;
procedure methodDef(s: PSym);
var
i, L, q: int;
disp: PSym;
begin
L := length(gMethods);
for i := 0 to L-1 do begin
if sameMethodBucket(gMethods[i][0], s) then begin
{@ignore}
q := length(gMethods[i]);
setLength(gMethods[i], q+1);
gMethods[i][q] := s;
{@emit
add(gMethods[i], s);
}
// store a symbol to the dispatcher:
addSon(s.ast, lastSon(gMethods[i][0].ast));
exit
end
end;
{@ignore}
setLength(gMethods, L+1);
setLength(gMethods[L], 1);
gMethods[L][0] := s;
{@emit
add(gMethods, @[s]);
}
// create a new dispatcher:
disp := copySym(s);
disp.typ := copyType(disp.typ, disp.typ.owner, false);
if disp.typ.callConv = ccInline then disp.typ.callConv := ccDefault;
disp.ast := copyTree(s.ast);
disp.ast.sons[codePos] := nil;
if s.typ.sons[0] <> nil then
disp.ast.sons[resultPos].sym := copySym(s.ast.sons[resultPos].sym);
addSon(s.ast, newSymNode(disp));
end;
function relevantCol(methods: TSymSeq; col: int): bool;
var
t: PType;
i: int;
begin
// returns true iff the position is relevant
t := methods[0].typ.sons[col];
result := false;
if skipTypes(t, skipPtrs).kind = tyObject then
for i := 1 to high(methods) do
if not SameType(methods[i].typ.sons[col], t) then begin
result := true; exit
end
end;
function cmpSignatures(a, b: PSym; const relevantCols: TIntSet): int;
var
col, d: int;
aa, bb: PType;
begin
result := 0;
for col := 1 to sonsLen(a.typ)-1 do
if intSetContains(relevantCols, col) then begin
aa := skipTypes(a.typ.sons[col], skipPtrs);
bb := skipTypes(b.typ.sons[col], skipPtrs);
d := inheritanceDiff(aa, bb);
if (d <> high(int)) then begin
result := d; exit
end
end
end;
procedure sortBucket(var a: TSymSeq; const relevantCols: TIntSet);
// we use shellsort here; fast and simple
var
N, i, j, h: int;
v: PSym;
begin
N := length(a);
h := 1; repeat h := 3*h+1; until h > N;
repeat
h := h div 3;
for i := h to N-1 do begin
v := a[i]; j := i;
while cmpSignatures(a[j-h], v, relevantCols) >= 0 do begin
a[j] := a[j-h]; j := j - h;
if j < h then break
end;
a[j] := v;
end;
until h = 1
end;
function genDispatcher(methods: TSymSeq; const relevantCols: TIntSet): PSym;
var
disp, cond, call, ret, a, isn: PNode;
base, curr, ands, iss: PSym;
meth, col, paramLen: int;
begin
base := lastSon(methods[0].ast).sym;
result := base;
paramLen := sonsLen(base.typ);
disp := newNodeI(nkIfStmt, base.info);
ands := getSysSym('and');
iss := getSysSym('is');
for meth := 0 to high(methods) do begin
curr := methods[meth];
// generate condition:
cond := nil;
for col := 1 to paramLen-1 do begin
if IntSetContains(relevantCols, col) then begin
isn := newNodeIT(nkCall, base.info, getSysType(tyBool));
addSon(isn, newSymNode(iss));
addSon(isn, newSymNode(base.typ.n.sons[col].sym));
addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col]));
if cond <> nil then begin
a := newNodeIT(nkCall, base.info, getSysType(tyBool));
addSon(a, newSymNode(ands));
addSon(a, cond);
addSon(a, isn);
cond := a
end
else
cond := isn
end
end;
// generate action:
call := newNodeI(nkCall, base.info);
addSon(call, newSymNode(curr));
for col := 1 to paramLen-1 do begin
addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym),
curr.typ.sons[col], false));
end;
if base.typ.sons[0] <> nil then begin
a := newNodeI(nkAsgn, base.info);
addSon(a, newSymNode(base.ast.sons[resultPos].sym));
addSon(a, call);
ret := newNodeI(nkReturnStmt, base.info);
addSon(ret, a);
end
else
ret := call;
a := newNodeI(nkElifBranch, base.info);
addSon(a, cond);
addSon(a, ret);
addSon(disp, a);
end;
result.ast.sons[codePos] := disp;
end;
function generateMethodDispatchers(): PNode;
var
bucket, col: int;
relevantCols: TIntSet;
begin
result := newNode(nkStmtList);
for bucket := 0 to length(gMethods)-1 do begin
IntSetInit(relevantCols);
for col := 1 to sonsLen(gMethods[bucket][0].typ)-1 do
if relevantCol(gMethods[bucket], col) then IntSetIncl(relevantCols, col);
sortBucket(gMethods[bucket], relevantCols);
addSon(result, newSymNode(genDispatcher(gMethods[bucket], relevantCols)));
end
end;
initialization
{@emit gMethods := @[]; }
end.

View file

@ -1036,6 +1036,7 @@ begin
case n.kind of case n.kind of
nkCommentStmt: app(d.modDesc, genComment(d, n)); nkCommentStmt: app(d.modDesc, genComment(d, n));
nkProcDef: genItem(d, n, n.sons[namePos], skProc); nkProcDef: genItem(d, n, n.sons[namePos], skProc);
nkMethodDef: genItem(d, n, n.sons[namePos], skMethod);
nkIteratorDef: genItem(d, n, n.sons[namePos], skIterator); nkIteratorDef: genItem(d, n, n.sons[namePos], skIterator);
nkMacroDef: genItem(d, n, n.sons[namePos], skMacro); nkMacroDef: genItem(d, n, n.sons[namePos], skMacro);
nkTemplateDef: genItem(d, n, n.sons[namePos], skTemplate); nkTemplateDef: genItem(d, n, n.sons[namePos], skTemplate);

View file

@ -1717,7 +1717,7 @@ begin
nkTypeSection, nkCommentStmt, nkIteratorDef, nkTypeSection, nkCommentStmt, nkIteratorDef,
nkIncludeStmt, nkImportStmt, nkIncludeStmt, nkImportStmt,
nkFromStmt, nkTemplateDef, nkMacroDef, nkPragma: begin end; nkFromStmt, nkTemplateDef, nkMacroDef, nkPragma: begin end;
nkProcDef, nkConverterDef: begin nkProcDef, nkMethodDef, nkConverterDef: begin
if (n.sons[genericParamsPos] = nil) then begin if (n.sons[genericParamsPos] = nil) then begin
prc := n.sons[namePos].sym; prc := n.sons[namePos].sym;
if (n.sons[codePos] <> nil) and not (lfNoDecl in prc.loc.flags) then if (n.sons[codePos] <> nil) and not (lfNoDecl in prc.loc.flags) then

View file

@ -938,23 +938,6 @@ begin
result := emptyNode; result := emptyNode;
end; end;
function evalAppendSeqSeq(c: PEvalContext; n: PNode): PNode;
var
a, b: PNode;
i: int;
begin
result := evalAux(c, n.sons[1]);
if result.kind = nkExceptBranch then exit;
a := result;
result := evalAux(c, n.sons[2]);
if result.kind = nkExceptBranch then exit;
b := result;
if a.kind = nkBracket then
for i := 0 to sonsLen(b)-1 do addSon(a, copyTree(b.sons[i]))
else InternalError(n.info, 'evalAppendSeqSeq');
result := emptyNode;
end;
function evalRepr(c: PEvalContext; n: PNode): PNode; function evalRepr(c: PEvalContext; n: PNode): PNode;
begin begin
result := evalAux(c, n.sons[1]); result := evalAux(c, n.sons[1]);
@ -993,7 +976,6 @@ begin
mAppendStrCh: result := evalAppendStrCh(c, n); mAppendStrCh: result := evalAppendStrCh(c, n);
mAppendStrStr: result := evalAppendStrStr(c, n); mAppendStrStr: result := evalAppendStrStr(c, n);
mAppendSeqElem: result := evalAppendSeqElem(c, n); mAppendSeqElem: result := evalAppendSeqElem(c, n);
mAppendSeqSeq: result := evalAppendSeqSeq(c, n);
mNLen: begin mNLen: begin
result := evalAux(c, n.sons[1]); result := evalAux(c, n.sons[1]);
@ -1338,7 +1320,7 @@ begin
end end
end end
end; end;
nkProcDef, nkMacroDef, nkCommentStmt, nkPragma, nkTypeSection, nkProcDef, nkMethodDef, nkMacroDef, nkCommentStmt, nkPragma, nkTypeSection,
nkTemplateDef, nkConstSection, nkIteratorDef, nkConverterDef, nkTemplateDef, nkConstSection, nkIteratorDef, nkConverterDef,
nkIncludeStmt, nkImportStmt, nkFromStmt: begin end; nkIncludeStmt, nkImportStmt, nkFromStmt: begin end;
nkIdentDefs, nkCast, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr, nkIdentDefs, nkCast, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr,

View file

@ -264,18 +264,37 @@ begin
while not (g.buf[pos] in [#0, #10, #13]) do inc(pos); while not (g.buf[pos] in [#0, #10, #13]) do inc(pos);
end; end;
'a'..'z', 'A'..'Z', '_', #128..#255: begin 'a'..'z', 'A'..'Z', '_', #128..#255: begin
if (g.buf[pos+1] = '"') and (g.buf[pos] in ['r', 'R']) then begin id := '';
g.kind := gtRawData; while g.buf[pos] in scanner.SymChars+['_'] do begin
inc(pos, 2); addChar(id, g.buf[pos]);
while not (g.buf[pos] in [#0, '"', #10, #13]) do inc(pos); inc(pos)
if g.buf[pos] = '"' then inc(pos); end;
if (g.buf[pos] = '"') then begin
if (g.buf[pos+1] = '"') and (g.buf[pos+2] = '"') then begin
inc(pos, 3);
g.kind := gtLongStringLit;
while true do begin
case g.buf[pos] of
#0: break;
'"': begin
inc(pos);
if (g.buf[pos] = '"') and (g.buf[pos+1] = '"') then begin
inc(pos, 2);
break
end
end;
else inc(pos);
end
end
end
else begin
g.kind := gtRawData;
inc(pos);
while not (g.buf[pos] in [#0, '"', #10, #13]) do inc(pos);
if g.buf[pos] = '"' then inc(pos);
end
end end
else begin else begin
id := '';
while g.buf[pos] in scanner.SymChars+['_'] do begin
addChar(id, g.buf[pos]);
inc(pos)
end;
g.kind := nimGetKeyword(id); g.kind := nimGetKeyword(id);
end end
end; end;

View file

@ -113,7 +113,7 @@ begin
InternalError(ident.info, 'importSymbol: 2'); InternalError(ident.info, 'importSymbol: 2');
// for an enumeration we have to add all identifiers // for an enumeration we have to add all identifiers
case s.Kind of case s.Kind of
skProc, skIterator, skMacro, skTemplate, skConverter: begin skProc, skMethod, skIterator, skMacro, skTemplate, skConverter: begin
// for a overloadable syms add all overloaded routines // for a overloadable syms add all overloaded routines
e := InitIdentIter(it, fromMod.tab, s.name); e := InitIdentIter(it, fromMod.tab, s.name);
while e <> nil do begin while e <> nil do begin
@ -155,6 +155,8 @@ begin
for i := 0 to sonsLen(n)-1 do begin for i := 0 to sonsLen(n)-1 do begin
f := getModuleFile(n.sons[i]); f := getModuleFile(n.sons[i]);
m := gImportModule(f); m := gImportModule(f);
if sfDeprecated in m.flags then
liMessage(n.sons[i].info, warnDeprecated, m.name.s);
// ``addDecl`` needs to be done before ``importAllSymbols``! // ``addDecl`` needs to be done before ``importAllSymbols``!
addDecl(c, m); // add symbol to symbol table of module addDecl(c, m); // add symbol to symbol table of module
importAllSymbols(c, m); importAllSymbols(c, m);

View file

@ -54,7 +54,7 @@ implementation
function getSymRepr(s: PSym): string; function getSymRepr(s: PSym): string;
begin begin
case s.kind of case s.kind of
skProc, skConverter, skIterator: result := getProcHeader(s); skProc, skMethod, skConverter, skIterator: result := getProcHeader(s);
else result := s.name.s else result := s.name.s
end end
end; end;
@ -72,7 +72,7 @@ begin
liMessage(s.info, errImplOfXexpected, getSymRepr(s)) liMessage(s.info, errImplOfXexpected, getSymRepr(s))
else if ([sfUsed, sfInInterface] * s.flags = []) and else if ([sfUsed, sfInInterface] * s.flags = []) and
(optHints in s.options) then // BUGFIX: check options in s! (optHints in s.options) then // BUGFIX: check options in s!
if not (s.kind in [skForVar, skParam, skUnknown]) then if not (s.kind in [skForVar, skParam, skMethod, skUnknown]) then
liMessage(s.info, hintXDeclaredButNotUsed, getSymRepr(s)); liMessage(s.info, hintXDeclaredButNotUsed, getSymRepr(s));
s := NextIter(it, tab.stack[tab.tos-1]); s := NextIter(it, tab.stack[tab.tos-1]);
end; end;

View file

@ -30,6 +30,8 @@ procedure registerCompilerProc(s: PSym);
procedure InitSystem(var tab: TSymTab); procedure InitSystem(var tab: TSymTab);
procedure FinishSystem(const tab: TStrTable); procedure FinishSystem(const tab: TStrTable);
function getSysSym(const name: string): PSym;
implementation implementation
var var
@ -48,14 +50,16 @@ begin
result.align := size; result.align := size;
end; end;
function sysTypeFromName(const name: string): PType; function getSysSym(const name: string): PSym;
var
s: PSym;
begin begin
s := StrTableGet(systemModule.tab, getIdent(name)); result := StrTableGet(systemModule.tab, getIdent(name));
if s = nil then rawMessage(errSystemNeeds, name); if result = nil then rawMessage(errSystemNeeds, name);
if s.kind = skStub then loadStub(s); if result.kind = skStub then loadStub(result);
result := s.typ; end;
function sysTypeFromName(const name: string): PType;
begin
result := getSysSym(name).typ;
end; end;
function getSysType(const kind: TTypeKind): PType; function getSysType(const kind: TTypeKind): PType;

View file

@ -1,7 +1,7 @@
// //
// //
// The Nimrod Compiler // The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf // (c) Copyright 2009 Andreas Rumpf
// //
// See the file "copying.txt", included in this // See the file "copying.txt", included in this
// distribution, for details about the copyright. // distribution, for details about the copyright.

View file

@ -31,10 +31,10 @@ const
//cog.outl('VersionMinor = %s;' % ver[1]) //cog.outl('VersionMinor = %s;' % ver[1])
//cog.outl('VersionPatch = %s;' % ver[2]) //cog.outl('VersionPatch = %s;' % ver[2])
//]]] //]]]
VersionAsString = '0.8.0'; VersionAsString = '0.8.1';
VersionMajor = 0; VersionMajor = 0;
VersionMinor = 8; VersionMinor = 8;
VersionPatch = 0; VersionPatch = 1;
//[[[[end]]]] //[[[[end]]]]
implementation implementation

View file

@ -61,7 +61,7 @@ const
('len', 'length'), ('len', 'length'),
('setlength', 'setlen') ('setlength', 'setlen')
); );
nimReplacements: array [1..32] of TReplaceTuple = ( nimReplacements: array [1..34] of TReplaceTuple = (
('nimread', 'read'), ('nimread', 'read'),
('nimwrite', 'write'), ('nimwrite', 'write'),
('nimclosefile', 'close'), ('nimclosefile', 'close'),
@ -96,7 +96,10 @@ const
('snil', 'nil'), ('snil', 'nil'),
('tostringf', '$'+''), ('tostringf', '$'+''),
('ttextfile', 'tfile'), ('ttextfile', 'tfile'),
('tbinaryfile', 'tfile') {, ('tbinaryfile', 'tfile'),
('strstart', '0'+''),
('nl', '"\n"')
{,
('NL', '"\n"'), ('NL', '"\n"'),
('tabulator', '''\t'''), ('tabulator', '''\t'''),
('esc', '''\e'''), ('esc', '''\e'''),

View file

@ -56,7 +56,7 @@ begin
nkStmtList: begin nkStmtList: begin
for i := 0 to sonsLen(n)-1 do {@discard} cleanup(c, n.sons[i]); for i := 0 to sonsLen(n)-1 do {@discard} cleanup(c, n.sons[i]);
end; end;
nkProcDef: begin nkProcDef, nkMethodDef: begin
if (n.sons[namePos].kind = nkSym) then begin if (n.sons[namePos].kind = nkSym) then begin
s := n.sons[namePos].sym; s := n.sons[namePos].sym;
if not (sfDeadCodeElim in getModule(s).flags) and if not (sfDeadCodeElim in getModule(s).flags) and

View file

@ -67,7 +67,7 @@ implementation
function astNeeded(s: PSym): bool; function astNeeded(s: PSym): bool;
begin begin
if (s.kind = skProc) if (s.kind in [skMethod, skProc])
and ([sfCompilerProc, sfCompileTime] * s.flags = []) and ([sfCompilerProc, sfCompileTime] * s.flags = [])
and (s.typ.callConv <> ccInline) and (s.typ.callConv <> ccInline)
and (s.ast.sons[genericParamsPos] = nil) then and (s.ast.sons[genericParamsPos] = nil) then

View file

@ -889,7 +889,7 @@ end;
function parseImportStmt(var p: TParser): PNode; function parseImportStmt(var p: TParser): PNode;
var var
a, b: PNode; a: PNode;
begin begin
result := newNodeP(nkImportStmt, p); result := newNodeP(nkImportStmt, p);
getTok(p); // skip `import` getTok(p); // skip `import`
@ -1535,9 +1535,9 @@ begin
end end
end; end;
function parseRecordPart(var p: TParser): PNode; forward; function parseObjectPart(var p: TParser): PNode; forward;
function parseRecordWhen(var p: TParser): PNode; function parseObjectWhen(var p: TParser): PNode;
var var
branch: PNode; branch: PNode;
begin begin
@ -1549,7 +1549,7 @@ begin
addSon(branch, parseExpr(p)); addSon(branch, parseExpr(p));
eat(p, tkColon); eat(p, tkColon);
skipComment(p, branch); skipComment(p, branch);
addSon(branch, parseRecordPart(p)); addSon(branch, parseObjectPart(p));
skipComment(p, branch); skipComment(p, branch);
addSon(result, branch); addSon(result, branch);
if p.tok.tokType <> tkElif then break if p.tok.tokType <> tkElif then break
@ -1558,12 +1558,12 @@ begin
branch := newNodeP(nkElse, p); branch := newNodeP(nkElse, p);
eat(p, tkElse); eat(p, tkColon); eat(p, tkElse); eat(p, tkColon);
skipComment(p, branch); skipComment(p, branch);
addSon(branch, parseRecordPart(p)); addSon(branch, parseObjectPart(p));
addSon(result, branch); addSon(result, branch);
end end
end; end;
function parseRecordCase(var p: TParser): PNode; function parseObjectCase(var p: TParser): PNode;
var var
a, b: PNode; a, b: PNode;
begin begin
@ -1591,13 +1591,13 @@ begin
else break; else break;
end; end;
skipComment(p, b); skipComment(p, b);
addSon(b, parseRecordPart(p)); addSon(b, parseObjectPart(p));
addSon(result, b); addSon(result, b);
if b.kind = nkElse then break; if b.kind = nkElse then break;
end end
end; end;
function parseRecordPart(var p: TParser): PNode; function parseObjectPart(var p: TParser): PNode;
begin begin
case p.tok.tokType of case p.tok.tokType of
tkInd: begin tkInd: begin
@ -1608,7 +1608,7 @@ begin
case p.tok.tokType of case p.tok.tokType of
tkSad: getTok(p); tkSad: getTok(p);
tkCase, tkWhen, tkSymbol, tkAccent, tkNil: begin tkCase, tkWhen, tkSymbol, tkAccent, tkNil: begin
addSon(result, parseRecordPart(p)); addSon(result, parseObjectPart(p));
end; end;
tkDed: begin getTok(p); break end; tkDed: begin getTok(p); break end;
tkEof: break; tkEof: break;
@ -1620,8 +1620,8 @@ begin
end; end;
popInd(p.lex^); popInd(p.lex^);
end; end;
tkWhen: result := parseRecordWhen(p); tkWhen: result := parseObjectWhen(p);
tkCase: result := parseRecordCase(p); tkCase: result := parseObjectCase(p);
tkSymbol, tkAccent: begin tkSymbol, tkAccent: begin
result := parseIdentColonEquals(p, {@set}[withPragma]); result := parseIdentColonEquals(p, {@set}[withPragma]);
skipComment(p, result); skipComment(p, result);
@ -1634,11 +1634,11 @@ begin
end end
end; end;
function parseRecordOrObject(var p: TParser; kind: TNodeKind): PNode; function parseObject(var p: TParser): PNode;
var var
a: PNode; a: PNode;
begin begin
result := newNodeP(kind, p); result := newNodeP(nkObjectTy, p);
getTok(p); getTok(p);
if p.tok.tokType = tkCurlyDotLe then addSon(result, parsePragma(p)) if p.tok.tokType = tkCurlyDotLe then addSon(result, parsePragma(p))
else addSon(result, nil); else addSon(result, nil);
@ -1646,14 +1646,11 @@ begin
a := newNodeP(nkOfInherit, p); a := newNodeP(nkOfInherit, p);
getTok(p); getTok(p);
addSon(a, parseTypeDesc(p)); addSon(a, parseTypeDesc(p));
if kind = nkObjectTy then addSon(result, a);
addSon(result, a)
else
parMessage(p, errInheritanceOnlyWithNonFinalObjects);
end end
else addSon(result, nil); else addSon(result, nil);
skipComment(p, result); skipComment(p, result);
addSon(result, parseRecordPart(p)); addSon(result, parseObjectPart(p));
end; end;
function parseDistinct(var p: TParser): PNode; function parseDistinct(var p: TParser): PNode;
@ -1675,7 +1672,7 @@ begin
if p.tok.tokType = tkEquals then begin if p.tok.tokType = tkEquals then begin
getTok(p); optInd(p, result); getTok(p); optInd(p, result);
case p.tok.tokType of case p.tok.tokType of
tkObject: a := parseRecordOrObject(p, nkObjectTy); tkObject: a := parseObject(p);
tkEnum: a := parseEnum(p); tkEnum: a := parseEnum(p);
tkDistinct: a := parseDistinct(p); tkDistinct: a := parseDistinct(p);
else a := parseTypeDesc(p); else a := parseTypeDesc(p);
@ -1754,6 +1751,7 @@ begin
tkBlock: result := parseBlock(p); tkBlock: result := parseBlock(p);
tkAsm: result := parseAsm(p); tkAsm: result := parseAsm(p);
tkProc: result := parseRoutine(p, nkProcDef); tkProc: result := parseRoutine(p, nkProcDef);
tkMethod: result := parseRoutine(p, nkMethodDef);
tkIterator: result := parseRoutine(p, nkIteratorDef); tkIterator: result := parseRoutine(p, nkIteratorDef);
tkMacro: result := parseRoutine(p, nkMacroDef); tkMacro: result := parseRoutine(p, nkMacroDef);
tkTemplate: result := parseRoutine(p, nkTemplateDef); tkTemplate: result := parseRoutine(p, nkTemplateDef);

View file

@ -30,6 +30,7 @@ const
wCppMethod, wDeprecated, wVarargs, wCompileTime, wMerge, wCppMethod, wDeprecated, wVarargs, wCompileTime, wMerge,
wBorrow]; wBorrow];
converterPragmas = procPragmas; converterPragmas = procPragmas;
methodPragmas = procPragmas;
macroPragmas = {@set}[FirstCallConv..LastCallConv, macroPragmas = {@set}[FirstCallConv..LastCallConv,
wImportc, wExportc, wNodecl, wMagic, wNosideEffect, wImportc, wExportc, wNodecl, wMagic, wNosideEffect,
wCompilerProc, wDeprecated, wTypeCheck]; wCompilerProc, wDeprecated, wTypeCheck];
@ -42,7 +43,7 @@ const
wHint, wWarning, wError, wFatal, wDefine, wUndef, wHint, wWarning, wError, wFatal, wDefine, wUndef,
wCompile, wLink, wLinkSys, wPure, wCompile, wLink, wLinkSys, wPure,
wPush, wPop, wBreakpoint, wCheckpoint, wPush, wPop, wBreakpoint, wCheckpoint,
wPassL, wPassC, wDeadCodeElim]; wPassL, wPassC, wDeadCodeElim, wDeprecated];
lambdaPragmas = {@set}[FirstCallConv..LastCallConv, lambdaPragmas = {@set}[FirstCallConv..LastCallConv,
wImportc, wExportc, wNodecl, wNosideEffect, wSideEffect, wImportc, wExportc, wNodecl, wNosideEffect, wSideEffect,
wNoreturn, wDynLib, wHeader, wPure, wDeprecated]; wNoreturn, wDynLib, wHeader, wPure, wDeprecated];
@ -545,7 +546,8 @@ begin
end; end;
wDeprecated: begin wDeprecated: begin
noVal(it); noVal(it);
include(sym.flags, sfDeprecated); if sym <> nil then include(sym.flags, sfDeprecated)
else include(c.module.flags, sfDeprecated);
end; end;
wVarargs: begin wVarargs: begin
noVal(it); noVal(it);

View file

@ -1235,6 +1235,10 @@ begin
putWithSpace(g, tkProc, 'proc'); putWithSpace(g, tkProc, 'proc');
gproc(g, n); gproc(g, n);
end; end;
nkMethodDef: begin
putWithSpace(g, tkMethod, 'method');
gproc(g, n);
end;
nkIteratorDef: begin nkIteratorDef: begin
putWithSpace(g, tkIterator, 'iterator'); putWithSpace(g, tkIterator, 'iterator');
gproc(g, n); gproc(g, n);

View file

@ -519,7 +519,7 @@ begin
s := n.sons[namePos].sym; s := n.sons[namePos].sym;
addInterfaceSym(w, s); addInterfaceSym(w, s);
end; end;
nkProcDef, nkIteratorDef, nkConverterDef: begin nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef: begin
s := n.sons[namePos].sym; s := n.sons[namePos].sym;
if s = nil then InternalError(n.info, 'rodwrite.process'); if s = nil then InternalError(n.info, 'rodwrite.process');
if (n.sons[codePos] <> nil) or (s.magic <> mNone) if (n.sons[codePos] <> nil) or (s.magic <> mNone)

View file

@ -56,6 +56,12 @@ begin
result.info := n.info; result.info := n.info;
end; end;
procedure markUsed(n: PNode; s: PSym);
begin
include(s.flags, sfUsed);
if sfDeprecated in s.flags then liMessage(n.info, warnDeprecated, s.name.s);
end;
function semIdentVis(c: PContext; kind: TSymKind; n: PNode; function semIdentVis(c: PContext; kind: TSymKind; n: PNode;
const allowed: TSymFlags): PSym; forward; const allowed: TSymFlags): PSym; forward;
// identifier with visability // identifier with visability
@ -130,7 +136,7 @@ var
p: PEvalContext; p: PEvalContext;
s: PStackFrame; s: PStackFrame;
begin begin
include(sym.flags, sfUsed); markUsed(n, sym);
p := newEvalContext(c.module, '', false); p := newEvalContext(c.module, '', false);
s := newStackFrame(); s := newStackFrame();
s.call := n; s.call := n;
@ -179,7 +185,8 @@ begin
it := c.generics.sons[i].sons[1]; it := c.generics.sons[i].sons[1];
if it.kind <> nkSym then InternalError('addCodeForGenerics'); if it.kind <> nkSym then InternalError('addCodeForGenerics');
prc := it.sym; prc := it.sym;
if (prc.kind in [skProc, skConverter]) and (prc.magic = mNone) then begin if (prc.kind in [skProc, skMethod, skConverter])
and (prc.magic = mNone) then begin
if (prc.ast = nil) or (prc.ast.sons[codePos] = nil) then if (prc.ast = nil) or (prc.ast.sons[codePos] = nil) then
InternalError(prc.info, 'no code for ' + prc.name.s); InternalError(prc.info, 'no code for ' + prc.name.s);
addSon(n, prc.ast); addSon(n, prc.ast);

View file

@ -13,7 +13,7 @@
function semTemplateExpr(c: PContext; n: PNode; s: PSym; function semTemplateExpr(c: PContext; n: PNode; s: PSym;
semCheck: bool = true): PNode; semCheck: bool = true): PNode;
begin begin
include(s.flags, sfUsed); markUsed(n, s);
pushInfoContext(n.info); pushInfoContext(n.info);
result := evalTemplate(c, n, s); result := evalTemplate(c, n, s);
if semCheck then if semCheck then
@ -47,8 +47,6 @@ var
diff: int; diff: int;
begin begin
diff := inheritanceDiff(castDest, src); diff := inheritanceDiff(castDest, src);
//if diff = 0 then
// liMessage(info, hintConvToBaseNotNeeded)
if diff = high(int) then if diff = high(int) then
liMessage(info, errGenerated, liMessage(info, errGenerated,
format(MsgKindToString(errIllegalConvFromXtoY), format(MsgKindToString(errIllegalConvFromXtoY),
@ -136,7 +134,7 @@ begin
else begin else begin
for i := 0 to sonsLen(op)-1 do begin for i := 0 to sonsLen(op)-1 do begin
if sameType(result.typ, op.sons[i].typ) then begin if sameType(result.typ, op.sons[i].typ) then begin
include(op.sons[i].sym.flags, sfUsed); markUsed(n, op.sons[i].sym);
result := op.sons[i]; exit result := op.sons[i]; exit
end end
end; end;
@ -453,8 +451,7 @@ const
FakeVarParams = {@set}[mNew, mNewFinalize, mInc, ast.mDec, mIncl, FakeVarParams = {@set}[mNew, mNewFinalize, mInc, ast.mDec, mIncl,
mExcl, mSetLengthStr, mSetLengthSeq, mExcl, mSetLengthStr, mSetLengthSeq,
mAppendStrCh, mAppendStrStr, mSwap, mAppendStrCh, mAppendStrStr, mSwap,
mAppendSeqElem, mAppendSeqSeq, mAppendSeqElem, mNewSeq];
mNewSeq];
var var
i: int; i: int;
t: PType; t: PType;
@ -474,7 +471,7 @@ var
callee: PSym; callee: PSym;
begin begin
if not (efWantIterator in flags) then if not (efWantIterator in flags) then
result := semDirectCall(c, n, {@set}[skProc, skConverter]) result := semDirectCall(c, n, {@set}[skProc, skMethod, skConverter])
else else
result := semDirectCall(c, n, {@set}[skIterator]); result := semDirectCall(c, n, {@set}[skIterator]);
if result <> nil then begin if result <> nil then begin
@ -586,17 +583,23 @@ begin
result := n; result := n;
end; end;
function LookUpForDefined(c: PContext; n: PNode): PSym; function LookUpForDefined(c: PContext; n: PNode; onlyCurrentScope: bool): PSym;
var var
m: PSym; m: PSym;
ident: PIdent; ident: PIdent;
begin begin
case n.kind of case n.kind of
nkIdent: result := SymtabGet(c.Tab, n.ident); // no need for stub loading nkIdent: begin
if onlyCurrentScope then
result := SymtabLocalGet(c.tab, n.ident)
else
result := SymtabGet(c.Tab, n.ident); // no need for stub loading
end;
nkDotExpr, nkQualified: begin nkDotExpr, nkQualified: begin
checkSonsLen(n, 2);
result := nil; result := nil;
m := LookupForDefined(c, n.sons[0]); if onlyCurrentScope then exit;
checkSonsLen(n, 2);
m := LookupForDefined(c, n.sons[0], onlyCurrentScope);
if (m <> nil) and (m.kind = skModule) then begin if (m <> nil) and (m.kind = skModule) then begin
if (n.sons[1].kind = nkIdent) then begin if (n.sons[1].kind = nkIdent) then begin
ident := n.sons[1].ident; ident := n.sons[1].ident;
@ -612,7 +615,7 @@ begin
end; end;
nkAccQuoted: begin nkAccQuoted: begin
checkSonsLen(n, 1); checkSonsLen(n, 1);
result := lookupForDefined(c, n.sons[0]); result := lookupForDefined(c, n.sons[0], onlyCurrentScope);
end end
else begin else begin
liMessage(n.info, errIdentifierExpected, renderTree(n)); liMessage(n.info, errIdentifierExpected, renderTree(n));
@ -621,14 +624,14 @@ begin
end end
end; end;
function semDefined(c: PContext; n: PNode): PNode; function semDefined(c: PContext; n: PNode; onlyCurrentScope: bool): PNode;
begin begin
checkSonsLen(n, 2); checkSonsLen(n, 2);
result := newIntNode(nkIntLit, 0); result := newIntNode(nkIntLit, 0);
// we replace this node by a 'true' or 'false' node // we replace this node by a 'true' or 'false' node
if LookUpForDefined(c, n.sons[1]) <> nil then if LookUpForDefined(c, n.sons[1], onlyCurrentScope) <> nil then
result.intVal := 1 result.intVal := 1
else if (n.sons[1].kind = nkIdent) else if not onlyCurrentScope and (n.sons[1].kind = nkIdent)
and condsyms.isDefined(n.sons[1].ident) then and condsyms.isDefined(n.sons[1].ident) then
result.intVal := 1; result.intVal := 1;
result.info := n.info; result.info := n.info;
@ -647,8 +650,8 @@ function semMagic(c: PContext; n: PNode; s: PSym; flags: TExprFlags): PNode;
begin begin
result := n; result := n;
case s.magic of // magics that need special treatment case s.magic of // magics that need special treatment
mDefined: result := semDefined(c, setMs(n, s)); mDefined: result := semDefined(c, setMs(n, s), false);
mDefinedInScope: result := semDefinedInScope(c, setMs(n, s)); mDefinedInScope: result := semDefined(c, setMs(n, s), true);
mLow: result := semLowHigh(c, setMs(n, s), mLow); mLow: result := semLowHigh(c, setMs(n, s), mLow);
mHigh: result := semLowHigh(c, setMs(n, s), mHigh); mHigh: result := semLowHigh(c, setMs(n, s), mHigh);
mSizeOf: result := semSizeof(c, setMs(n, s)); mSizeOf: result := semSizeof(c, setMs(n, s));
@ -658,11 +661,6 @@ begin
end; end;
end; end;
procedure checkDeprecated(n: PNode; s: PSym);
begin
if sfDeprecated in s.flags then liMessage(n.info, warnDeprecated, s.name.s);
end;
function isTypeExpr(n: PNode): bool; function isTypeExpr(n: PNode): bool;
begin begin
case n.kind of case n.kind of
@ -795,7 +793,7 @@ begin
result := newSymNode(f); result := newSymNode(f);
result.info := n.info; result.info := n.info;
result.typ := ty; result.typ := ty;
checkDeprecated(n, f); markUsed(n, f);
end end
else else
liMessage(n.sons[1].info, errEnumHasNoValueX, i.s); liMessage(n.sons[1].info, errEnumHasNoValueX, i.s);
@ -823,7 +821,7 @@ begin
n.sons[0] := makeDeref(n.sons[0]); n.sons[0] := makeDeref(n.sons[0]);
n.sons[1] := newSymNode(f); // we now have the correct field n.sons[1] := newSymNode(f); // we now have the correct field
n.typ := f.typ; n.typ := f.typ;
checkDeprecated(n, f); markUsed(n, f);
if check = nil then result := n if check = nil then result := n
else begin else begin
check.sons[0] := n; check.sons[0] := n;
@ -841,7 +839,7 @@ begin
n.sons[1] := newSymNode(f); n.sons[1] := newSymNode(f);
n.typ := f.typ; n.typ := f.typ;
result := n; result := n;
checkDeprecated(n, f); markUsed(n, f);
exit exit
end end
end; end;
@ -851,7 +849,7 @@ begin
//if (f <> nil) and (f.kind = skStub) then loadStub(f); //if (f <> nil) and (f.kind = skStub) then loadStub(f);
// ``loadStub`` is not correct here as we don't care for ``f`` really // ``loadStub`` is not correct here as we don't care for ``f`` really
if (f <> nil) then begin if (f <> nil) then begin
// BUGFIX: do not check for (f.kind in [skProc, skIterator]) here // BUGFIX: do not check for (f.kind in [skProc, skMethod, skIterator]) here
result := newNodeI(nkDotCall, n.info); result := newNodeI(nkDotCall, n.info);
// This special node kind is to merge with the call handler in `semExpr`. // This special node kind is to merge with the call handler in `semExpr`.
addSon(result, newIdentNode(i, n.info)); addSon(result, newIdentNode(i, n.info));
@ -1145,7 +1143,6 @@ begin
a := n.sons[0]; a := n.sons[0];
s := qualifiedLookup(c, a, false); s := qualifiedLookup(c, a, false);
if (s <> nil) then begin if (s <> nil) then begin
checkDeprecated(n, s);
case s.kind of case s.kind of
skMacro: result := semMacroExpr(c, n, s, semCheck); skMacro: result := semMacroExpr(c, n, s, semCheck);
skTemplate: begin skTemplate: begin
@ -1176,7 +1173,7 @@ begin
if (s.kind = skType) and not (efAllowType in flags) then if (s.kind = skType) and not (efAllowType in flags) then
liMessage(n.info, errATypeHasNoValue); liMessage(n.info, errATypeHasNoValue);
case s.kind of case s.kind of
skProc, skIterator, skConverter: begin skProc, skMethod, skIterator, skConverter: begin
if (s.magic <> mNone) then if (s.magic <> mNone) then
liMessage(n.info, errInvalidContextForBuiltinX, s.name.s); liMessage(n.info, errInvalidContextForBuiltinX, s.name.s);
result := symChoice(c, n, s); result := symChoice(c, n, s);
@ -1194,7 +1191,7 @@ begin
copy `x`'s AST into each context, so that the type fixup phase can copy `x`'s AST into each context, so that the type fixup phase can
deal with two different ``[]``. deal with two different ``[]``.
*) *)
include(s.flags, sfUsed); markUsed(n, s);
if s.typ.kind in ConstAbstractTypes then begin if s.typ.kind in ConstAbstractTypes then begin
result := copyTree(s.ast); result := copyTree(s.ast);
result.info := n.info; result.info := n.info;
@ -1208,7 +1205,7 @@ begin
skMacro: result := semMacroExpr(c, n, s); skMacro: result := semMacroExpr(c, n, s);
skTemplate: result := semTemplateExpr(c, n, s); skTemplate: result := semTemplateExpr(c, n, s);
skVar: begin skVar: begin
include(s.flags, sfUsed); markUsed(n, s);
// if a proc accesses a global variable, it is not side effect free // if a proc accesses a global variable, it is not side effect free
if sfGlobal in s.flags then include(c.p.owner.flags, sfSideEffect); if sfGlobal in s.flags then include(c.p.owner.flags, sfSideEffect);
result := newSymNode(s); result := newSymNode(s);
@ -1219,12 +1216,11 @@ begin
result := semExpr(c, s.ast); result := semExpr(c, s.ast);
end end
else begin else begin
include(s.flags, sfUsed); markUsed(n, s);
result := newSymNode(s); result := newSymNode(s);
result.info := n.info; result.info := n.info;
end end
end; end;
checkDeprecated(n, s);
end; end;
function semDotExpr(c: PContext; n: PNode; flags: TExprFlags): PNode; function semDotExpr(c: PContext; n: PNode; flags: TExprFlags): PNode;
@ -1259,7 +1255,7 @@ begin
if (s.kind = skType) and not (efAllowType in flags) then if (s.kind = skType) and not (efAllowType in flags) then
liMessage(n.info, errATypeHasNoValue); liMessage(n.info, errATypeHasNoValue);
if (s.magic <> mNone) and if (s.magic <> mNone) and
(s.kind in [skProc, skIterator, skConverter]) then (s.kind in [skProc, skMethod, skIterator, skConverter]) then
liMessage(n.info, errInvalidContextForBuiltinX, s.name.s); *) liMessage(n.info, errInvalidContextForBuiltinX, s.name.s); *)
// because of the changed symbol binding, this does not mean that we // because of the changed symbol binding, this does not mean that we
// don't have to check the symbol for semantics here again! // don't have to check the symbol for semantics here again!
@ -1296,18 +1292,16 @@ begin
checkMinSonsLen(n, 1); checkMinSonsLen(n, 1);
s := qualifiedLookup(c, n.sons[0], false); s := qualifiedLookup(c, n.sons[0], false);
if (s <> nil) then begin if (s <> nil) then begin
checkDeprecated(n, s);
case s.kind of case s.kind of
skMacro: result := semMacroExpr(c, n, s); skMacro: result := semMacroExpr(c, n, s);
skTemplate: result := semTemplateExpr(c, n, s); skTemplate: result := semTemplateExpr(c, n, s);
skType: begin skType: begin
include(s.flags, sfUsed);
if n.kind <> nkCall then if n.kind <> nkCall then
liMessage(n.info, errXisNotCallable, s.name.s); liMessage(n.info, errXisNotCallable, s.name.s);
// XXX does this check make any sense? // XXX does this check make any sense?
result := semConv(c, n, s); result := semConv(c, n, s);
end; end;
skProc, skConverter, skIterator: begin skProc, skMethod, skConverter, skIterator: begin
if s.magic = mNone then result := semDirectOp(c, n, flags) if s.magic = mNone then result := semDirectOp(c, n, flags)
else result := semMagic(c, n, s, flags); else result := semMagic(c, n, s, flags);
end; end;
@ -1328,7 +1322,8 @@ begin
nkBracketExpr: begin nkBracketExpr: begin
checkMinSonsLen(n, 1); checkMinSonsLen(n, 1);
s := qualifiedLookup(c, n.sons[0], false); s := qualifiedLookup(c, n.sons[0], false);
if (s <> nil) and (s.kind in [skProc, skConverter, skIterator]) then begin if (s <> nil)
and (s.kind in [skProc, skMethod, skConverter, skIterator]) then begin
// type parameters: partial generic specialization // type parameters: partial generic specialization
// XXX: too implement! // XXX: too implement!
internalError(n.info, 'explicit generic instantation not implemented'); internalError(n.info, 'explicit generic instantation not implemented');

View file

@ -288,7 +288,7 @@ begin
result.typ := n.typ; result.typ := n.typ;
end; end;
mNewString, mExit, mInc, ast.mDec, mEcho, mAssert, mSwap, mNewString, mExit, mInc, ast.mDec, mEcho, mAssert, mSwap,
mAppendStrCh, mAppendStrStr, mAppendSeqElem, mAppendSeqSeq, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
mSetLengthStr, mSetLengthSeq, mNLen..mNError: begin end; mSetLengthStr, mSetLengthSeq, mNLen..mNError: begin end;
else InternalError(a.info, 'evalOp(' +{&} magicToStr[m] +{&} ')'); else InternalError(a.info, 'evalOp(' +{&} magicToStr[m] +{&} ')');
end end
@ -412,7 +412,7 @@ begin
else result := copyTree(s.ast); // BUGFIX else result := copyTree(s.ast); // BUGFIX
end end
end end
else if s.kind = skProc then // BUGFIX else if s.kind in [skProc, skMethod] then // BUGFIX
result := n result := n
end; end;
nkCharLit..nkNilLit: result := copyNode(n); nkCharLit..nkNilLit: result := copyNode(n);

View file

@ -182,7 +182,7 @@ begin
addDecl(c, result); addDecl(c, result);
if n.sons[codePos] <> nil then begin if n.sons[codePos] <> nil then begin
c.p := newProcCon(result); c.p := newProcCon(result);
if result.kind in [skProc, skConverter] then begin if result.kind in [skProc, skMethod, skConverter] then begin
addResult(c, result.typ.sons[0], n.info); addResult(c, result.typ.sons[0], n.info);
addResultNode(c, n); addResultNode(c, n);
end; end;

View file

@ -316,7 +316,7 @@ var
begin begin
result := n; result := n;
checkSonsLen(n, 1); checkSonsLen(n, 1);
if not (c.p.owner.kind in [skConverter, skProc, skMacro]) then if not (c.p.owner.kind in [skConverter, skMethod, skProc, skMacro]) then
liMessage(n.info, errXNotAllowedHere, '''return'''); liMessage(n.info, errXNotAllowedHere, '''return''');
if (n.sons[0] <> nil) then begin if (n.sons[0] <> nil) then begin
n.sons[0] := SemExprWithType(c, n.sons[0]); n.sons[0] := SemExprWithType(c, n.sons[0]);
@ -970,11 +970,20 @@ begin
result := semProcAux(c, n, skProc, procPragmas); result := semProcAux(c, n, skProc, procPragmas);
end; end;
function semMethod(c: PContext; n: PNode): PNode;
begin
if not isTopLevel(c) then
liMessage(n.info, errXOnlyAtModuleScope, 'method');
result := semProcAux(c, n, skMethod, methodPragmas);
end;
function semConverterDef(c: PContext; n: PNode): PNode; function semConverterDef(c: PContext; n: PNode): PNode;
var var
t: PType; t: PType;
s: PSym; s: PSym;
begin begin
if not isTopLevel(c) then
liMessage(n.info, errXOnlyAtModuleScope, 'converter');
checkSonsLen(n, codePos+1); checkSonsLen(n, codePos+1);
if n.sons[genericParamsPos] <> nil then if n.sons[genericParamsPos] <> nil then
liMessage(n.info, errNoGenericParamsAllowedForX, 'converter'); liMessage(n.info, errNoGenericParamsAllowedForX, 'converter');
@ -1080,6 +1089,7 @@ begin
nkPragma: pragma(c, c.p.owner, n, stmtPragmas); nkPragma: pragma(c, c.p.owner, n, stmtPragmas);
nkIteratorDef: result := semIterator(c, n); nkIteratorDef: result := semIterator(c, n);
nkProcDef: result := semProc(c, n); nkProcDef: result := semProc(c, n);
nkMethodDef: result := semMethod(c, n);
nkConverterDef: result := semConverterDef(c, n); nkConverterDef: result := semConverterDef(c, n);
nkMacroDef: result := semMacroDef(c, n); nkMacroDef: result := semMacroDef(c, n);
nkTemplateDef: result := semTemplateDef(c, n); nkTemplateDef: result := semTemplateDef(c, n);

View file

@ -94,7 +94,7 @@ begin
if i < a then if i < a then
arg := n.sons[i] arg := n.sons[i]
else else
arg := copyTree(s.ast.sons[paramsPos].sons[i-1].sym.ast); arg := copyTree(s.typ.n.sons[i].sym.ast);
if arg = nil then liMessage(n.info, errWrongNumberOfArguments); if arg = nil then liMessage(n.info, errWrongNumberOfArguments);
if not (s.typ.sons[i].kind in [tyTypeDesc, tyStmt, tyExpr]) then begin if not (s.typ.sons[i].kind in [tyTypeDesc, tyStmt, tyExpr]) then begin
// concrete type means semantic checking for argument: // concrete type means semantic checking for argument:
@ -132,7 +132,7 @@ begin
if i <= 1 then begin if i <= 1 then begin
result := newSymNode(s); result := newSymNode(s);
result.info := n.info; result.info := n.info;
include(s.flags, sfUsed); markUsed(n, s);
end end
else begin else begin
// semantic checking requires a type; ``fitNode`` deals with it // semantic checking requires a type; ``fitNode`` deals with it

View file

@ -232,7 +232,7 @@ function semTypeIdent(c: PContext; n: PNode): PSym;
begin begin
result := qualifiedLookup(c, n, true); result := qualifiedLookup(c, n, true);
if (result <> nil) then begin if (result <> nil) then begin
include(result.flags, sfUsed); markUsed(n, result);
if result.kind <> skType then liMessage(n.info, errTypeExpected); if result.kind <> skType then liMessage(n.info, errTypeExpected);
end end
else else
@ -792,7 +792,7 @@ begin
assignType(prev, t); assignType(prev, t);
result := prev; result := prev;
end; end;
include(n.sym.flags, sfUsed); // BUGFIX markUsed(n, n.sym);
end end
else else
liMessage(n.info, errTypeExpected); liMessage(n.info, errTypeExpected);

View file

@ -103,7 +103,7 @@ begin
candidates := ''; candidates := '';
sym := initOverloadIter(o, c, n.sons[0]); sym := initOverloadIter(o, c, n.sons[0]);
while sym <> nil do begin while sym <> nil do begin
if sym.kind in [skProc, skIterator, skConverter] then begin if sym.kind in [skProc, skMethod, skIterator, skConverter] then begin
add(candidates, getProcHeader(sym)); add(candidates, getProcHeader(sym));
add(candidates, nl) add(candidates, nl)
end; end;
@ -709,7 +709,7 @@ begin
best := -1; best := -1;
for i := 0 to sonsLen(arg)-1 do begin for i := 0 to sonsLen(arg)-1 do begin
// iterators are not first class yet, so ignore them // iterators are not first class yet, so ignore them
if arg.sons[i].sym.kind in {@set}[skProc, skConverter] then begin if arg.sons[i].sym.kind in {@set}[skProc, skMethod, skConverter] then begin
copyCandidate(z, m); copyCandidate(z, m);
r := typeRel(z.bindings, f, arg.sons[i].typ); r := typeRel(z.bindings, f, arg.sons[i].typ);
if r <> isNone then begin if r <> isNone then begin
@ -734,7 +734,7 @@ begin
end end
else begin else begin
// only one valid interpretation found: // only one valid interpretation found:
include(arg.sons[best].sym.flags, sfUsed); markUsed(arg, arg.sons[best].sym);
result := ParamTypesMatchAux(c, m, f, arg.sons[best].typ, arg.sons[best]); result := ParamTypesMatchAux(c, m, f, arg.sons[best].typ, arg.sons[best]);
end end
end end
@ -884,6 +884,19 @@ begin
end end
end; end;
function sameMethodDispatcher(a, b: PSym): bool;
var
aa, bb: PNode;
begin
result := false;
if (a.kind = skMethod) and (b.kind = skMethod) then begin
aa := lastSon(a.ast);
bb := lastSon(b.ast);
if (aa.kind = nkSym) and (bb.kind = nkSym) and
(aa.sym = bb.sym) then result := true
end
end;
function semDirectCall(c: PContext; n: PNode; filter: TSymKinds): PNode; function semDirectCall(c: PContext; n: PNode; filter: TSymKinds): PNode;
var var
sym: PSym; sym: PSym;
@ -923,7 +936,8 @@ begin
// do not generate an error yet; the semantic checking will check for // do not generate an error yet; the semantic checking will check for
// an overloaded () operator // an overloaded () operator
end end
else if (y.state = csMatch) and (cmpCandidates(x, y) = 0) then begin else if (y.state = csMatch) and (cmpCandidates(x, y) = 0)
and not sameMethodDispatcher(x.calleeSym, y.calleeSym) then begin
if x.state <> csMatch then if x.state <> csMatch then
InternalError(n.info, 'x.state is not csMatch'); InternalError(n.info, 'x.state is not csMatch');
//writeMatches(x); //writeMatches(x);
@ -935,7 +949,7 @@ begin
end end
else begin else begin
// only one valid interpretation found: // only one valid interpretation found:
include(x.calleeSym.flags, sfUsed); markUsed(n, x.calleeSym);
if x.calleeSym.ast = nil then if x.calleeSym.ast = nil then
internalError(n.info, 'calleeSym.ast is nil'); // XXX: remove this check! internalError(n.info, 'calleeSym.ast is nil'); // XXX: remove this check!
if x.calleeSym.ast.sons[genericParamsPos] <> nil then begin if x.calleeSym.ast.sons[genericParamsPos] <> nil then begin

View file

@ -15,7 +15,7 @@ unit transf;
// * inlines constants // * inlines constants
// * performes contant folding // * performes contant folding
// * introduces nkHiddenDeref, nkHiddenSubConv, etc. // * introduces nkHiddenDeref, nkHiddenSubConv, etc.
// * aggressive compile-time evaluation based on the side-effect analysis // * introduces method dispatchers
interface interface
@ -24,7 +24,7 @@ interface
uses uses
sysutils, nsystem, charsets, strutils, sysutils, nsystem, charsets, strutils,
lists, options, ast, astalgo, trees, treetab, evals, lists, options, ast, astalgo, trees, treetab, evals,
msgs, nos, idents, rnimsyn, types, passes, semfold, magicsys; msgs, nos, idents, rnimsyn, types, passes, semfold, magicsys, cgmeth;
const const
genPrefix = ':tmp'; // prefix for generated names genPrefix = ':tmp'; // prefix for generated names
@ -574,7 +574,7 @@ end;
function getMagicOp(call: PNode): TMagic; function getMagicOp(call: PNode): TMagic;
begin begin
if (call.sons[0].kind = nkSym) if (call.sons[0].kind = nkSym)
and (call.sons[0].sym.kind in [skProc, skConverter]) then and (call.sons[0].sym.kind in [skProc, skMethod, skConverter]) then
result := call.sons[0].sym.magic result := call.sons[0].sym.magic
else else
result := mNone result := mNone
@ -842,6 +842,11 @@ begin
if sonsLen(result) = 2 then if sonsLen(result) = 2 then
result := result.sons[1]; result := result.sons[1];
end end
else if (result.sons[0].kind = nkSym)
and (result.sons[0].sym.kind = skMethod) then begin
// use the dispatcher for the call:
result := methodCall(result);
end
(* (*
else if result.sons[0].kind = nkSym then begin else if result.sons[0].kind = nkSym then begin
// optimization still too aggressive // optimization still too aggressive
@ -882,9 +887,12 @@ begin
nkLambda: result := transformLambda(c, n); nkLambda: result := transformLambda(c, n);
nkForStmt: result := transformFor(c, n); nkForStmt: result := transformFor(c, n);
nkCaseStmt: result := transformCase(c, n); nkCaseStmt: result := transformCase(c, n);
nkProcDef, nkIteratorDef, nkMacroDef: begin nkProcDef, nkMethodDef, nkIteratorDef, nkMacroDef: begin
if n.sons[genericParamsPos] = nil then if n.sons[genericParamsPos] = nil then begin
n.sons[codePos] := transform(c, n.sons[codePos]); n.sons[codePos] := transform(c, n.sons[codePos]);
if n.kind = nkMethodDef then
methodDef(n.sons[namePos].sym);
end
end; end;
nkWhileStmt: begin nkWhileStmt: begin
if (sonsLen(n) <> 2) then InternalError(n.info, 'transform'); if (sonsLen(n) <> 2) then InternalError(n.info, 'transform');

View file

@ -23,7 +23,7 @@
## Nimrod shall produce. ## Nimrod shall produce.
import import
strutils, regexprs, os, osproc, streams strutils, pegs, os, osproc, streams
const const
cmdTemplate = r"nimrod cc --hints:on $options $filename" cmdTemplate = r"nimrod cc --hints:on $options $filename"
@ -38,7 +38,7 @@ type
proc myExec(cmd: string): string = proc myExec(cmd: string): string =
#echo("Executing: " & cmd) #echo("Executing: " & cmd)
result = osproc.executeProcess(cmd) result = osproc.execProcess(cmd)
#echo("Received: " & result) #echo("Received: " & result)
proc parseTest(filename: string): TSpec = proc parseTest(filename: string): TSpec =
@ -49,24 +49,30 @@ proc parseTest(filename: string): TSpec =
result.file = filename result.file = filename
for s in lines(filename): for s in lines(filename):
inc(i) inc(i)
if find(s, r"\#OUT\s*(.*)", matches): if contains(s, peg"'#OUT' \s+ {.*}", matches):
result.outp = matches[1] result.outp = matches[0]
break break
if find(s, r"\#ERROR_IN\s*(\S*)\s*(\d+)", matches): if contains(s, peg"'#ERROR_IN' \s* {\S*} \s* {\d+}", matches):
result.file = matches[1] result.file = matches[0]
result.line = parseInt(matches[2]) result.line = parseInt(matches[1])
result.err = true result.err = true
break break
if find(s, r"\#ERROR_MSG\s*(.*)", matches): if contains(s, peg"'#ERROR_MSG' \s* {.*}", matches):
result.line = i result.line = i
result.outp = matches[1] result.outp = matches[0]
result.err = True result.err = True
break break
if find(s, r"\#ERROR$", matches): if contains(s, peg"'#ERROR' \s* !.", matches):
result.line = i result.line = i
result.err = true result.err = true
break break
var
pegLineError = peg"{[^(]*} '(' {\d+} ', ' \d+ ') Error:' \s* {.*}"
pegOtherError = peg"'Error:' \s* {.*}"
pegSuccess = peg"'Hint: operation successful'.*"
pegOfInterest = pegLineError / pegOtherError / pegSuccess
proc callCompiler(filename, options: string): TSpec = proc callCompiler(filename, options: string): TSpec =
var c = parseCmdLine(cmdTemplate % ["filename", filename, "options", options]) var c = parseCmdLine(cmdTemplate % ["filename", filename, "options", options])
var a: seq[string] = @[] # slicing is not yet implemented :-( var a: seq[string] = @[] # slicing is not yet implemented :-(
@ -77,52 +83,50 @@ proc callCompiler(filename, options: string): TSpec =
var s = "" var s = ""
while running(p) or not outp.atEnd(outp): while running(p) or not outp.atEnd(outp):
var x = outp.readLine() var x = outp.readLine()
if match(x, r"(.*)\((\d+), \d+\) Error\: (.*)") or if x =~ pegOfInterest:
match(x, r"Error\: (.*)") or
match(x, r"Hint\: operation successful"):
# `s` should contain the last error message # `s` should contain the last error message
s = x s = x
result.outp = "" result.outp = ""
result.puremsg = "" result.puremsg = ""
result.file = "" result.file = ""
result.err = true result.err = true
if s =~ r"(.*)\((\d+), \d+\) Error\: (.*)": if s =~ pegLineError:
result.file = matches[1] result.file = matches[0]
result.line = parseInt(matches[2]) result.line = parseInt(matches[1])
result.outp = matches[0] result.outp = s
result.puremsg = matches[3] result.puremsg = matches[2]
elif s =~ r"Error\: (.*)": elif s =~ pegOtherError:
result.puremsg = matches[1] result.puremsg = matches[0]
result.outp = matches[0] result.outp = s
result.line = 1 result.line = 1
elif s =~ r"Hint\: operation successful": elif s =~ pegSuccess:
result.outp = matches[0] result.outp = s
result.err = false result.err = false
proc cmpResults(filename: string, spec, comp: TSpec): bool = proc sameResults(filename: string, spec, comp: TSpec): bool =
# short filename for messages (better readability): # short filename for messages (better readability):
var shortfile = os.extractFilename(filename) var shortfile = os.extractFilename(filename)
if comp.err and comp.outp == "": if comp.err and comp.outp == "":
# the compiler did not say "[Error]" nor "operation sucessful" # the compiler did not say "[Error]" nor "operation sucessful"
Echo("[Tester] $1 -- FAILED; COMPILER BROKEN" % shortfile) Echo("[Tester] $# -- FAILED; COMPILER BROKEN" % shortfile)
elif spec.err != comp.err: elif spec.err != comp.err:
Echo(("[Tester] $1 -- FAILED\n" & Echo(("[Tester] $# -- FAILED\n" &
"Compiler says: $2\n" & "Compiler says: $#\n" &
"But specification says: $3") % "But specification says: $#") %
[shortfile, comp.outp, spec.outp]) [shortfile, comp.outp, spec.outp])
elif spec.err: elif spec.err:
if extractFilename(comp.file) != extractFilename(spec.file): if extractFilename(comp.file) != extractFilename(spec.file):
Echo(("[Tester] $1 -- FAILED: file names do not match:\n" & Echo(("[Tester] $# -- FAILED: file names do not match:\n" &
"Compiler: $2\nSpec: $3") % [shortfile, comp.file, spec.file]) "Compiler: $#\nSpec: $#") % [shortfile, comp.file, spec.file])
elif strip(spec.outp) notin strip(comp.puremsg): elif strip(spec.outp) notin strip(comp.puremsg):
Echo(("[Tester] $1 -- FAILED: error messages do not match:\n" & Echo(("[Tester] $# -- FAILED: error messages do not match:\n" &
"Compiler: $2\nSpec: $3") % [shortfile, comp.pureMsg, spec.outp]) "Compiler: $#\nSpec: $#") % [shortfile, comp.pureMsg, spec.outp])
elif comp.line != spec.line: elif comp.line != spec.line:
Echo(("[Tester] $1 -- FAILED: line numbers do not match:\n" & Echo(("[Tester] $# -- FAILED: line numbers do not match:\n" &
"Compiler: $2\nSpec: $3") % [shortfile, $comp.line, $spec.line]) "Compiler: $#\nSpec: $#") % [shortfile, $comp.line, $spec.line])
else: else:
Echo("[Tester] $1 -- OK" % shortfile) Echo("[Tester] $# -- OK" % shortfile)
result = true result = true
else: else:
# we have to run the executable and check its output: # we have to run the executable and check its output:
@ -131,17 +135,17 @@ proc cmpResults(filename: string, spec, comp: TSpec): bool =
if len(spec.outp) == 0: if len(spec.outp) == 0:
# we have no output to validate against, but compilation succeeded, # we have no output to validate against, but compilation succeeded,
# so it's okay: # so it's okay:
Echo("[Tester] $1 -- OK" % shortfile) Echo("[Tester] $# -- OK" % shortfile)
result = true result = true
else: else:
var buf = myExec(exeFile) var buf = myExec(exeFile)
result = strip(buf) == strip(spec.outp) result = strip(buf) == strip(spec.outp)
if result: if result:
Echo("[Tester] $1 -- compiled program OK" % shortfile) Echo("[Tester] $# -- compiled program OK" % shortfile)
else: else:
Echo("[Tester] $1 -- compiled program FAILED" % shortfile) Echo("[Tester] $# -- compiled program FAILED" % shortfile)
else: else:
Echo("[Tester] $1 -- FAILED; executable not found" % shortfile) Echo("[Tester] $# -- FAILED; executable not found" % shortfile)
proc main(options: string) = proc main(options: string) =
# runs the complete testsuite # runs the complete testsuite
@ -151,16 +155,17 @@ proc main(options: string) =
if extractFilename(filename) == "tester.nim": continue if extractFilename(filename) == "tester.nim": continue
var spec = parseTest(filename) var spec = parseTest(filename)
var comp = callCompiler(filename, options) var comp = callCompiler(filename, options)
if cmpResults(filename, spec, comp): inc(passed) if sameResults(filename, spec, comp): inc(passed)
inc(total) inc(total)
# ensure that the examples at least compile # ensure that the examples at least compiles
for filename in os.walkFiles("examples/*.nim"): for filename in os.walkFiles("examples/*.nim"):
if executeShellCommand(cmdTemplate % var comp = callCompiler(filename, options)
["filename", filename, "options", options]) == 0: var shortfile = os.extractFilename(filename)
inc(passed) if comp.err:
else:
var shortfile = os.extractFilename(filename)
Echo("[Tester] Example '$#' -- FAILED" % shortfile) Echo("[Tester] Example '$#' -- FAILED" % shortfile)
else:
Echo("[Tester] Example $# -- OK" % shortfile)
inc(passed)
inc(total) inc(total)
Echo("[Tester] $#/$# tests passed\n" % [$passed, $total]) Echo("[Tester] $#/$# tests passed\n" % [$passed, $total])

View file

@ -20,7 +20,7 @@ Name: english; MessagesFile: compiler:Default.isl
[Files] [Files]
#for i in low(TFileCategory)..fcWindows: #for i in low(TFileCategory)..fcWindows:
# for f in items(c.cat[i]): # for f in items(c.cat[i]):
Source: ${expandFilename(f)}; DestDir: {app}\${extractDir(f)}; Flags: ignoreversion Source: ${expandFilename(f)}; DestDir: {app}\${splitFile(f).dir}; Flags: ignoreversion
# end for # end for
#end for #end for

View file

@ -58,7 +58,7 @@ if [ $# -eq 1 ] ; then
#var createdDirs = newStringTable() #var createdDirs = newStringTable()
#for cat in fcConfig..fcLib: #for cat in fcConfig..fcLib:
# for f in items(c.cat[cat]): # for f in items(c.cat[cat]):
# var mk = extractDir(f.skipRoot) # var mk = splitFile(f.skipRoot).dir
# if mk.len > 0: # if mk.len > 0:
# mk = unixDirVars[cat] & "/" & mk # mk = unixDirVars[cat] & "/" & mk
# if not createdDirs.hasKey(mk): # if not createdDirs.hasKey(mk):

View file

@ -116,7 +116,7 @@ Compile_options:
""" """
proc parseCmdLine(c: var TConfigData) = proc parseCmdLine(c: var TConfigData) =
var p = init() var p = initOptParser()
while true: while true:
next(p) next(p)
var kind = p.kind var kind = p.kind
@ -132,8 +132,8 @@ proc parseCmdLine(c: var TConfigData) =
of "inno": incl(c.actions, actionInno) of "inno": incl(c.actions, actionInno)
else: quit(Usage) else: quit(Usage)
else: else:
c.infile = appendFileExt(key, "ini") c.infile = addFileExt(key, "ini")
c.nimrodArgs = getRestOfCommandLine(p) c.nimrodArgs = cmdLineRest(p)
break break
of cmdLongOption, cmdShortOption: of cmdLongOption, cmdShortOption:
case normalize(key) case normalize(key)
@ -255,7 +255,7 @@ proc parseIniFile(c: var TConfigData) =
proc readCFiles(c: var TConfigData, osA, cpuA: int) = proc readCFiles(c: var TConfigData, osA, cpuA: int) =
var cfg: TCfgParser var cfg: TCfgParser
var cfilesSection = false var cfilesSection = false
var f = extractDir(c.infile) / "mapping.txt" var f = splitFile(c.infile).dir / "mapping.txt"
c.cfiles[osA][cpuA] = @[] c.cfiles[osA][cpuA] = @[]
var input = newFileStream(f, fmRead) var input = newFileStream(f, fmRead)
if input != nil: if input != nil:
@ -302,7 +302,7 @@ proc srcdist(c: var TConfigData) =
[c.oses[osA-1], c.cpus[cpuA-1], c.nimrodArgs, [c.oses[osA-1], c.cpus[cpuA-1], c.nimrodArgs,
changeFileExt(c.infile, "nim")] changeFileExt(c.infile, "nim")]
echo(cmd) echo(cmd)
if executeShellCommand(cmd) != 0: if execShellCmd(cmd) != 0:
quit("Error: call to nimrod compiler failed") quit("Error: call to nimrod compiler failed")
readCFiles(c, osA, cpuA) readCFiles(c, osA, cpuA)
for i in 0 .. c.cfiles[osA][cpuA].len-1: for i in 0 .. c.cfiles[osA][cpuA].len-1:

View file

@ -56,7 +56,7 @@ Compile_options:
""" """
proc parseCmdLine(c: var TConfigData) = proc parseCmdLine(c: var TConfigData) =
var p = init() var p = initOptParser()
while true: while true:
next(p) next(p)
var kind = p.kind var kind = p.kind
@ -64,8 +64,8 @@ proc parseCmdLine(c: var TConfigData) =
var val = p.val var val = p.val
case kind case kind
of cmdArgument: of cmdArgument:
c.infile = appendFileExt(key, "ini") c.infile = addFileExt(key, "ini")
c.nimrodArgs = getRestOfCommandLine(p) c.nimrodArgs = cmdLineRest(p)
break break
of cmdLongOption, cmdShortOption: of cmdLongOption, cmdShortOption:
case normalize(key) case normalize(key)
@ -84,7 +84,7 @@ proc walkDirRecursively(s: var seq[string], root, ext: string) =
for k, f in walkDir(root): for k, f in walkDir(root):
case k case k
of pcFile, pcLinkToFile: of pcFile, pcLinkToFile:
if cmpIgnoreCase(ext, extractFileExt(f)) == 0: if cmpIgnoreCase(ext, splitFile(f).ext) == 0:
add(s, f) add(s, f)
of pcDirectory: walkDirRecursively(s, f, ext) of pcDirectory: walkDirRecursively(s, f, ext)
of pcLinkToDirectory: nil of pcLinkToDirectory: nil
@ -94,7 +94,7 @@ proc addFiles(s: var seq[string], dir, ext: string, patterns: seq[string]) =
if existsDir(dir / p): if existsDir(dir / p):
walkDirRecursively(s, dir / p, ext) walkDirRecursively(s, dir / p, ext)
else: else:
add(s, dir / appendFileExt(p, ext)) add(s, dir / addFileExt(p, ext))
proc parseIniFile(c: var TConfigData) = proc parseIniFile(c: var TConfigData) =
var var
@ -143,7 +143,7 @@ proc parseIniFile(c: var TConfigData) =
if c.projectName.len == 0: if c.projectName.len == 0:
c.projectName = changeFileExt(extractFilename(c.infile), "") c.projectName = changeFileExt(extractFilename(c.infile), "")
if c.outdir.len == 0: if c.outdir.len == 0:
c.outdir = extractDir(c.infile) c.outdir = splitFile(c.infile).dir
else: else:
quit("cannot open: " & c.infile) quit("cannot open: " & c.infile)
@ -151,18 +151,18 @@ proc parseIniFile(c: var TConfigData) =
proc Exec(cmd: string) = proc Exec(cmd: string) =
echo(cmd) echo(cmd)
if os.executeShellCommand(cmd) != 0: quit("external program failed") if os.execShellCmd(cmd) != 0: quit("external program failed")
proc buildDoc(c: var TConfigData, destPath: string) = proc buildDoc(c: var TConfigData, destPath: string) =
# call nim for the documentation: # call nim for the documentation:
for d in items(c.doc): for d in items(c.doc):
Exec("nimrod rst2html $# -o:$# --index=$#/theindex $#" % Exec("nimrod rst2html $# -o:$# --index=$#/theindex $#" %
[c.nimrodArgs, destPath / changeFileExt(extractFileTrunk(d), "html"), [c.nimrodArgs, destPath / changeFileExt(splitFile(d).name, "html"),
destpath, d]) destpath, d])
Exec("nimrod rst2tex $# $#" % [c.nimrodArgs, d]) Exec("nimrod rst2tex $# $#" % [c.nimrodArgs, d])
for d in items(c.srcdoc): for d in items(c.srcdoc):
Exec("nimrod doc $# -o:$# --index=$#/theindex $#" % Exec("nimrod doc $# -o:$# --index=$#/theindex $#" %
[c.nimrodArgs, destPath / changeFileExt(extractFileTrunk(d), "html"), [c.nimrodArgs, destPath / changeFileExt(splitFile(d).name, "html"),
destpath, d]) destpath, d])
Exec("nimrod rst2html $1 -o:$2/theindex.html $2/theindex" % Exec("nimrod rst2html $1 -o:$2/theindex.html $2/theindex" %
[c.nimrodArgs, destPath]) [c.nimrodArgs, destPath])
@ -171,7 +171,7 @@ proc buildAddDoc(c: var TConfigData, destPath: string) =
# build additional documentation (without the index): # build additional documentation (without the index):
for d in items(c.webdoc): for d in items(c.webdoc):
Exec("nimrod doc $# -o:$# $#" % Exec("nimrod doc $# -o:$# $#" %
[c.nimrodArgs, destPath / changeFileExt(extractFileTrunk(d), "html"), d]) [c.nimrodArgs, destPath / changeFileExt(splitFile(d).name, "html"), d])
proc main(c: var TConfigData) = proc main(c: var TConfigData) =
const const

View file

@ -2,8 +2,9 @@
import strutils, os import strutils, os
proc newName(f: string): string = proc newName(f: string): string =
return extractDir(f) / "trim_" & extractFilename(f) var (dir, name, ext) = splitFile(f)
return dir / "trim_" & name & ext
proc walker(dir: string) = proc walker(dir: string) =
for kind, path in walkDir(dir): for kind, path in walkDir(dir):
@ -11,7 +12,7 @@ proc walker(dir: string) =
of pcFile: of pcFile:
moveFile(newName(path), path) moveFile(newName(path), path)
# test if installation still works: # test if installation still works:
if executeShellCommand(r"nimrod c --force_build tests\tlastmod") == 0: if execShellCmd(r"nimrod c --force_build tests\tlastmod") == 0:
echo "Optional: ", path echo "Optional: ", path
removeFile(newName(path)) removeFile(newName(path))
else: else:

View file

@ -52,6 +52,7 @@ Nimrod is efficient
from pointers to manually managed memory. from pointers to manually managed memory.
* Zero-overhead iterators. * Zero-overhead iterators.
* Cross-module inlining. * Cross-module inlining.
* Dynamic method binding with inlining and without virtual method table.
* Compile time evaluation of user-defined functions. * Compile time evaluation of user-defined functions.
* Whole program dead code elimination: Only *used functions* are included in * Whole program dead code elimination: Only *used functions* are included in
the executable. the executable.
@ -78,10 +79,8 @@ Nimrod is elegant
Nimrod's syntax is flexible enough. Nimrod's syntax is flexible enough.
* Yet Nimrod can be parsed with an LL(1) parser. * Yet Nimrod can be parsed with an LL(1) parser.
* Statements are grouped by indentation but can span multiple lines. * Statements are grouped by indentation but can span multiple lines.
Indentation must not contain tabulators so **the compiler always sees Indentation must not contain tabulators so the compiler always sees
the code the same way as you do**. the code the same way as you do.
* Nimrod is a simple language that leads to simple programs. However,
the language is not crippled in the name of simplicity.
Nimrod plays nice with others Nimrod plays nice with others
@ -97,11 +96,10 @@ Nimrod plays nice with others
* Nimrod's documentation syntax is a subset of the wonderfully readable plaintext * Nimrod's documentation syntax is a subset of the wonderfully readable plaintext
markup syntax markup syntax
`reStructuredText <http://docutils.sourceforge.net/docs/user/rst/quickref.html>`_. `reStructuredText <http://docutils.sourceforge.net/docs/user/rst/quickref.html>`_.
**The documentation generator is very flexible; this website has been generated The documentation generator is very flexible; this website has been generated
with it!** with it!
Roadmap to 1.0 Roadmap to 1.0
============== ==============

View file

@ -21,7 +21,7 @@ Additions
--------- ---------
- implemented generic types and two phase symbol lookup in generic routines - implemented generic types and two phase symbol lookup in generic routines
- template parameters can now be real types - template parameters can now have real types
- implemented generalized raw string literals: ``ident"abc"`` is a shortcut for - implemented generalized raw string literals: ``ident"abc"`` is a shortcut for
``ident(r"abc")`` ``ident(r"abc")``
- in overloading resolution iterators are separated from procs; iterators now - in overloading resolution iterators are separated from procs; iterators now

View file

@ -21,11 +21,11 @@ FAQ: question
file: ticker file: ticker
[Documentation] [Documentation]
doc: "endb;intern;lib;manual;tut1;tut2;nimrodc;overview" doc: "endb;intern;apis;lib;manual;tut1;tut2;nimrodc;overview"
srcdoc: "system.nim;pure/os;pure/strutils;pure/regexprs;pure/math" srcdoc: "system.nim;pure/os;pure/strutils;pure/regexprs;pure/math"
srcdoc: "pure/complex;pure/times;pure/osproc" srcdoc: "pure/complex;pure/times;pure/osproc;pure/pegs;pure/dynlib"
srcdoc: "pure/parseopt;pure/hashes;pure/strtabs;pure/lexbase" srcdoc: "pure/parseopt;pure/hashes;pure/strtabs;pure/lexbase"
srcdoc: "pure/parsecfg;pure/parsexml;pure/parsecsv" srcdoc: "pure/parsecfg;pure/parsexml;pure/parsecsv;pure/parsesql"
srcdoc: "pure/streams;pure/terminal;pure/cgi;impure/web;pure/unicode" srcdoc: "pure/streams;pure/terminal;pure/cgi;impure/web;pure/unicode"
srcdoc: "impure/zipfiles;pure/xmlgen;pure/macros" srcdoc: "impure/zipfiles;pure/xmlgen;pure/macros"