big rename

This commit is contained in:
Araq 2014-08-27 23:42:51 +02:00
commit 11b6958755
98 changed files with 2491 additions and 2341 deletions

View file

@ -1,6 +1,6 @@
# #
# #
# The Nimrod Compiler # The Nim Compiler
# (c) Copyright 2013 Andreas Rumpf # (c) Copyright 2013 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this

View file

@ -1,6 +1,6 @@
# #
# #
# The Nimrod Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2014 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this

View file

@ -1,5 +1,5 @@
# Configuration file for the Nimrod Compiler. # Configuration file for the Nim Compiler.
# (c) 2013 Andreas Rumpf # (c) 2014 Andreas Rumpf
# Feel free to edit the default values as you need. # Feel free to edit the default values as you need.
@ -13,6 +13,8 @@ cc = gcc
arm.linux.gcc.exe = "arm-linux-gcc" arm.linux.gcc.exe = "arm-linux-gcc"
arm.linux.gcc.linkerexe = "arm-linux-gcc" arm.linux.gcc.linkerexe = "arm-linux-gcc"
cs:partial
path="$lib/core" path="$lib/core"
path="$lib/pure" path="$lib/pure"
path="$lib/pure/collections" path="$lib/pure/collections"

File diff suppressed because it is too large Load diff

View file

@ -180,7 +180,7 @@ So it is not necessary to write ``peg" 'abc' "`` in the above example.
Examples Examples
-------- --------
Check if `s` matches Nimrod's "while" keyword: Check if `s` matches Nim's "while" keyword:
.. code-block:: nimrod .. code-block:: nimrod
s =~ peg" y'while'" s =~ peg" y'while'"
@ -212,7 +212,7 @@ example ``*`` should not be greedy, so ``\[.*?\]`` should be used instead.
PEG construction PEG construction
---------------- ----------------
There are two ways to construct a PEG in Nimrod code: There are two ways to construct a PEG in Nim code:
(1) Parsing a string into an AST which consists of `TPeg` nodes with the (1) Parsing a string into an AST which consists of `TPeg` nodes with the
`peg` proc. `peg` proc.
(2) Constructing the AST directly with proc calls. This method does not (2) Constructing the AST directly with proc calls. This method does not

View file

@ -1,9 +1,9 @@
======================== =====================
Nimrod Tutorial (Part I) Nim Tutorial (Part I)
======================== =====================
:Author: Andreas Rumpf :Author: Andreas Rumpf
:Version: |nimrodversion| :Version: |nimversion|
.. contents:: .. contents::
@ -16,7 +16,7 @@ Introduction
</p></blockquote> </p></blockquote>
This document is a tutorial for the programming language *Nimrod*. This document is a tutorial for the programming language *Nim*.
This tutorial assumes that you are familiar with basic programming concepts This tutorial assumes that you are familiar with basic programming concepts
like variables, types or statements but is kept very basic. The `manual like variables, types or statements but is kept very basic. The `manual
<manual.html>`_ contains many more examples of the advanced language features. <manual.html>`_ contains many more examples of the advanced language features.
@ -28,7 +28,7 @@ The first program
We start the tour with a modified "hello world" program: We start the tour with a modified "hello world" program:
.. code-block:: Nimrod .. code-block:: Nim
# This is a comment # This is a comment
echo("What's your name? ") echo("What's your name? ")
var name: string = readLine(stdin) var name: string = readLine(stdin)
@ -37,30 +37,30 @@ We start the tour with a modified "hello world" program:
Save this code to the file "greetings.nim". Now compile and run it:: Save this code to the file "greetings.nim". Now compile and run it::
nimrod compile --run greetings.nim nim compile --run greetings.nim
With the ``--run`` `switch <nimrodc.html#command-line-switches>`_ Nimrod With the ``--run`` `switch <nimc.html#command-line-switches>`_ Nim
executes the file automatically after compilation. You can give your program executes the file automatically after compilation. You can give your program
command line arguments by appending them after the filename:: command line arguments by appending them after the filename::
nimrod compile --run greetings.nim arg1 arg2 nim compile --run greetings.nim arg1 arg2
Commonly used commands and switches have abbreviations, so you can also use:: Commonly used commands and switches have abbreviations, so you can also use::
nimrod c -r greetings.nim nim c -r greetings.nim
To compile a release version use:: To compile a release version use::
nimrod c -d:release greetings.nim nim c -d:release greetings.nim
By default the Nimrod compiler generates a large amount of runtime checks By default the Nim compiler generates a large amount of runtime checks
aiming for your debugging pleasure. With ``-d:release`` these checks are aiming for your debugging pleasure. With ``-d:release`` these checks are
`turned off and optimizations are turned on `turned off and optimizations are turned on
<nimrodc.html#compile-time-symbols>`_. <nimc.html#compile-time-symbols>`_.
Though it should be pretty obvious what the program does, I will explain the Though it should be pretty obvious what the program does, I will explain the
syntax: statements which are not indented are executed when the program syntax: statements which are not indented are executed when the program
starts. Indentation is Nimrod's way of grouping statements. Indentation is starts. Indentation is Nim's way of grouping statements. Indentation is
done with spaces only, tabulators are not allowed. done with spaces only, tabulators are not allowed.
String literals are enclosed in double quotes. The ``var`` statement declares String literals are enclosed in double quotes. The ``var`` statement declares
@ -70,11 +70,11 @@ compiler knows that `readLine <system.html#readLine,TFile>`_ returns a string,
you can leave out the type in the declaration (this is called `local type you can leave out the type in the declaration (this is called `local type
inference`:idx:). So this will work too: inference`:idx:). So this will work too:
.. code-block:: Nimrod .. code-block:: Nim
var name = readLine(stdin) var name = readLine(stdin)
Note that this is basically the only form of type inference that exists in Note that this is basically the only form of type inference that exists in
Nimrod: it is a good compromise between brevity and readability. Nim: it is a good compromise between brevity and readability.
The "hello world" program contains several identifiers that are already known The "hello world" program contains several identifiers that are already known
to the compiler: ``echo``, `readLine <system.html#readLine,TFile>`_, etc. to the compiler: ``echo``, `readLine <system.html#readLine,TFile>`_, etc.
@ -85,8 +85,8 @@ imported by any other module.
Lexical elements Lexical elements
================ ================
Let us look at Nimrod's lexical elements in more detail: like other Let us look at Nim's lexical elements in more detail: like other
programming languages Nimrod consists of (string) literals, identifiers, programming languages Nim consists of (string) literals, identifiers,
keywords, comments, operators, and other punctuation marks. keywords, comments, operators, and other punctuation marks.
@ -97,7 +97,7 @@ String literals are enclosed in double quotes; character literals in single
quotes. Special characters are escaped with ``\``: ``\n`` means newline, ``\t`` quotes. Special characters are escaped with ``\``: ``\n`` means newline, ``\t``
means tabulator, etc. There are also *raw* string literals: means tabulator, etc. There are also *raw* string literals:
.. code-block:: Nimrod .. code-block:: Nim
r"C:\program files\nim" r"C:\program files\nim"
In raw literals the backslash is not an escape character. In raw literals the backslash is not an escape character.
@ -115,7 +115,7 @@ Comments start anywhere outside a string or character literal with the
hash character ``#``. Documentation comments start with ``##``. Multiline hash character ``#``. Documentation comments start with ``##``. Multiline
comments need to be aligned at the same column: comments need to be aligned at the same column:
.. code-block:: nimrod .. code-block:: nim
i = 0 # This is a single comment over multiple lines belonging to the i = 0 # This is a single comment over multiple lines belonging to the
# assignment statement. # assignment statement.
@ -128,7 +128,7 @@ comments need to be aligned at the same column:
The alignment requirement does not hold if the preceding comment piece ends in The alignment requirement does not hold if the preceding comment piece ends in
a backslash: a backslash:
.. code-block:: nimrod .. code-block:: nim
type type
TMyObject {.final, pure, acyclic.} = object # comment continues: \ TMyObject {.final, pure, acyclic.} = object # comment continues: \
# we have lots of space here to comment 'TMyObject'. # we have lots of space here to comment 'TMyObject'.
@ -151,15 +151,15 @@ the syntax, watch their indentation:
**Note**: To comment out a large piece of code, it is often better to use a **Note**: To comment out a large piece of code, it is often better to use a
``when false:`` statement. ``when false:`` statement.
.. code-block:: nimrod .. code-block:: nim
when false: when false:
brokenCode() brokenCode()
Another option is to use the `discard statement`_ together with *long string Another option is to use the `discard statement`_ together with *long string
literals* to create block comments: literals* to create block comments:
.. code-block:: nimrod .. code-block:: nim
discard """ You can have any Nimrod code text commented discard """ You can have any Nim code text commented
out inside this with no indentation restrictions. out inside this with no indentation restrictions.
yes("May I ask a pointless question?") """ yes("May I ask a pointless question?") """
@ -204,7 +204,7 @@ to a storage location:
``=`` is the *assignment operator*. The assignment operator cannot be ``=`` is the *assignment operator*. The assignment operator cannot be
overloaded, overwritten or forbidden, but this might change in a future version overloaded, overwritten or forbidden, but this might change in a future version
of Nimrod. You can declare multiple variables with a single assignment of Nim. You can declare multiple variables with a single assignment
statement and all the variables will have the same value: statement and all the variables will have the same value:
.. code-block:: .. code-block::
@ -229,7 +229,7 @@ Constants are symbols which are bound to a value. The constant's value
cannot change. The compiler must be able to evaluate the expression in a cannot change. The compiler must be able to evaluate the expression in a
constant declaration at compile time: constant declaration at compile time:
.. code-block:: nimrod .. code-block:: nim
const x = "abc" # the constant x contains the string "abc" const x = "abc" # the constant x contains the string "abc"
Indentation can be used after the ``const`` keyword to list a whole section of Indentation can be used after the ``const`` keyword to list a whole section of
@ -277,7 +277,7 @@ If statement
The if statement is one way to branch the control flow: The if statement is one way to branch the control flow:
.. code-block:: nimrod .. code-block:: nim
let name = readLine(stdin) let name = readLine(stdin)
if name == "": if name == "":
echo("Poor soul, you lost your name?") echo("Poor soul, you lost your name?")
@ -298,7 +298,7 @@ Case statement
Another way to branch is provided by the case statement. A case statement is Another way to branch is provided by the case statement. A case statement is
a multi-branch: a multi-branch:
.. code-block:: nimrod .. code-block:: nim
let name = readLine(stdin) let name = readLine(stdin)
case name case name
of "": of "":
@ -317,7 +317,7 @@ The case statement can deal with integers, other ordinal types and strings.
(What an ordinal type is will be explained soon.) (What an ordinal type is will be explained soon.)
For integers or other ordinal types value ranges are also possible: For integers or other ordinal types value ranges are also possible:
.. code-block:: nimrod .. code-block:: nim
# this statement will be explained later: # this statement will be explained later:
from strutils import parseInt from strutils import parseInt
@ -333,7 +333,7 @@ every value that ``n`` may contain, but the code only handles the values
(though it is possible thanks to the range notation), we fix this by telling (though it is possible thanks to the range notation), we fix this by telling
the compiler that for every other value nothing should be done: the compiler that for every other value nothing should be done:
.. code-block:: nimrod .. code-block:: nim
... ...
case n case n
of 0..2, 4..7: echo("The number is in the set: {0, 1, 2, 4, 5, 6, 7}") of 0..2, 4..7: echo("The number is in the set: {0, 1, 2, 4, 5, 6, 7}")
@ -355,7 +355,7 @@ While statement
The while statement is a simple looping construct: The while statement is a simple looping construct:
.. code-block:: nimrod .. code-block:: nim
echo("What's your name? ") echo("What's your name? ")
var name = readLine(stdin) var name = readLine(stdin)
@ -375,7 +375,7 @@ The ``for`` statement is a construct to loop over any element an *iterator*
provides. The example uses the built-in `countup <system.html#countup>`_ provides. The example uses the built-in `countup <system.html#countup>`_
iterator: iterator:
.. code-block:: nimrod .. code-block:: nim
echo("Counting to ten: ") echo("Counting to ten: ")
for i in countup(1, 10): for i in countup(1, 10):
echo($i) echo($i)
@ -387,7 +387,7 @@ other types into a string. The variable ``i`` is implicitly declared by the
<system.html#countup>`_ returns. ``i`` runs through the values 1, 2, .., 10. <system.html#countup>`_ returns. ``i`` runs through the values 1, 2, .., 10.
Each value is ``echo``-ed. This code does the same: Each value is ``echo``-ed. This code does the same:
.. code-block:: nimrod .. code-block:: nim
echo("Counting to 10: ") echo("Counting to 10: ")
var i = 1 var i = 1
while i <= 10: while i <= 10:
@ -397,16 +397,16 @@ Each value is ``echo``-ed. This code does the same:
Counting down can be achieved as easily (but is less often needed): Counting down can be achieved as easily (but is less often needed):
.. code-block:: nimrod .. code-block:: nim
echo("Counting down from 10 to 1: ") echo("Counting down from 10 to 1: ")
for i in countdown(10, 1): for i in countdown(10, 1):
echo($i) echo($i)
# --> Outputs 10 9 8 7 6 5 4 3 2 1 on different lines # --> Outputs 10 9 8 7 6 5 4 3 2 1 on different lines
Since counting up occurs so often in programs, Nimrod also has a `.. Since counting up occurs so often in programs, Nim also has a `..
<system.html#...i,S,T>`_ iterator that does the same: <system.html#...i,S,T>`_ iterator that does the same:
.. code-block:: nimrod .. code-block:: nim
for i in 1..10: for i in 1..10:
... ...
@ -417,7 +417,7 @@ Control flow statements have a feature not covered yet: they open a
new scope. This means that in the following example, ``x`` is not accessible new scope. This means that in the following example, ``x`` is not accessible
outside the loop: outside the loop:
.. code-block:: nimrod .. code-block:: nim
while false: while false:
var x = "hi" var x = "hi"
echo(x) # does not work echo(x) # does not work
@ -426,7 +426,7 @@ A while (for) statement introduces an implicit block. Identifiers
are only visible within the block they have been declared. The ``block`` are only visible within the block they have been declared. The ``block``
statement can be used to open a new block explicitly: statement can be used to open a new block explicitly:
.. code-block:: nimrod .. code-block:: nim
block myblock: block myblock:
var x = "hi" var x = "hi"
echo(x) # does not work either echo(x) # does not work either
@ -440,7 +440,7 @@ 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 can leave a ``while``, ``for``, or a ``block`` statement. It leaves the
innermost construct, unless a label of a block is given: innermost construct, unless a label of a block is given:
.. code-block:: nimrod .. code-block:: nim
block myblock: block myblock:
echo("entering block") echo("entering block")
while true: while true:
@ -461,7 +461,7 @@ Continue statement
Like in many other programming languages, a ``continue`` statement starts Like in many other programming languages, a ``continue`` statement starts
the next iteration immediately: the next iteration immediately:
.. code-block:: nimrod .. code-block:: nim
while true: while true:
let x = readLine(stdin) let x = readLine(stdin)
if x == "": continue if x == "": continue
@ -473,7 +473,7 @@ When statement
Example: Example:
.. code-block:: nimrod .. code-block:: nim
when system.hostOS == "windows": when system.hostOS == "windows":
echo("running on Windows!") echo("running on Windows!")
@ -504,17 +504,17 @@ possible.
Statements and indentation Statements and indentation
========================== ==========================
Now that we covered the basic control flow statements, let's return to Nimrod Now that we covered the basic control flow statements, let's return to Nim
indentation rules. indentation rules.
In Nimrod there is a distinction between *simple statements* and *complex In Nim there is a distinction between *simple statements* and *complex
statements*. *Simple statements* cannot contain other statements: statements*. *Simple statements* cannot contain other statements:
Assignment, procedure calls or the ``return`` statement belong to the simple Assignment, procedure calls or the ``return`` statement belong to the simple
statements. *Complex statements* like ``if``, ``when``, ``for``, ``while`` can statements. *Complex statements* like ``if``, ``when``, ``for``, ``while`` can
contain other statements. To avoid ambiguities, complex statements always have contain other statements. To avoid ambiguities, complex statements always have
to be indented, but single simple statements do not: to be indented, but single simple statements do not:
.. code-block:: nimrod .. code-block:: nim
# no indentation needed for single assignment statement: # no indentation needed for single assignment statement:
if x: x = false if x: x = false
@ -535,7 +535,7 @@ to be indented, but single simple statements do not:
condition in an if statement is an example for an expression. Expressions can condition in an if statement is an example for an expression. Expressions can
contain indentation at certain places for better readability: contain indentation at certain places for better readability:
.. code-block:: nimrod .. code-block:: nim
if thisIsaLongCondition() and if thisIsaLongCondition() and
thisIsAnotherLongCondition(1, thisIsAnotherLongCondition(1,
@ -548,7 +548,7 @@ an open parenthesis and after commas.
With parenthesis and semicolons ``(;)`` you can use statements where only With parenthesis and semicolons ``(;)`` you can use statements where only
an expression is allowed: an expression is allowed:
.. code-block:: nimrod .. code-block:: nim
# computes fac(4) at compile time: # computes fac(4) at compile time:
const fac4 = (var x = 1; for i in 1..4: x *= i; x) const fac4 = (var x = 1; for i in 1..4: x *= i; x)
@ -558,10 +558,10 @@ Procedures
To define new commands like `echo <system.html#echo>`_ and `readLine To define new commands like `echo <system.html#echo>`_ and `readLine
<system.html#readLine,TFile>`_ in the examples, the concept of a `procedure` <system.html#readLine,TFile>`_ in the examples, the concept of a `procedure`
is needed. (Some languages call them *methods* or *functions*.) In Nimrod new is needed. (Some languages call them *methods* or *functions*.) In Nim new
procedures are defined with the ``proc`` keyword: procedures are defined with the ``proc`` keyword:
.. code-block:: nimrod .. code-block:: nim
proc yes(question: string): bool = proc yes(question: string): bool =
echo(question, " (y/n)") echo(question, " (y/n)")
while true: while true:
@ -597,7 +597,7 @@ shorthand for ``return result``. The ``result`` value is always returned
automatically at the end a procedure if there is no ``return`` statement at automatically at the end a procedure if there is no ``return`` statement at
the exit. the exit.
.. code-block:: nimrod .. code-block:: nim
proc sumTillNegative(x: varargs[int]): int = proc sumTillNegative(x: varargs[int]): int =
for i in x: for i in x:
if i < 0: if i < 0:
@ -624,7 +624,7 @@ most efficient way. If a mutable variable is needed inside the procedure, it has
to be declared with ``var`` in the procedure body. Shadowing the parameter name to be declared with ``var`` in the procedure body. Shadowing the parameter name
is possible, and actually an idiom: is possible, and actually an idiom:
.. code-block:: nimrod .. code-block:: nim
proc printSeq(s: seq, nprinted: int = -1) = proc printSeq(s: seq, nprinted: int = -1) =
var nprinted = if nprinted == -1: s.len else: min(nprinted, s.len) var nprinted = if nprinted == -1: s.len else: min(nprinted, s.len)
for i in 0 .. <nprinted: for i in 0 .. <nprinted:
@ -633,7 +633,7 @@ is possible, and actually an idiom:
If the procedure needs to modify the argument for the If the procedure needs to modify the argument for the
caller, a ``var`` parameter can be used: caller, a ``var`` parameter can be used:
.. code-block:: nimrod .. code-block:: nim
proc divmod(a, b: int; res, remainder: var int) = proc divmod(a, b: int; res, remainder: var int) =
res = a div b # integer division res = a div b # integer division
remainder = a mod b # integer modulo operation remainder = a mod b # integer modulo operation
@ -653,17 +653,17 @@ a tuple as a return value instead of using var parameters.
Discard statement Discard statement
----------------- -----------------
To call a procedure that returns a value just for its side effects and ignoring To call a procedure that returns a value just for its side effects and ignoring
its return value, a ``discard`` statement **has** to be used. Nimrod does not its return value, a ``discard`` statement **has** to be used. Nim does not
allow to silently throw away a return value: allow to silently throw away a return value:
.. code-block:: nimrod .. code-block:: nim
discard yes("May I ask a pointless question?") discard yes("May I ask a pointless question?")
The return value can be ignored implicitly if the called proc/iterator has The return value can be ignored implicitly if the called proc/iterator has
been declared with the ``discardable`` pragma: been declared with the ``discardable`` pragma:
.. code-block:: nimrod .. code-block:: nim
proc p(x, y: int): int {.discardable.} = proc p(x, y: int): int {.discardable.} =
return x + y return x + y
@ -681,7 +681,7 @@ parameters appear. This is especially true for procedures that construct a
complex data type. Therefore the arguments to a procedure can be named, so complex data type. Therefore the arguments to a procedure can be named, so
that it is clear which argument belongs to which parameter: that it is clear which argument belongs to which parameter:
.. code-block:: nimrod .. code-block:: nim
proc createWindow(x, y, width, height: int; title: string; proc createWindow(x, y, width, height: int; title: string;
show: bool): Window = show: bool): Window =
... ...
@ -693,7 +693,7 @@ Now that we use named arguments to call ``createWindow`` the argument order
does not matter anymore. Mixing named arguments with ordered arguments is does not matter anymore. Mixing named arguments with ordered arguments is
also possible, but not very readable: also possible, but not very readable:
.. code-block:: nimrod .. code-block:: nim
var w = createWindow(0, 0, title = "My Application", var w = createWindow(0, 0, title = "My Application",
height = 600, width = 800, true) height = 600, width = 800, true)
@ -706,7 +706,7 @@ To make the ``createWindow`` proc easier to use it should provide `default
values`, these are values that are used as arguments if the caller does not values`, these are values that are used as arguments if the caller does not
specify them: specify them:
.. code-block:: nimrod .. code-block:: nim
proc createWindow(x = 0, y = 0, width = 500, height = 700, proc createWindow(x = 0, y = 0, width = 500, height = 700,
title = "unknown", title = "unknown",
show = true): Window = show = true): Window =
@ -723,9 +723,9 @@ no need to write ``title: string = "unknown"``, for example.
Overloaded procedures Overloaded procedures
--------------------- ---------------------
Nimrod provides the ability to overload procedures similar to C++: Nim provides the ability to overload procedures similar to C++:
.. code-block:: nimrod .. code-block:: nim
proc toString(x: int): string = ... proc toString(x: int): string = ...
proc toString(x: bool): string = proc toString(x: bool): string =
if x: result = "true" if x: result = "true"
@ -735,7 +735,7 @@ Nimrod provides the ability to overload procedures similar to C++:
echo(toString(true)) # calls the toString(x: bool) proc echo(toString(true)) # calls the toString(x: bool) proc
(Note that ``toString`` is usually the `$ <system.html#$>`_ operator in (Note that ``toString`` is usually the `$ <system.html#$>`_ operator in
Nimrod.) The compiler chooses the most appropriate proc for the ``toString`` Nim.) The compiler chooses the most appropriate proc for the ``toString``
calls. How this overloading resolution algorithm works exactly is not calls. How this overloading resolution algorithm works exactly is not
discussed here (it will be specified in the manual soon). However, it does discussed here (it will be specified in the manual soon). However, it does
not lead to nasty surprises and is based on a quite simple unification not lead to nasty surprises and is based on a quite simple unification
@ -744,13 +744,13 @@ algorithm. Ambiguous calls are reported as errors.
Operators Operators
--------- ---------
The Nimrod library makes heavy use of overloading - one reason for this is that The Nim library makes heavy use of overloading - one reason for this is that
each operator like ``+`` is a just an overloaded proc. The parser lets you each operator like ``+`` is a just an overloaded proc. The parser lets you
use operators in `infix notation` (``a + b``) or `prefix notation` (``+ a``). use operators in `infix notation` (``a + b``) or `prefix notation` (``+ a``).
An infix operator always receives two arguments, a prefix operator always one. An infix operator always receives two arguments, a prefix operator always one.
Postfix operators are not possible, because this would be ambiguous: does Postfix operators are not possible, because this would be ambiguous: does
``a @ @ b`` mean ``(a) @ (@b)`` or ``(a@) @ (b)``? It always means ``a @ @ b`` mean ``(a) @ (@b)`` or ``(a@) @ (b)``? It always means
``(a) @ (@b)``, because there are no postfix operators in Nimrod. ``(a) @ (@b)``, because there are no postfix operators in Nim.
Apart from a few built-in keyword operators such as ``and``, ``or``, ``not``, Apart from a few built-in keyword operators such as ``and``, ``or``, ``not``,
operators always consist of these characters: operators always consist of these characters:
@ -764,7 +764,7 @@ can be found in the manual.
To define a new operator enclose the operator in backticks "``": To define a new operator enclose the operator in backticks "``":
.. code-block:: nimrod .. code-block:: nim
proc `$` (x: myDataType): string = ... proc `$` (x: myDataType): string = ...
# now the $ operator also works with myDataType, overloading resolution # now the $ operator also works with myDataType, overloading resolution
# ensures that $ works for built-in types just like before # ensures that $ works for built-in types just like before
@ -772,7 +772,7 @@ To define a new operator enclose the operator in backticks "``":
The "``" notation can also be used to call an operator just like any other The "``" notation can also be used to call an operator just like any other
procedure: procedure:
.. code-block:: nimrod .. code-block:: nim
if `==`( `+`(3, 4), 7): echo("True") if `==`( `+`(3, 4), 7): echo("True")
@ -783,7 +783,7 @@ Every variable, procedure, etc. needs to be declared before it can be used.
(The reason for this is compilation efficiency.) (The reason for this is compilation efficiency.)
However, this cannot be done for mutually recursive procedures: However, this cannot be done for mutually recursive procedures:
.. code-block:: nimrod .. code-block:: nim
# forward declaration: # forward declaration:
proc even(n: int): bool proc even(n: int): bool
@ -810,7 +810,7 @@ Iterators
Let's return to the boring counting example: Let's return to the boring counting example:
.. code-block:: nimrod .. code-block:: nim
echo("Counting to ten: ") echo("Counting to ten: ")
for i in countup(1, 10): for i in countup(1, 10):
echo($i) echo($i)
@ -818,7 +818,7 @@ Let's return to the boring counting example:
Can a `countup <system.html#countup>`_ proc be written that supports this Can a `countup <system.html#countup>`_ proc be written that supports this
loop? Lets try: loop? Lets try:
.. code-block:: nimrod .. code-block:: nim
proc countup(a, b: int): int = proc countup(a, b: int): int =
var res = a var res = a
while res <= b: while res <= b:
@ -831,7 +831,7 @@ finished. This *return and continue* is called a `yield` statement. Now
the only thing left to do is to replace the ``proc`` keyword by ``iterator`` the only thing left to do is to replace the ``proc`` keyword by ``iterator``
and there it is - our first iterator: and there it is - our first iterator:
.. code-block:: nimrod .. code-block:: nim
iterator countup(a, b: int): int = iterator countup(a, b: int): int =
var res = a var res = a
while res <= b: while res <= b:
@ -868,7 +868,7 @@ that are available for them in detail.
Booleans Booleans
-------- --------
The boolean type is named ``bool`` in Nimrod and consists of the two The boolean type is named ``bool`` in Nim and consists of the two
pre-defined values ``true`` and ``false``. Conditions in while, pre-defined values ``true`` and ``false``. Conditions in while,
if, elif, when statements need to be of type bool. if, elif, when statements need to be of type bool.
@ -876,7 +876,7 @@ The operators ``not, and, or, xor, <, <=, >, >=, !=, ==`` are defined
for the bool type. The ``and`` and ``or`` operators perform short-cut for the bool type. The ``and`` and ``or`` operators perform short-cut
evaluation. Example: evaluation. Example:
.. code-block:: nimrod .. code-block:: nim
while p != nil and p.name != "xyz": while p != nil and p.name != "xyz":
# p.name is not evaluated if p == nil # p.name is not evaluated if p == nil
@ -885,7 +885,7 @@ evaluation. Example:
Characters Characters
---------- ----------
The `character type` is named ``char`` in Nimrod. Its size is one byte. The `character type` is named ``char`` in Nim. Its size is one byte.
Thus it cannot represent an UTF-8 character, but a part of it. Thus it cannot represent an UTF-8 character, but a part of it.
The reason for this is efficiency: for the overwhelming majority of use-cases, The reason for this is efficiency: for the overwhelming majority of use-cases,
the resulting programs will still handle UTF-8 properly as UTF-8 was specially the resulting programs will still handle UTF-8 properly as UTF-8 was specially
@ -900,13 +900,13 @@ Converting from an integer to a ``char`` is done with the ``chr`` proc.
Strings Strings
------- -------
String variables in Nimrod are **mutable**, so appending to a string String variables in Nim are **mutable**, so appending to a string
is quite efficient. Strings in Nimrod are both zero-terminated and have a is quite efficient. Strings in Nim are both zero-terminated and have a
length field. One can retrieve a string's length with the builtin ``len`` length field. One can retrieve a string's length with the builtin ``len``
procedure; the length never counts the terminating zero. Accessing the procedure; the length never counts the terminating zero. Accessing the
terminating zero is no error and often leads to simpler code: terminating zero is no error and often leads to simpler code:
.. code-block:: nimrod .. code-block:: nim
if s[i] == 'a' and s[i+1] == 'b': if s[i] == 'a' and s[i+1] == 'b':
# no need to check whether ``i < len(s)``! # no need to check whether ``i < len(s)``!
... ...
@ -929,14 +929,14 @@ object on the heap, so there is a trade-off to be made here.
Integers Integers
-------- --------
Nimrod has these integer types built-in: Nim has these integer types built-in:
``int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64``. ``int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64``.
The default integer type is ``int``. Integer literals can have a *type suffix* The default integer type is ``int``. Integer literals can have a *type suffix*
to mark them to be of another integer type: to mark them to be of another integer type:
.. code-block:: nimrod .. code-block:: nim
let let
x = 0 # x is of type ``int`` x = 0 # x is of type ``int``
y = 0'i8 # y is of type ``int8`` y = 0'i8 # y is of type ``int8``
@ -964,7 +964,7 @@ cannot be detected at compile time).
Floats Floats
------ ------
Nimrod has these floating point types built-in: ``float float32 float64``. Nim has these floating point types built-in: ``float float32 float64``.
The default float type is ``float``. In the current implementation, The default float type is ``float``. In the current implementation,
``float`` is always 64 bit wide. ``float`` is always 64 bit wide.
@ -972,7 +972,7 @@ The default float type is ``float``. In the current implementation,
Float literals can have a *type suffix* to mark them to be of another float Float literals can have a *type suffix* to mark them to be of another float
type: type:
.. code-block:: nimrod .. code-block:: nim
var var
x = 0.0 # x is of type ``float`` x = 0.0 # x is of type ``float``
y = 0.0'f32 # y is of type ``float32`` y = 0.0'f32 # y is of type ``float32``
@ -1001,11 +1001,11 @@ having to write its ``$`` operator. You can use then the `repr
graphs with cycles. The following example shows that even for basic types graphs with cycles. The following example shows that even for basic types
there is a difference between the ``$`` and ``repr`` outputs: there is a difference between the ``$`` and ``repr`` outputs:
.. code-block:: nimrod .. code-block:: nim
var var
myBool = true myBool = true
myCharacter = 'n' myCharacter = 'n'
myString = "nimrod" myString = "nim"
myInteger = 42 myInteger = 42
myFloat = 3.14 myFloat = 3.14
echo($myBool, ":", repr(myBool)) echo($myBool, ":", repr(myBool))
@ -1013,7 +1013,7 @@ there is a difference between the ``$`` and ``repr`` outputs:
echo($myCharacter, ":", repr(myCharacter)) echo($myCharacter, ":", repr(myCharacter))
# --> n:'n' # --> n:'n'
echo($myString, ":", repr(myString)) echo($myString, ":", repr(myString))
# --> nimrod:0x10fa8c050"nimrod" # --> nim:0x10fa8c050"nim"
echo($myInteger, ":", repr(myInteger)) echo($myInteger, ":", repr(myInteger))
# --> 42:42 # --> 42:42
echo($myFloat, ":", repr(myFloat)) echo($myFloat, ":", repr(myFloat))
@ -1023,9 +1023,9 @@ there is a difference between the ``$`` and ``repr`` outputs:
Advanced types Advanced types
============== ==============
In Nimrod new types can be defined within a ``type`` statement: In Nim new types can be defined within a ``type`` statement:
.. code-block:: nimrod .. code-block:: nim
type type
biggestInt = int64 # biggest integer type that is available biggestInt = int64 # biggest integer type that is available
biggestFloat = float64 # biggest float type that is available biggestFloat = float64 # biggest float type that is available
@ -1041,7 +1041,7 @@ limited set. This set consists of ordered symbols. Each symbol is mapped
to an integer value internally. The first symbol is represented to an integer value internally. The first symbol is represented
at runtime by 0, the second by 1 and so on. Example: at runtime by 0, the second by 1 and so on. Example:
.. code-block:: nimrod .. code-block:: nim
type type
TDirection = enum TDirection = enum
@ -1050,7 +1050,7 @@ at runtime by 0, the second by 1 and so on. Example:
var x = south # `x` is of type `TDirection`; its value is `south` var x = south # `x` is of type `TDirection`; its value is `south`
echo($x) # writes "south" to `stdout` echo($x) # writes "south" to `stdout`
(To prefix a new type with the letter ``T`` is a convention in Nimrod.) (To prefix a new type with the letter ``T`` is a convention in Nim.)
All comparison operators can be used with enumeration types. All comparison operators can be used with enumeration types.
An enumeration's symbol can be qualified to avoid ambiguities: An enumeration's symbol can be qualified to avoid ambiguities:
@ -1066,7 +1066,7 @@ explicitly given is assigned the value of the previous symbol + 1.
An explicit ordered enum can have *holes*: An explicit ordered enum can have *holes*:
.. code-block:: nimrod .. code-block:: nim
type type
TMyEnum = enum TMyEnum = enum
a = 2, b = 4, c = 89 a = 2, b = 4, c = 89
@ -1104,7 +1104,7 @@ Subranges
A subrange type is a range of values from an integer or enumeration type A subrange type is a range of values from an integer or enumeration type
(the base type). Example: (the base type). Example:
.. code-block:: nimrod .. code-block:: nim
type type
TSubrange = range[0..5] TSubrange = range[0..5]
@ -1119,7 +1119,7 @@ type as ``range[0..high(int)]`` (`high <system.html#high>`_ returns the
maximal value). Other programming languages mandate the usage of unsigned maximal value). Other programming languages mandate the usage of unsigned
integers for natural numbers. This is often **wrong**: you don't want unsigned integers for natural numbers. This is often **wrong**: you don't want unsigned
arithmetic (which wraps around) just because the numbers cannot be negative. arithmetic (which wraps around) just because the numbers cannot be negative.
Nimrod's ``Natural`` type helps to avoid this common programming error. Nim's ``Natural`` type helps to avoid this common programming error.
Sets Sets
@ -1134,7 +1134,7 @@ the array has the same type. The array's index type can be any ordinal type.
Arrays can be constructed via ``[]``: Arrays can be constructed via ``[]``:
.. code-block:: nimrod .. code-block:: nim
type type
TIntArray = array[0..5, int] # an array that is indexed with 0..5 TIntArray = array[0..5, int] # an array that is indexed with 0..5
@ -1149,14 +1149,14 @@ Array access is always bounds checked (at compile-time or at runtime). These
checks can be disabled via pragmas or invoking the compiler with the checks can be disabled via pragmas or invoking the compiler with the
``--bound_checks:off`` command line switch. ``--bound_checks:off`` command line switch.
Arrays are value types, like any other Nimrod type. The assignment operator Arrays are value types, like any other Nim type. The assignment operator
copies the whole array contents. copies the whole array contents.
The built-in `len <system.html#len,TOpenArray>`_ proc returns the array's The built-in `len <system.html#len,TOpenArray>`_ proc returns the array's
length. `low(a) <system.html#low>`_ returns the lowest valid index for the length. `low(a) <system.html#low>`_ returns the lowest valid index for the
array `a` and `high(a) <system.html#high>`_ the highest valid index. array `a` and `high(a) <system.html#high>`_ the highest valid index.
.. code-block:: nimrod .. code-block:: nim
type type
TDirection = enum TDirection = enum
north, east, south, west north, east, south, west
@ -1175,13 +1175,13 @@ array `a` and `high(a) <system.html#high>`_ the highest valid index.
The syntax for nested arrays (multidimensional) in other languages is a matter The syntax for nested arrays (multidimensional) in other languages is a matter
of appending more brackets because usually each dimension is restricted to the of appending more brackets because usually each dimension is restricted to the
same index type as the others. In nimrod you can have different dimensions with same index type as the others. In Nim you can have different dimensions with
different index types, so the nesting syntax is slightly different. Building on different index types, so the nesting syntax is slightly different. Building on
the previous example where a level is defined as an array of enums indexed by the previous example where a level is defined as an array of enums indexed by
yet another enum, we can add the following lines to add a light tower type yet another enum, we can add the following lines to add a light tower type
subdivided in height levels accessed through their integer index: subdivided in height levels accessed through their integer index:
.. code-block:: nimrod .. code-block:: nim
type type
TLightTower = array[1..10, TLevelSetting] TLightTower = array[1..10, TLevelSetting]
var var
@ -1200,14 +1200,14 @@ length. Another way of defining the ``TLightTower`` to show better its
nested nature would be to omit the previous definition of the ``TLevelSetting`` nested nature would be to omit the previous definition of the ``TLevelSetting``
type and instead write it embedded directly as the type of the first dimension: type and instead write it embedded directly as the type of the first dimension:
.. code-block:: nimrod .. code-block:: nim
type type
TLightTower = array[1..10, array[north..west, TBlinkLights]] TLightTower = array[1..10, array[north..west, TBlinkLights]]
It is quite frequent to have arrays start at zero, so there's a shortcut syntax It is quite frequent to have arrays start at zero, so there's a shortcut syntax
to specify a range from zero to the specified index minus one: to specify a range from zero to the specified index minus one:
.. code-block:: nimrod .. code-block:: nim
type type
TIntArray = array[0..5, int] # an array that is indexed with 0..5 TIntArray = array[0..5, int] # an array that is indexed with 0..5
TQuickArray = array[6, int] # an array that is indexed with 0..5 TQuickArray = array[6, int] # an array that is indexed with 0..5
@ -1239,7 +1239,7 @@ A sequence may be passed to an openarray parameter.
Example: Example:
.. code-block:: nimrod .. code-block:: nim
var var
x: seq[int] # a sequence of integers x: seq[int] # a sequence of integers
@ -1261,7 +1261,7 @@ value. Here the ``for`` statement is looping over the results from the
`pairs() <system.html#pairs.i,seq[T]>`_ iterator from the `system `pairs() <system.html#pairs.i,seq[T]>`_ iterator from the `system
<system.html>`_ module. Examples: <system.html>`_ module. Examples:
.. code-block:: nimrod .. code-block:: nim
for i in @[3, 4, 5]: for i in @[3, 4, 5]:
echo($i) echo($i)
# --> 3 # --> 3
@ -1299,7 +1299,7 @@ also a means to implement passing a variable number of
arguments to a procedure. The compiler converts the list of arguments arguments to a procedure. The compiler converts the list of arguments
to an array automatically: to an array automatically:
.. code-block:: nimrod .. code-block:: nim
proc myWriteln(f: TFile, a: varargs[string]) = proc myWriteln(f: TFile, a: varargs[string]) =
for s in items(a): for s in items(a):
write(f, s) write(f, s)
@ -1313,7 +1313,7 @@ This transformation is only done if the varargs parameter is the
last parameter in the procedure header. It is also possible to perform last parameter in the procedure header. It is also possible to perform
type conversions in this context: type conversions in this context:
.. code-block:: nimrod .. code-block:: nim
proc myWriteln(f: TFile, a: varargs[string, `$`]) = proc myWriteln(f: TFile, a: varargs[string, `$`]) =
for s in items(a): for s in items(a):
write(f, s) write(f, s)
@ -1336,10 +1336,10 @@ context. A slice is just an object of type TSlice which contains two bounds,
`a` and `b`. By itself a slice is not very useful, but other collection types `a` and `b`. By itself a slice is not very useful, but other collection types
define operators which accept TSlice objects to define ranges. define operators which accept TSlice objects to define ranges.
.. code-block:: nimrod .. code-block:: nim
var var
a = "Nimrod is a progamming language" a = "Nim is a progamming language"
b = "Slices are useless." b = "Slices are useless."
echo a[10..15] # --> 'a prog' echo a[10..15] # --> 'a prog'
@ -1366,7 +1366,7 @@ The assignment operator for tuples copies each component. The notation
``t[i]`` to access the ``i``'th field. Here ``i`` needs to be a constant ``t[i]`` to access the ``i``'th field. Here ``i`` needs to be a constant
integer. integer.
.. code-block:: nimrod .. code-block:: nim
type type
TPerson = tuple[name: string, age: int] # type representing a person: TPerson = tuple[name: string, age: int] # type representing a person:
@ -1411,19 +1411,19 @@ use parenthesis around the values you want to assign the unpacking to,
otherwise you will be assigning the same value to all the individual otherwise you will be assigning the same value to all the individual
variables! Example: variables! Example:
.. code-block:: nimrod .. code-block:: nim
import os import os
let let
path = "usr/local/nimrodc.html" path = "usr/local/nimc.html"
(dir, name, ext) = splitFile(path) (dir, name, ext) = splitFile(path)
baddir, badname, badext = splitFile(path) baddir, badname, badext = splitFile(path)
echo dir # outputs `usr/local` echo dir # outputs `usr/local`
echo name # outputs `nimrodc` echo name # outputs `nimc`
echo ext # outputs `.html` echo ext # outputs `.html`
# All the following output the same line: # All the following output the same line:
# `(dir: usr/local, name: nimrodc, ext: .html)` # `(dir: usr/local, name: nimc, ext: .html)`
echo baddir echo baddir
echo badname echo badname
echo badext echo badext
@ -1431,12 +1431,12 @@ variables! Example:
Tuple unpacking **only** works in ``var`` or ``let`` blocks. The following code Tuple unpacking **only** works in ``var`` or ``let`` blocks. The following code
won't compile: won't compile:
.. code-block:: nimrod .. code-block:: nim
import os import os
var var
path = "usr/local/nimrodc.html" path = "usr/local/nimc.html"
dir, name, ext = "" dir, name, ext = ""
(dir, name, ext) = splitFile(path) (dir, name, ext) = splitFile(path)
@ -1449,7 +1449,7 @@ References (similar to pointers in other programming languages) are a
way to introduce many-to-one relationships. This means different references can way to introduce many-to-one relationships. This means different references can
point to and modify the same location in memory. point to and modify the same location in memory.
Nimrod distinguishes between `traced`:idx: and `untraced`:idx: references. Nim distinguishes between `traced`:idx: and `untraced`:idx: references.
Untraced references are also called *pointers*. Traced references point to Untraced references are also called *pointers*. Traced references point to
objects of a garbage collected heap, untraced references point to objects of a garbage collected heap, untraced references point to
manually allocated objects or to objects somewhere else in memory. Thus manually allocated objects or to objects somewhere else in memory. Thus
@ -1464,7 +1464,7 @@ meaning to retrieve the item the reference points to. The ``.`` (access a
tuple/object field operator) and ``[]`` (array/string/sequence index operator) tuple/object field operator) and ``[]`` (array/string/sequence index operator)
operators perform implicit dereferencing operations for reference types: operators perform implicit dereferencing operations for reference types:
.. code-block:: nimrod .. code-block:: nim
type type
PNode = ref TNode PNode = ref TNode
@ -1489,12 +1489,12 @@ Procedural type
--------------- ---------------
A procedural type is a (somewhat abstract) pointer to a procedure. A procedural type 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 Nim uses procedural types to achieve `functional`:idx: programming
techniques. techniques.
Example: Example:
.. code-block:: nimrod .. code-block:: nim
proc echoItem(x: int) = echo(x) proc echoItem(x: int) = echo(x)
proc forEach(action: proc (x: int)) = proc forEach(action: proc (x: int)) =
@ -1513,13 +1513,13 @@ listed in the `manual <manual.html>`_.
Modules Modules
======= =======
Nimrod supports splitting a program into pieces with a module concept. Nim supports splitting a program into pieces with a module concept.
Each module is in its own file. Modules enable `information hiding`:idx: and Each module is in its own file. Modules enable `information hiding`:idx: and
`separate compilation`:idx:. A module may gain access to symbols of another `separate compilation`:idx:. A module may gain access to symbols of another
module by the `import`:idx: statement. Only top-level symbols that are marked module by the `import`:idx: statement. Only top-level symbols that are marked
with an asterisk (``*``) are exported: with an asterisk (``*``) are exported:
.. code-block:: nimrod .. code-block:: nim
# Module A # Module A
var var
x*, y: int x*, y: int
@ -1554,7 +1554,7 @@ The algorithm for compiling modules is:
This is best illustrated by an example: This is best illustrated by an example:
.. code-block:: nimrod .. code-block:: nim
# Module A # Module A
type type
T1* = int # Module A exports the type ``T1`` T1* = int # Module A exports the type ``T1``
@ -1565,7 +1565,7 @@ This is best illustrated by an example:
main() main()
.. code-block:: nimrod .. code-block:: nim
# Module B # Module B
import A # A is not parsed here! Only the already known symbols import A # A is not parsed here! Only the already known symbols
# of A are imported. # of A are imported.
@ -1581,15 +1581,15 @@ the symbol is ambiguous, it even *has* to be qualified. A symbol is ambiguous
if it is defined in two (or more) different modules and both modules are if it is defined in two (or more) different modules and both modules are
imported by a third one: imported by a third one:
.. code-block:: nimrod .. code-block:: nim
# Module A # Module A
var x*: string var x*: string
.. code-block:: nimrod .. code-block:: nim
# Module B # Module B
var x*: int var x*: int
.. code-block:: nimrod .. code-block:: nim
# Module C # Module C
import A, B import A, B
write(stdout, x) # error: x is ambiguous write(stdout, x) # error: x is ambiguous
@ -1602,15 +1602,15 @@ imported by a third one:
But this rule does not apply to procedures or iterators. Here the overloading But this rule does not apply to procedures or iterators. Here the overloading
rules apply: rules apply:
.. code-block:: nimrod .. code-block:: nim
# Module A # Module A
proc x*(a: int): string = result = $a proc x*(a: int): string = result = $a
.. code-block:: nimrod .. code-block:: nim
# Module B # Module B
proc x*(a: string): string = result = $a proc x*(a: string): string = result = $a
.. code-block:: nimrod .. code-block:: nim
# Module C # Module C
import A, B import A, B
write(stdout, x(3)) # no error: A.x is called write(stdout, x(3)) # no error: A.x is called
@ -1627,7 +1627,7 @@ The normal ``import`` statement will bring in all exported symbols.
These can be limited by naming symbols which should be excluded with These can be limited by naming symbols which should be excluded with
the ``except`` qualifier. the ``except`` qualifier.
.. code-block:: nimrod .. code-block:: nim
import mymodule except y import mymodule except y
@ -1638,19 +1638,19 @@ We have already seen the simple ``import`` statement that just imports all
exported symbols. An alternative that only imports listed symbols is the exported symbols. An alternative that only imports listed symbols is the
``from import`` statement: ``from import`` statement:
.. code-block:: nimrod .. code-block:: nim
from mymodule import x, y, z from mymodule import x, y, z
The ``from`` statement can also force namespace qualification on The ``from`` statement can also force namespace qualification on
symbols, thereby making symbols available, but needing to be qualified symbols, thereby making symbols available, but needing to be qualified
to be used. to be used.
.. code-block:: nimrod .. code-block:: nim
from mymodule import x, y, z from mymodule import x, y, z
x() # use x without any qualification x() # use x without any qualification
.. code-block:: nimrod .. code-block:: nim
from mymodule import nil from mymodule import nil
mymodule.x() # must qualify x with the module name as prefix mymodule.x() # must qualify x with the module name as prefix
@ -1660,7 +1660,7 @@ to be used.
Since module names are generally long to be descriptive, you can also Since module names are generally long to be descriptive, you can also
define a shorter alias to use when qualifying symbols. define a shorter alias to use when qualifying symbols.
.. code-block:: nimrod .. code-block:: nim
from mymodule as m import nil from mymodule as m import nil
m.x() # m is aliasing mymodule m.x() # m is aliasing mymodule
@ -1672,7 +1672,7 @@ The ``include`` statement does something fundamentally different than
importing a module: it merely includes the contents of a file. The ``include`` importing a module: it merely includes the contents of a file. The ``include``
statement is useful to split up a large module into several files: statement is useful to split up a large module into several files:
.. code-block:: nimrod .. code-block:: nim
include fileA, fileB, fileC include fileA, fileB, fileC
**Note**: The documentation generator currently does not follow ``include`` **Note**: The documentation generator currently does not follow ``include``
@ -1683,7 +1683,7 @@ generated documentation.
Part 2 Part 2
====== ======
So, now that we are done with the basics, let's see what Nimrod offers apart So, now that we are done with the basics, let's see what Nim offers apart
from a nice syntax for procedural programming: `Part II <tut2.html>`_ from a nice syntax for procedural programming: `Part II <tut2.html>`_

View file

@ -1,9 +1,9 @@
========================= ======================
Nimrod Tutorial (Part II) Nim Tutorial (Part II)
========================= ======================
:Author: Andreas Rumpf :Author: Andreas Rumpf
:Version: |nimrodversion| :Version: |nimversion|
.. contents:: .. contents::
@ -15,7 +15,7 @@ Introduction
only have originated in California." --Edsger Dijkstra only have originated in California." --Edsger Dijkstra
This document is a tutorial for the advanced constructs of the *Nimrod* This document is a tutorial for the advanced constructs of the *Nim*
programming language. **Note that this document is somewhat obsolete as the** programming language. **Note that this document is somewhat obsolete as the**
`manual <manual.html>`_ **contains many more examples of the advanced language `manual <manual.html>`_ **contains many more examples of the advanced language
features.** features.**
@ -24,18 +24,18 @@ features.**
Pragmas Pragmas
======= =======
Pragmas are Nimrod's method to give the compiler additional information/ Pragmas are Nim's method to give the compiler additional information/
commands without introducing a massive number of new keywords. Pragmas are commands without introducing a massive number of new keywords. Pragmas are
enclosed in the special ``{.`` and ``.}`` curly dot brackets. This tutorial enclosed in the special ``{.`` and ``.}`` curly dot brackets. This tutorial
does not cover pragmas. See the `manual <manual.html#pragmas>`_ or `user guide does not cover pragmas. See the `manual <manual.html#pragmas>`_ or `user guide
<nimrodc.html#additional-features>`_ for a description of the available <nimc.html#additional-features>`_ for a description of the available
pragmas. pragmas.
Object Oriented Programming Object Oriented Programming
=========================== ===========================
While Nimrod's support for object oriented programming (OOP) is minimalistic, While Nim's support for object oriented programming (OOP) is minimalistic,
powerful OOP technics can be used. OOP is seen as *one* way to design a powerful OOP technics can be used. OOP is seen as *one* way to design a
program, not *the only* way. Often a procedural approach leads to simpler program, not *the only* way. Often a procedural approach leads to simpler
and more efficient code. In particular, prefering composition over inheritance and more efficient code. In particular, prefering composition over inheritance
@ -55,7 +55,7 @@ a *constructor*).
Objects have access to their type at runtime. There is an Objects have access to their type at runtime. There is an
``of`` operator that can be used to check the object's type: ``of`` operator that can be used to check the object's type:
.. code-block:: nimrod .. code-block:: nim
type type
TPerson = object of TObject TPerson = object of TObject
name*: string # the * means that `name` is accessible from other modules name*: string # the * means that `name` is accessible from other modules
@ -86,20 +86,20 @@ in the GTK wrapper for instance.)
**Note**: Composition (*has-a* relation) is often preferable to inheritance **Note**: Composition (*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, composition is as efficient as inheritance. Nim, composition is as efficient as inheritance.
Mutually recursive types 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; they are *mutually recursive*. In Nimrod depend on each other; they are *mutually recursive*. In Nim
these types can only 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:
.. code-block:: nimrod .. code-block:: nim
type type
PNode = ref TNode # a traced reference to a TNode PNode = ref TNode # a traced reference to a TNode
TNode = object TNode = object
@ -114,7 +114,7 @@ Example:
Type conversions Type conversions
---------------- ----------------
Nimrod distinguishes between `type casts`:idx: and `type conversions`:idx:. Nim distinguishes between `type casts`:idx: and `type conversions`:idx:.
Casts are done with the ``cast`` operator and force the compiler to Casts are done with the ``cast`` operator and force the compiler to
interpret a bit pattern to be of another type. interpret a bit pattern to be of another type.
@ -126,7 +126,7 @@ raised.
The syntax for type conversions is ``destination_type(expression_to_convert)`` The syntax for type conversions is ``destination_type(expression_to_convert)``
(like an ordinary call): (like an ordinary call):
.. code-block:: nimrod .. code-block:: nim
proc getID(x: TPerson): int = proc getID(x: TPerson): int =
TStudent(x).id TStudent(x).id
@ -141,9 +141,9 @@ variant types are needed.
An example: An example:
.. code-block:: nimrod .. code-block:: nim
# This is an example how an abstract syntax tree could be modeled in Nimrod # This is an example how an abstract syntax tree could be modeled in Nim
type type
TNodeKind = enum # the different node types TNodeKind = enum # the different node types
nkInt, # a leaf with an integer value nkInt, # a leaf with an integer value
@ -183,8 +183,8 @@ bound to a class. This has disadvantages:
* Often it is unclear where the method should belong to: is * Often it is unclear where the method should belong to: is
``join`` a string method or an array method? ``join`` a string method or an array method?
Nimrod avoids these problems by not assigning methods to a class. All methods Nim avoids these problems by not assigning methods to a class. All methods
in Nimrod are multi-methods. As we will see later, multi-methods are in Nim are multi-methods. As we will see later, multi-methods are
distinguished from procs only for dynamic binding purposes. distinguished from procs only for dynamic binding purposes.
@ -199,7 +199,7 @@ If there are no remaining arguments, the parentheses can be omitted:
This method call syntax is not restricted to objects, it can be used This method call syntax is not restricted to objects, it can be used
for any type: for any type:
.. code-block:: nimrod .. code-block:: nim
echo("abc".len) # is the same as echo(len("abc")) echo("abc".len) # is the same as echo(len("abc"))
echo("abc".toUpper()) echo("abc".toUpper())
@ -211,7 +211,7 @@ postfix notation.)
So "pure object oriented" code is easy to write: So "pure object oriented" code is easy to write:
.. code-block:: nimrod .. code-block:: nim
import strutils import strutils
stdout.writeln("Give a list of numbers (separated by spaces): ") stdout.writeln("Give a list of numbers (separated by spaces): ")
@ -221,12 +221,12 @@ So "pure object oriented" code is easy to write:
Properties Properties
---------- ----------
As the above example shows, Nimrod has no need for *get-properties*: As the above example shows, Nim has no need for *get-properties*:
Ordinary get-procedures that are called with the *method call syntax* achieve Ordinary get-procedures that are called with the *method call syntax* achieve
the same. But setting a value is different; for this a special setter syntax the same. But setting a value is different; for this a special setter syntax
is needed: is needed:
.. code-block:: nimrod .. code-block:: nim
type type
TSocket* = object of TObject TSocket* = object of TObject
@ -252,7 +252,7 @@ is needed:
The ``[]`` array access operator can be overloaded to provide The ``[]`` array access operator can be overloaded to provide
`array properties`:idx:\ : `array properties`:idx:\ :
.. code-block:: nimrod .. code-block:: nim
type type
TVector* = object TVector* = object
x, y, z: float x, y, z: float
@ -283,7 +283,7 @@ Dynamic dispatch
Procedures always use static dispatch. For dynamic dispatch replace the Procedures always use static dispatch. For dynamic dispatch replace the
``proc`` keyword by ``method``: ``proc`` keyword by ``method``:
.. code-block:: nimrod .. code-block:: nim
type type
PExpr = ref object of TObject ## abstract base class for an expression PExpr = ref object of TObject ## abstract base class for an expression
PLiteral = ref object of PExpr PLiteral = ref object of PExpr
@ -311,7 +311,7 @@ requires dynamic binding.
In a multi-method all parameters that have an object type are used for the In a multi-method all parameters that have an object type are used for the
dispatching: dispatching:
.. code-block:: nimrod .. code-block:: nim
type type
TThing = object of TObject TThing = object of TObject
@ -336,7 +336,7 @@ As the example demonstrates, invocation of a multi-method cannot be ambiguous:
Collide 2 is preferred over collide 1 because the resolution works from left to Collide 2 is preferred over collide 1 because the resolution works from left to
right. Thus ``TUnit, TThing`` is preferred over ``TThing, TUnit``. right. Thus ``TUnit, TThing`` is preferred over ``TThing, TUnit``.
**Perfomance note**: Nimrod does not produce a virtual method table, but **Perfomance note**: Nim does not produce a virtual method table, but
generates dispatch trees. This avoids the expensive indirect branch for method generates dispatch trees. This avoids the expensive indirect branch for method
calls and enables inlining. However, other optimizations like compile time calls and enables inlining. However, other optimizations like compile time
evaluation or dead code elimination do not work with methods. evaluation or dead code elimination do not work with methods.
@ -345,7 +345,7 @@ evaluation or dead code elimination do not work with methods.
Exceptions Exceptions
========== ==========
In Nimrod exceptions are objects. By convention, exception types are In Nim exceptions are objects. By convention, exception types are
prefixed with an 'E', not 'T'. The `system <system.html>`_ module defines an prefixed with an 'E', not 'T'. The `system <system.html>`_ module defines an
exception hierarchy that you might want to stick to. Exceptions derive from exception hierarchy that you might want to stick to. Exceptions derive from
E_Base, which provides the common interface. E_Base, which provides the common interface.
@ -364,7 +364,7 @@ Raise statement
--------------- ---------------
Raising an exception is done with the ``raise`` statement: Raising an exception is done with the ``raise`` statement:
.. code-block:: nimrod .. code-block:: nim
var var
e: ref EOS e: ref EOS
new(e) new(e)
@ -375,7 +375,7 @@ If the ``raise`` keyword is not followed by an expression, the last exception
is *re-raised*. For the purpose of avoiding repeating this common code pattern, is *re-raised*. For the purpose of avoiding repeating this common code pattern,
the template ``newException`` in the ``system`` module can be used: the template ``newException`` in the ``system`` module can be used:
.. code-block:: nimrod .. code-block:: nim
raise newException(EOS, "the request to the OS failed") raise newException(EOS, "the request to the OS failed")
@ -384,7 +384,7 @@ Try statement
The ``try`` statement handles exceptions: The ``try`` statement handles exceptions:
.. code-block:: nimrod .. code-block:: nim
# read the first two lines of a text file that should contain numbers # read the first two lines of a text file that should contain numbers
# and tries to add them # and tries to add them
var var
@ -428,7 +428,7 @@ If you need to *access* the actual exception object or message inside an
<system.html#getCurrentExceptionMsg>`_ procs from the `system <system.html>`_ <system.html#getCurrentExceptionMsg>`_ procs from the `system <system.html>`_
module. Example: module. Example:
.. code-block:: nimrod .. code-block:: nim
try: try:
doSomethingHere() doSomethingHere()
except: except:
@ -460,7 +460,7 @@ instance, if you specify that a proc raises ``EIO``, and at some point it (or
one of the procs it calls) starts raising a new exception the compiler will one of the procs it calls) starts raising a new exception the compiler will
prevent that proc from compiling. Usage example: prevent that proc from compiling. Usage example:
.. code-block:: nimrod .. code-block:: nim
proc complexProc() {.raises: [EIO, EArithmetic].} = proc complexProc() {.raises: [EIO, EArithmetic].} =
... ...
@ -476,21 +476,21 @@ help you locate the offending code which has changed.
If you want to add the ``{.raises.}`` pragma to existing code, the compiler can If you want to add the ``{.raises.}`` pragma to existing code, the compiler can
also help you. You can add the ``{.effects.}`` pragma statement to your proc and also help you. You can add the ``{.effects.}`` pragma statement to your proc and
the compiler will output all inferred effects up to that point (exception the compiler will output all inferred effects up to that point (exception
tracking is part of Nimrod's effect system). Another more roundabout way to tracking is part of Nim's effect system). Another more roundabout way to
find out the list of exceptions raised by a proc is to use the Nimrod ``doc2`` find out the list of exceptions raised by a proc is to use the Nim ``doc2``
command which generates documentation for a whole module and decorates all command which generates documentation for a whole module and decorates all
procs with the list of raised exceptions. You can read more about Nimrod's procs with the list of raised exceptions. You can read more about Nim's
`effect system and related pragmas in the manual <manual.html#effect-system>`_. `effect system and related pragmas in the manual <manual.html#effect-system>`_.
Generics Generics
======== ========
Generics are Nimrod's means to parametrize procs, iterators or types Generics are Nim's means to parametrize procs, iterators or types
with `type parameters`:idx:. They are most useful for efficient type safe with `type parameters`:idx:. They are most useful for efficient type safe
containers: containers:
.. code-block:: nimrod .. code-block:: nim
type type
TBinaryTree[T] = object # TBinaryTree is a generic type with TBinaryTree[T] = object # TBinaryTree is a generic type with
# with generic param ``T`` # with generic param ``T``
@ -557,7 +557,7 @@ is not hidden and is used in the ``preorder`` iterator.
Templates Templates
========= =========
Templates are a simple substitution mechanism that operates on Nimrod's Templates are a simple substitution mechanism that operates on Nim's
abstract syntax trees. Templates are processed in the semantic pass of the abstract syntax trees. Templates are processed in the semantic pass of the
compiler. They integrate well with the rest of the language and share none compiler. They integrate well with the rest of the language and share none
of C's preprocessor macros flaws. of C's preprocessor macros flaws.
@ -566,7 +566,7 @@ To *invoke* a template, call it like a procedure.
Example: Example:
.. code-block:: nimrod .. code-block:: nim
template `!=` (a, b: expr): expr = template `!=` (a, b: expr): expr =
# this definition exists in the System module # this definition exists in the System module
not (a == b) not (a == b)
@ -585,7 +585,7 @@ for IEEE floating point numbers - NaN breaks basic boolean logic.)
Templates are especially useful for lazy evaluation purposes. Consider a Templates are especially useful for lazy evaluation purposes. Consider a
simple proc for logging: simple proc for logging:
.. code-block:: nimrod .. code-block:: nim
const const
debug = true debug = true
@ -602,7 +602,7 @@ 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:
.. code-block:: nimrod .. code-block:: nim
const const
debug = true debug = true
@ -618,32 +618,11 @@ The parameters' types can be ordinary types or the meta types ``expr``
(stands for *type description*). If the template has no explicit return type, (stands for *type description*). If the template has no explicit return type,
``stmt`` is used for consistency with procs and methods. ``stmt`` is used for consistency with procs and methods.
The template body does not open a new scope. To open a new scope use a ``block``
statement:
.. code-block:: nimrod
template declareInScope(x: expr, t: typeDesc): stmt {.immediate.} =
var x: t
template declareInNewScope(x: expr, t: typeDesc): stmt {.immediate.} =
# open a new scope:
block:
var x: t
declareInScope(a, int)
a = 42 # works, `a` is known here
declareInNewScope(b, int)
b = 42 # does not work, `b` is unknown
(The `manual explains <manual.html#ordinary-vs-immediate-templates>`_ why the
``immediate`` pragma is needed for these templates.)
If there is a ``stmt`` parameter it should be the last in the template If there is a ``stmt`` parameter it should be the last in the template
declaration. The reason is that statements can be passed to a template declaration. The reason is that statements can be passed to a template
via a special ``:`` syntax: via a special ``:`` syntax:
.. code-block:: nimrod .. code-block:: nim
template withFile(f: expr, filename: string, mode: TFileMode, template withFile(f: expr, filename: string, mode: TFileMode,
body: stmt): stmt {.immediate.} = body: stmt): stmt {.immediate.} =
@ -672,18 +651,18 @@ Macros
====== ======
Macros enable advanced compile-time code transformations, but they cannot Macros enable advanced compile-time code transformations, but they cannot
change Nimrod's syntax. However, this is no real restriction because Nimrod's change Nim's syntax. However, this is no real restriction because Nim's
syntax is flexible enough anyway. Macros have to be implemented in pure Nimrod syntax is flexible enough anyway. Macros have to be implemented in pure Nim
code if `foreign function interface (FFI) code if `foreign function interface (FFI)
<manual.html#foreign-function-interface>`_ is not enabled in the compiler, but <manual.html#foreign-function-interface>`_ is not enabled in the compiler, but
other than that restriction (which at some point in the future will go away) other than that restriction (which at some point in the future will go away)
you can write any kind of Nimrod code and the compiler will run it at compile you can write any kind of Nim code and the compiler will run it at compile
time. time.
There are two ways to write a macro, either *generating* Nimrod source code and There are two ways to write a macro, either *generating* Nim source code and
letting the compiler parse it, or creating manually an abstract syntax tree letting the compiler parse it, or creating manually an abstract syntax tree
(AST) which you feed to the compiler. In order to build the AST one needs to (AST) which you feed to the compiler. In order to build the AST one needs to
know how the Nimrod concrete syntax is converted to an abstract syntax tree know how the Nim concrete syntax is converted to an abstract syntax tree
(AST). The AST is documented in the `macros <macros.html>`_ module. (AST). The AST is documented in the `macros <macros.html>`_ module.
Once your macro is finished, there are two ways to invoke it: Once your macro is finished, there are two ways to invoke it:
@ -698,13 +677,13 @@ Expression Macros
The following example implements a powerful ``debug`` command that accepts a The following example implements a powerful ``debug`` command that accepts a
variable number of arguments: variable number of arguments:
.. code-block:: nimrod .. code-block:: nim
# to work with Nimrod syntax trees, we need an API that is defined in the # to work with Nim syntax trees, we need an API that is defined in the
# ``macros`` module: # ``macros`` module:
import macros import macros
macro debug(n: varargs[expr]): stmt = macro debug(n: varargs[expr]): stmt =
# `n` is a Nimrod AST that contains a list of expressions; # `n` is a Nim AST that contains a list of expressions;
# this macro returns a list of statements: # this macro returns a list of statements:
result = newNimNode(nnkStmtList, n) result = newNimNode(nnkStmtList, n)
# iterate over any argument that is passed to this macro: # iterate over any argument that is passed to this macro:
@ -727,7 +706,7 @@ variable number of arguments:
The macro call expands to: The macro call expands to:
.. code-block:: nimrod .. code-block:: nim
write(stdout, "a[0]") write(stdout, "a[0]")
write(stdout, ": ") write(stdout, ": ")
writeln(stdout, a[0]) writeln(stdout, a[0])
@ -751,7 +730,7 @@ invoked by an expression following a colon.
The following example outlines a macro that generates a lexical analyzer from The following example outlines a macro that generates a lexical analyzer from
regular expressions: regular expressions:
.. code-block:: nimrod .. code-block:: nim
macro case_token(n: stmt): stmt = macro case_token(n: stmt): stmt =
# creates a lexical analyzer from regular expressions # creates a lexical analyzer from regular expressions
@ -784,7 +763,7 @@ To give a footstart to writing macros we will show now how to turn your typical
dynamic code into something that compiles statically. For the exercise we will dynamic code into something that compiles statically. For the exercise we will
use the following snippet of code as the starting point: use the following snippet of code as the starting point:
.. code-block:: nimrod .. code-block:: nim
import strutils, tables import strutils, tables
@ -848,7 +827,7 @@ time string with the *generated source code*, which we then pass to the
``parseStmt`` proc from the `macros module <macros.html>`_. Here is the ``parseStmt`` proc from the `macros module <macros.html>`_. Here is the
modified source code implementing the macro: modified source code implementing the macro:
.. code-block:: nimrod .. code-block:: nim
import macros, strutils import macros, strutils
macro readCfgAndBuildSource(cfgFilename: string): stmt = macro readCfgAndBuildSource(cfgFilename: string): stmt =
@ -893,13 +872,13 @@ this limitation by using the ``slurp`` proc from the `system module
``gorge`` which executes an external program and captures its output). ``gorge`` which executes an external program and captures its output).
The interesting thing is that our macro does not return a runtime ``TTable`` The interesting thing is that our macro does not return a runtime ``TTable``
object. Instead, it builds up Nimrod source code into the ``source`` variable. object. Instead, it builds up Nim source code into the ``source`` variable.
For each line of the configuration file a ``const`` variable will be generated. For each line of the configuration file a ``const`` variable will be generated.
To avoid conflicts we prefix these variables with ``cfg``. In essence, what the To avoid conflicts we prefix these variables with ``cfg``. In essence, what the
compiler is doing is replacing the line calling the macro with the following compiler is doing is replacing the line calling the macro with the following
snippet of code: snippet of code:
.. code-block:: nimrod .. code-block:: nim
const cfgversion= "1.1" const cfgversion= "1.1"
const cfglicenseOwner= "Hyori Lee" const cfglicenseOwner= "Hyori Lee"
const cfglicenseKey= "M1Tl3PjBWO2CC48m" const cfglicenseKey= "M1Tl3PjBWO2CC48m"
@ -919,14 +898,14 @@ Generating AST by hand
++++++++++++++++++++++ ++++++++++++++++++++++
To generate an AST we would need to intimately know the structures used by the To generate an AST we would need to intimately know the structures used by the
Nimrod compiler exposed in the `macros module <macros.html>`_, which at first Nim compiler exposed in the `macros module <macros.html>`_, which at first
look seems a daunting task. But we can use as helper shortcut the ``dumpTree`` look seems a daunting task. But we can use as helper shortcut the ``dumpTree``
macro, which is used as a statement macro instead of an expression macro. macro, which is used as a statement macro instead of an expression macro.
Since we know that we want to generate a bunch of ``const`` symbols we can Since we know that we want to generate a bunch of ``const`` symbols we can
create the following source file and compile it to see what the compiler create the following source file and compile it to see what the compiler
*expects* from us: *expects* from us:
.. code-block:: nimrod .. code-block:: nim
import macros import macros
dumpTree: dumpTree:
@ -969,7 +948,7 @@ identifier, optionally a type (can be an *empty* node) and the value. Armed
with this knowledge, let's look at the finished version of the AST building with this knowledge, let's look at the finished version of the AST building
macro: macro:
.. code-block:: nimrod .. code-block:: nim
import macros, strutils import macros, strutils
macro readCfgAndBuildAST(cfgFilename: string): stmt = macro readCfgAndBuildAST(cfgFilename: string): stmt =

View file

@ -15,7 +15,7 @@ var links = 0 # count the number of links
var filename = addFileExt(ParamStr(1), "html") var filename = addFileExt(ParamStr(1), "html")
var s = newFileStream(filename, fmRead) var s = newFileStream(filename, fmRead)
if s == nil: quit("cannot open the file " & filename) if s == nil: quit("cannot open the file " & filename)
var x: TXmlParser var x: XmlParser
open(x, s, filename) open(x, s, filename)
next(x) # get first event next(x) # get first event
block mainLoop: block mainLoop:

View file

@ -10,7 +10,7 @@ if paramCount() < 1:
var filename = addFileExt(paramStr(1), "html") var filename = addFileExt(paramStr(1), "html")
var s = newFileStream(filename, fmRead) var s = newFileStream(filename, fmRead)
if s == nil: quit("cannot open the file " & filename) if s == nil: quit("cannot open the file " & filename)
var x: TXmlParser var x: XmlParser
open(x, s, filename) open(x, s, filename)
while true: while true:
x.next() x.next()

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -21,6 +21,11 @@
## for i in 0 .. < 300: ## for i in 0 .. < 300:
## a.spawn(i, proc (x: int) {.thread.} = echo x) ## a.spawn(i, proc (x: int) {.thread.} = echo x)
## a.join() ## a.join()
##
## **Note**: This whole module is deprecated. Use `threadpool` and ``spawn``
## instead.
{.deprecated.}
from os import sleep from os import sleep

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -10,9 +10,12 @@
## This module implements some common generic algorithms. ## This module implements some common generic algorithms.
type type
TSortOrder* = enum ## sort order SortOrder* = enum ## sort order
Descending, Ascending Descending, Ascending
{.deprecated: [TSortOrder: SortOrder].}
proc `*`*(x: int, order: TSortOrder): int {.inline.} = proc `*`*(x: int, order: TSortOrder): int {.inline.} =
## flips `x` if ``order == Descending``; ## flips `x` if ``order == Descending``;
## if ``order == Ascending`` then `x` is returned. ## if ``order == Ascending`` then `x` is returned.

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2014 Dominik Picheta # (c) Copyright 2014 Dominik Picheta
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -13,7 +13,7 @@ import os, oids, tables, strutils, macros, times
import rawsockets, net import rawsockets, net
export TPort, TSocketFlags export Port, SocketFlags
#{.injectStmt: newGcInvariant().} #{.injectStmt: newGcInvariant().}
@ -36,21 +36,24 @@ export TPort, TSocketFlags
# -- Futures # -- Futures
type type
PFutureBase* = ref object of PObject FutureBase* = ref object of RootObj
cb: proc () {.closure,gcsafe.} cb: proc () {.closure,gcsafe.}
finished: bool finished: bool
error*: ref EBase error*: ref Exception
errorStackTrace*: string errorStackTrace*: string
when not defined(release): when not defined(release):
stackTrace: string ## For debugging purposes only. stackTrace: string ## For debugging purposes only.
id: int id: int
fromProc: string fromProc: string
PFuture*[T] = ref object of PFutureBase Future*[T] = ref object of FutureBase
value: T value: T
{.deprecated: [PFutureBase: FutureBase, PFuture: Future].}
var currentID* = 0 var currentID* = 0
proc newFuture*[T](fromProc: string = "unspecified"): PFuture[T] = proc newFuture*[T](fromProc: string = "unspecified"): Future[T] =
## Creates a new future. ## Creates a new future.
## ##
## Specifying ``fromProc``, which is a string specifying the name of the proc ## Specifying ``fromProc``, which is a string specifying the name of the proc
@ -63,7 +66,7 @@ proc newFuture*[T](fromProc: string = "unspecified"): PFuture[T] =
result.fromProc = fromProc result.fromProc = fromProc
currentID.inc() currentID.inc()
proc checkFinished[T](future: PFuture[T]) = proc checkFinished[T](future: Future[T]) =
when not defined(release): when not defined(release):
if future.finished: if future.finished:
echo("<-----> ", future.id, " ", future.fromProc) echo("<-----> ", future.id, " ", future.fromProc)
@ -76,7 +79,7 @@ proc checkFinished[T](future: PFuture[T]) =
echo getStackTrace() echo getStackTrace()
assert false assert false
proc complete*[T](future: PFuture[T], val: T) = proc complete*[T](future: Future[T], val: T) =
## Completes ``future`` with value ``val``. ## Completes ``future`` with value ``val``.
#assert(not future.finished, "Future already finished, cannot finish twice.") #assert(not future.finished, "Future already finished, cannot finish twice.")
checkFinished(future) checkFinished(future)
@ -86,7 +89,7 @@ proc complete*[T](future: PFuture[T], val: T) =
if future.cb != nil: if future.cb != nil:
future.cb() future.cb()
proc complete*(future: PFuture[void]) = proc complete*(future: Future[void]) =
## Completes a void ``future``. ## Completes a void ``future``.
#assert(not future.finished, "Future already finished, cannot finish twice.") #assert(not future.finished, "Future already finished, cannot finish twice.")
checkFinished(future) checkFinished(future)
@ -95,7 +98,7 @@ proc complete*(future: PFuture[void]) =
if future.cb != nil: if future.cb != nil:
future.cb() future.cb()
proc fail*[T](future: PFuture[T], error: ref EBase) = proc fail*[T](future: Future[T], error: ref Exception) =
## Completes ``future`` with ``error``. ## Completes ``future`` with ``error``.
#assert(not future.finished, "Future already finished, cannot finish twice.") #assert(not future.finished, "Future already finished, cannot finish twice.")
checkFinished(future) checkFinished(future)
@ -111,8 +114,9 @@ proc fail*[T](future: PFuture[T], error: ref EBase) =
# TODO: This may turn out to be a bad idea. # TODO: This may turn out to be a bad idea.
# Turns out this is a bad idea. # Turns out this is a bad idea.
#raise error #raise error
discard
proc `callback=`*(future: PFutureBase, cb: proc () {.closure,gcsafe.}) = proc `callback=`*(future: FutureBase, cb: proc () {.closure,gcsafe.}) =
## Sets the callback proc to be called when the future completes. ## Sets the callback proc to be called when the future completes.
## ##
## If future has already completed then ``cb`` will be called immediately. ## If future has already completed then ``cb`` will be called immediately.
@ -123,23 +127,23 @@ proc `callback=`*(future: PFutureBase, cb: proc () {.closure,gcsafe.}) =
if future.finished: if future.finished:
future.cb() future.cb()
proc `callback=`*[T](future: PFuture[T], proc `callback=`*[T](future: Future[T],
cb: proc (future: PFuture[T]) {.closure,gcsafe.}) = cb: proc (future: Future[T]) {.closure,gcsafe.}) =
## Sets the callback proc to be called when the future completes. ## Sets the callback proc to be called when the future completes.
## ##
## If future has already completed then ``cb`` will be called immediately. ## If future has already completed then ``cb`` will be called immediately.
future.callback = proc () = cb(future) future.callback = proc () = cb(future)
proc echoOriginalStackTrace[T](future: PFuture[T]) = proc echoOriginalStackTrace[T](future: Future[T]) =
# TODO: Come up with something better. # TODO: Come up with something better.
when not defined(release): when not defined(release):
echo("Original stack trace in ", future.fromProc, ":") echo("Original stack trace in ", future.fromProc, ":")
if not future.errorStackTrace.isNil() and future.errorStackTrace != "": if not future.errorStackTrace.isNil and future.errorStackTrace != "":
echo(future.errorStackTrace) echo(future.errorStackTrace)
else: else:
echo("Empty or nil stack trace.") echo("Empty or nil stack trace.")
proc read*[T](future: PFuture[T]): T = proc read*[T](future: Future[T]): T =
## Retrieves the value of ``future``. Future must be finished otherwise ## Retrieves the value of ``future``. Future must be finished otherwise
## this function will fail with a ``EInvalidValue`` exception. ## this function will fail with a ``EInvalidValue`` exception.
## ##
@ -154,22 +158,22 @@ proc read*[T](future: PFuture[T]): T =
# TODO: Make a custom exception type for this? # TODO: Make a custom exception type for this?
raise newException(EInvalidValue, "Future still in progress.") raise newException(EInvalidValue, "Future still in progress.")
proc readError*[T](future: PFuture[T]): ref EBase = proc readError*[T](future: Future[T]): ref EBase =
if future.error != nil: return future.error if future.error != nil: return future.error
else: else:
raise newException(EInvalidValue, "No error in future.") raise newException(EInvalidValue, "No error in future.")
proc finished*[T](future: PFuture[T]): bool = proc finished*[T](future: Future[T]): bool =
## Determines whether ``future`` has completed. ## Determines whether ``future`` has completed.
## ##
## ``True`` may indicate an error or a value. Use ``failed`` to distinguish. ## ``True`` may indicate an error or a value. Use ``failed`` to distinguish.
future.finished future.finished
proc failed*(future: PFutureBase): bool = proc failed*(future: FutureBase): bool =
## Determines whether ``future`` completed with an error. ## Determines whether ``future`` completed with an error.
future.error != nil future.error != nil
proc asyncCheck*[T](future: PFuture[T]) = proc asyncCheck*[T](future: Future[T]) =
## Sets a callback on ``future`` which raises an exception if the future ## Sets a callback on ``future`` which raises an exception if the future
## finished with an error. ## finished with an error.
## ##
@ -180,7 +184,7 @@ proc asyncCheck*[T](future: PFuture[T]) =
echoOriginalStackTrace(future) echoOriginalStackTrace(future)
raise future.error raise future.error
proc `and`*[T, Y](fut1: PFuture[T], fut2: PFuture[Y]): PFuture[void] = proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
## Returns a future which will complete once both ``fut1`` and ``fut2`` ## Returns a future which will complete once both ``fut1`` and ``fut2``
## complete. ## complete.
var retFuture = newFuture[void]() var retFuture = newFuture[void]()
@ -192,7 +196,7 @@ proc `and`*[T, Y](fut1: PFuture[T], fut2: PFuture[Y]): PFuture[void] =
if fut1.finished: retFuture.complete() if fut1.finished: retFuture.complete()
return retFuture return retFuture
proc `or`*[T, Y](fut1: PFuture[T], fut2: PFuture[Y]): PFuture[void] = proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
## Returns a future which will complete once either ``fut1`` or ``fut2`` ## Returns a future which will complete once either ``fut1`` or ``fut2``
## complete. ## complete.
var retFuture = newFuture[void]() var retFuture = newFuture[void]()
@ -203,7 +207,7 @@ proc `or`*[T, Y](fut1: PFuture[T], fut2: PFuture[Y]): PFuture[void] =
type type
PDispatcherBase = ref object of PObject PDispatcherBase = ref object of PObject
timers: seq[tuple[finishAt: float, fut: PFuture[void]]] timers: seq[tuple[finishAt: float, fut: Future[void]]]
proc processTimers(p: PDispatcherBase) = proc processTimers(p: PDispatcherBase) =
var oldTimers = p.timers var oldTimers = p.timers
@ -375,10 +379,10 @@ when defined(windows) or defined(nimdoc):
RemoteSockaddr, RemoteSockaddrLength) RemoteSockaddr, RemoteSockaddrLength)
proc connect*(socket: TAsyncFD, address: string, port: TPort, proc connect*(socket: TAsyncFD, address: string, port: TPort,
af = AF_INET): PFuture[void] = af = AF_INET): Future[void] =
## Connects ``socket`` to server at ``address:port``. ## Connects ``socket`` to server at ``address:port``.
## ##
## Returns a ``PFuture`` which will complete when the connection succeeds ## Returns a ``Future`` which will complete when the connection succeeds
## or an error occurs. ## or an error occurs.
verifyPresence(socket) verifyPresence(socket)
var retFuture = newFuture[void]("connect") var retFuture = newFuture[void]("connect")
@ -437,7 +441,7 @@ when defined(windows) or defined(nimdoc):
return retFuture return retFuture
proc recv*(socket: TAsyncFD, size: int, proc recv*(socket: TAsyncFD, size: int,
flags = {TSocketFlags.SafeDisconn}): PFuture[string] = flags = {TSocketFlags.SafeDisconn}): Future[string] =
## Reads **up to** ``size`` bytes from ``socket``. Returned future will ## Reads **up to** ``size`` bytes from ``socket``. Returned future will
## complete once all the data requested is read, a part of the data has been ## complete once all the data requested is read, a part of the data has been
## read, or the socket has disconnected in which case the future will ## read, or the socket has disconnected in which case the future will
@ -525,7 +529,7 @@ when defined(windows) or defined(nimdoc):
return retFuture return retFuture
proc send*(socket: TAsyncFD, data: string, proc send*(socket: TAsyncFD, data: string,
flags = {TSocketFlags.SafeDisconn}): PFuture[void] = flags = {TSocketFlags.SafeDisconn}): Future[void] =
## Sends ``data`` to ``socket``. The returned future will complete once all ## Sends ``data`` to ``socket``. The returned future will complete once all
## data has been sent. ## data has been sent.
verifyPresence(socket) verifyPresence(socket)
@ -568,7 +572,7 @@ when defined(windows) or defined(nimdoc):
return retFuture return retFuture
proc acceptAddr*(socket: TAsyncFD, flags = {TSocketFlags.SafeDisconn}): proc acceptAddr*(socket: TAsyncFD, flags = {TSocketFlags.SafeDisconn}):
PFuture[tuple[address: string, client: TAsyncFD]] = Future[tuple[address: string, client: TAsyncFD]] =
## Accepts a new connection. Returns a future containing the client socket ## Accepts a new connection. Returns a future containing the client socket
## corresponding to that connection and the remote address of the client. ## corresponding to that connection and the remote address of the client.
## The future will complete when the connection is successfully accepted. ## The future will complete when the connection is successfully accepted.
@ -789,7 +793,7 @@ else:
processTimers(p) processTimers(p)
proc connect*(socket: TAsyncFD, address: string, port: TPort, proc connect*(socket: TAsyncFD, address: string, port: TPort,
af = AF_INET): PFuture[void] = af = AF_INET): Future[void] =
var retFuture = newFuture[void]("connect") var retFuture = newFuture[void]("connect")
proc cb(sock: TAsyncFD): bool = proc cb(sock: TAsyncFD): bool =
@ -824,7 +828,7 @@ else:
return retFuture return retFuture
proc recv*(socket: TAsyncFD, size: int, proc recv*(socket: TAsyncFD, size: int,
flags = {TSocketFlags.SafeDisconn}): PFuture[string] = flags = {TSocketFlags.SafeDisconn}): Future[string] =
var retFuture = newFuture[string]("recv") var retFuture = newFuture[string]("recv")
var readBuffer = newString(size) var readBuffer = newString(size)
@ -855,7 +859,7 @@ else:
return retFuture return retFuture
proc send*(socket: TAsyncFD, data: string, proc send*(socket: TAsyncFD, data: string,
flags = {TSocketFlags.SafeDisconn}): PFuture[void] = flags = {TSocketFlags.SafeDisconn}): Future[void] =
var retFuture = newFuture[void]("send") var retFuture = newFuture[void]("send")
var written = 0 var written = 0
@ -887,7 +891,7 @@ else:
return retFuture return retFuture
proc acceptAddr*(socket: TAsyncFD, flags = {TSocketFlags.SafeDisconn}): proc acceptAddr*(socket: TAsyncFD, flags = {TSocketFlags.SafeDisconn}):
PFuture[tuple[address: string, client: TAsyncFD]] = Future[tuple[address: string, client: TAsyncFD]] =
var retFuture = newFuture[tuple[address: string, var retFuture = newFuture[tuple[address: string,
client: TAsyncFD]]("acceptAddr") client: TAsyncFD]]("acceptAddr")
proc cb(sock: TAsyncFD): bool = proc cb(sock: TAsyncFD): bool =
@ -912,7 +916,7 @@ else:
addRead(socket, cb) addRead(socket, cb)
return retFuture return retFuture
proc sleepAsync*(ms: int): PFuture[void] = proc sleepAsync*(ms: int): Future[void] =
## Suspends the execution of the current async procedure for the next ## Suspends the execution of the current async procedure for the next
## ``ms`` miliseconds. ## ``ms`` miliseconds.
var retFuture = newFuture[void]("sleepAsync") var retFuture = newFuture[void]("sleepAsync")
@ -921,14 +925,14 @@ proc sleepAsync*(ms: int): PFuture[void] =
return retFuture return retFuture
proc accept*(socket: TAsyncFD, proc accept*(socket: TAsyncFD,
flags = {TSocketFlags.SafeDisconn}): PFuture[TAsyncFD] = flags = {TSocketFlags.SafeDisconn}): Future[TAsyncFD] =
## Accepts a new connection. Returns a future containing the client socket ## Accepts a new connection. Returns a future containing the client socket
## corresponding to that connection. ## corresponding to that connection.
## The future will complete when the connection is successfully accepted. ## The future will complete when the connection is successfully accepted.
var retFut = newFuture[TAsyncFD]("accept") var retFut = newFuture[TAsyncFD]("accept")
var fut = acceptAddr(socket, flags) var fut = acceptAddr(socket, flags)
fut.callback = fut.callback =
proc (future: PFuture[tuple[address: string, client: TAsyncFD]]) = proc (future: Future[tuple[address: string, client: TAsyncFD]]) =
assert future.finished assert future.finished
if future.failed: if future.failed:
retFut.fail(future.error) retFut.fail(future.error)
@ -1111,12 +1115,12 @@ macro async*(prc: stmt): stmt {.immediate.} =
hint("Processing " & prc[0].getName & " as an async proc.") hint("Processing " & prc[0].getName & " as an async proc.")
let returnType = prc[3][0] let returnType = prc[3][0]
# Verify that the return type is a PFuture[T] # Verify that the return type is a Future[T]
if returnType.kind == nnkIdent: if returnType.kind == nnkIdent:
error("Expected return type of 'PFuture' got '" & $returnType & "'") error("Expected return type of 'Future' got '" & $returnType & "'")
elif returnType.kind == nnkBracketExpr: elif returnType.kind == nnkBracketExpr:
if $returnType[0] != "PFuture": if $returnType[0] != "Future":
error("Expected return type of 'PFuture' got '" & $returnType[0] & "'") error("Expected return type of 'Future' got '" & $returnType[0] & "'")
let subtypeIsVoid = returnType.kind == nnkEmpty or let subtypeIsVoid = returnType.kind == nnkEmpty or
(returnType.kind == nnkBracketExpr and (returnType.kind == nnkBracketExpr and
@ -1137,7 +1141,7 @@ macro async*(prc: stmt): stmt {.immediate.} =
subRetType), subRetType),
newLit(prc[0].getName)))) # Get type from return type of this proc newLit(prc[0].getName)))) # Get type from return type of this proc
# -> iterator nameIter(): PFutureBase {.closure.} = # -> iterator nameIter(): FutureBase {.closure.} =
# -> var result: T # -> var result: T
# -> <proc_body> # -> <proc_body>
# -> complete(retFuture, result) # -> complete(retFuture, result)
@ -1153,7 +1157,7 @@ macro async*(prc: stmt): stmt {.immediate.} =
# -> complete(retFuture) # -> complete(retFuture)
procBody.add(newCall(newIdentNode("complete"), retFutureSym)) procBody.add(newCall(newIdentNode("complete"), retFutureSym))
var closureIterator = newProc(iteratorNameSym, [newIdentNode("PFutureBase")], var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase")],
procBody, nnkIteratorDef) procBody, nnkIteratorDef)
closureIterator[4] = newNimNode(nnkPragma, prc[6]).add(newIdentNode("closure")) closureIterator[4] = newNimNode(nnkPragma, prc[6]).add(newIdentNode("closure"))
outerProcBody.add(closureIterator) outerProcBody.add(closureIterator)
@ -1176,8 +1180,8 @@ macro async*(prc: stmt): stmt {.immediate.} =
if subtypeIsVoid: if subtypeIsVoid:
# Add discardable pragma. # Add discardable pragma.
if returnType.kind == nnkEmpty: if returnType.kind == nnkEmpty:
# Add PFuture[void] # Add Future[void]
result[3][0] = parseExpr("PFuture[void]") result[3][0] = parseExpr("Future[void]")
result[6] = outerProcBody result[6] = outerProcBody
@ -1185,7 +1189,7 @@ macro async*(prc: stmt): stmt {.immediate.} =
#if prc[0].getName == "processClient": #if prc[0].getName == "processClient":
# echo(toStrLit(result)) # echo(toStrLit(result))
proc recvLine*(socket: TAsyncFD): PFuture[string] {.async.} = proc recvLine*(socket: TAsyncFD): Future[string] {.async.} =
## Reads a line of data from ``socket``. Returned future will complete once ## Reads a line of data from ``socket``. Returned future will complete once
## a full line is read or an error occurs. ## a full line is read or an error occurs.
## ##

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2014 Dominik Picheta # (c) Copyright 2014 Dominik Picheta
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -13,7 +13,7 @@
import strtabs, asyncnet, asyncdispatch, parseutils, uri, strutils import strtabs, asyncnet, asyncdispatch, parseutils, uri, strutils
type type
TRequest* = object Request* = object
client*: PAsyncSocket # TODO: Separate this into a Response object? client*: PAsyncSocket # TODO: Separate this into a Response object?
reqMethod*: string reqMethod*: string
headers*: PStringTable headers*: PStringTable
@ -22,10 +22,10 @@ type
hostname*: string ## The hostname of the client that made the request. hostname*: string ## The hostname of the client that made the request.
body*: string body*: string
PAsyncHttpServer* = ref object AsyncHttpServer* = ref object
socket: PAsyncSocket socket: PAsyncSocket
THttpCode* = enum HttpCode* = enum
Http200 = "200 OK", Http200 = "200 OK",
Http303 = "303 Moved", Http303 = "303 Moved",
Http400 = "400 Bad Request", Http400 = "400 Bad Request",
@ -33,10 +33,13 @@ type
Http500 = "500 Internal Server Error", Http500 = "500 Internal Server Error",
Http502 = "502 Bad Gateway" Http502 = "502 Bad Gateway"
THttpVersion* = enum HttpVersion* = enum
HttpVer11, HttpVer11,
HttpVer10 HttpVer10
{.deprecated: [TRequest: Request, PAsyncHttpServer: AsyncHttpServer,
THttpCode: HttpCode, THttpVersion: HttpVersion].}
proc `==`*(protocol: tuple[orig: string, major, minor: int], proc `==`*(protocol: tuple[orig: string, major, minor: int],
ver: THttpVersion): bool = ver: THttpVersion): bool =
let major = let major =

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf, Dominik Picheta # (c) Copyright 2012 Andreas Rumpf, Dominik Picheta
# 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.
@ -10,8 +10,8 @@ include "system/inclrtl"
import sockets, os import sockets, os
## This module implements an asynchronous event loop together with asynchronous sockets ## This module implements an asynchronous event loop together with asynchronous
## which use this event loop. ## sockets which use this event loop.
## It is akin to Python's asyncore module. Many modules that use sockets ## It is akin to Python's asyncore module. Many modules that use sockets
## have an implementation for this module, those modules should all have a ## have an implementation for this module, those modules should all have a
## ``register`` function which you should use to add the desired objects to a ## ``register`` function which you should use to add the desired objects to a
@ -31,10 +31,10 @@ import sockets, os
## ##
## Most (if not all) modules that use asyncio provide a userArg which is passed ## Most (if not all) modules that use asyncio provide a userArg which is passed
## on with the events. The type that you set userArg to must be inheriting from ## on with the events. The type that you set userArg to must be inheriting from
## TObject! ## ``RootObj``!
## ##
## **Note:** If you want to provide async ability to your module please do not ## **Note:** If you want to provide async ability to your module please do not
## use the ``TDelegate`` object, instead use ``PAsyncSocket``. It is possible ## use the ``Delegate`` object, instead use ``AsyncSocket``. It is possible
## that in the future this type's fields will not be exported therefore breaking ## that in the future this type's fields will not be exported therefore breaking
## your code. ## your code.
## ##
@ -44,10 +44,10 @@ import sockets, os
## Asynchronous sockets ## Asynchronous sockets
## ==================== ## ====================
## ##
## For most purposes you do not need to worry about the ``TDelegate`` type. The ## For most purposes you do not need to worry about the ``Delegate`` type. The
## ``PAsyncSocket`` is what you are after. It's a reference to the ``TAsyncSocket`` ## ``AsyncSocket`` is what you are after. It's a reference to
## object. This object defines events which you should overwrite by your own ## the ``AsyncSocketObj`` object. This object defines events which you should
## procedures. ## overwrite by your own procedures.
## ##
## For server sockets the only event you need to worry about is the ``handleAccept`` ## For server sockets the only event you need to worry about is the ``handleAccept``
## event, in your handleAccept proc you should call ``accept`` on the server ## event, in your handleAccept proc you should call ``accept`` on the server
@ -57,13 +57,13 @@ import sockets, os
## ##
## An example ``handleAccept`` follows: ## An example ``handleAccept`` follows:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## ##
## var disp: PDispatcher = newDispatcher() ## var disp = newDispatcher()
## ... ## ...
## proc handleAccept(s: PAsyncSocket) = ## proc handleAccept(s: AsyncSocket) =
## echo("Accepted client.") ## echo("Accepted client.")
## var client: PAsyncSocket ## var client: AsyncSocket
## new(client) ## new(client)
## s.accept(client) ## s.accept(client)
## client.handleRead = ... ## client.handleRead = ...
@ -76,29 +76,29 @@ import sockets, os
## the socket has established a connection to a server socket; from that point ## the socket has established a connection to a server socket; from that point
## it can be safely written to. ## it can be safely written to.
## ##
## Getting a blocking client from a PAsyncSocket ## Getting a blocking client from an AsyncSocket
## ============================================= ## =============================================
## ##
## If you need a asynchronous server socket but you wish to process the clients ## If you need a asynchronous server socket but you wish to process the clients
## synchronously then you can use the ``getSocket`` converter to get a TSocket ## synchronously then you can use the ``getSocket`` converter to get
## object from the PAsyncSocket object, this can then be combined with ``accept`` ## a ``Socket`` from the ``AsyncSocket`` object, this can then be combined
## like so: ## with ``accept`` like so:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## ##
## proc handleAccept(s: PAsyncSocket) = ## proc handleAccept(s: AsyncSocket) =
## var client: TSocket ## var client: Socket
## getSocket(s).accept(client) ## getSocket(s).accept(client)
when defined(windows): when defined(windows):
from winlean import TTimeVal, TSocketHandle, TFdSet, FD_ZERO, FD_SET, FD_ISSET, select from winlean import TimeVal, SocketHandle, FdSet, FD_ZERO, FD_SET, FD_ISSET, select
else: else:
from posix import TTimeVal, TSocketHandle, TFdSet, FD_ZERO, FD_SET, FD_ISSET, select from posix import TimeVal, SocketHandle, FdSet, FD_ZERO, FD_SET, FD_ISSET, select
type type
TDelegate* = object DelegateObj* = object
fd*: TSocketHandle fd*: SocketHandle
deleVal*: PObject deleVal*: RootRef
handleRead*: proc (h: PObject) {.nimcall, gcsafe.} handleRead*: proc (h: PObject) {.nimcall, gcsafe.}
handleWrite*: proc (h: PObject) {.nimcall, gcsafe.} handleWrite*: proc (h: PObject) {.nimcall, gcsafe.}
@ -107,18 +107,18 @@ type
open*: bool open*: bool
task*: proc (h: PObject) {.nimcall, gcsafe.} task*: proc (h: PObject) {.nimcall, gcsafe.}
mode*: TFileMode mode*: FileMode
PDelegate* = ref TDelegate Delegate* = ref DelegateObj
PDispatcher* = ref TDispatcher Dispatcher* = ref DispatcherObj
TDispatcher = object DispatcherObj = object
delegates: seq[PDelegate] delegates: seq[PDelegate]
PAsyncSocket* = ref TAsyncSocket AsyncSocket* = ref AsyncSocketObj
TAsyncSocket* = object of TObject AsyncSocketObj* = object of RootObj
socket: TSocket socket: Socket
info: TInfo info: SocketStatus
handleRead*: proc (s: PAsyncSocket) {.closure, gcsafe.} handleRead*: proc (s: PAsyncSocket) {.closure, gcsafe.}
handleWrite: proc (s: PAsyncSocket) {.closure, gcsafe.} handleWrite: proc (s: PAsyncSocket) {.closure, gcsafe.}
@ -134,10 +134,17 @@ type
proto: TProtocol proto: TProtocol
deleg: PDelegate deleg: PDelegate
TInfo* = enum SocketStatus* = enum
SockIdle, SockConnecting, SockConnected, SockListening, SockClosed, SockIdle, SockConnecting, SockConnected, SockListening, SockClosed,
SockUDPBound SockUDPBound
{.deprecated: [TDelegate: DelegateObj, PDelegate: Delegate,
TProcessOption: ProcessOption,
TInfo: SocketStatus, PAsyncSocket: AsyncSocket, TAsyncSocket: AsyncSocketObj,
TDispatcher: DispatcherObj, PDispatcher: Dispatcher,
].}
proc newDelegate*(): PDelegate = proc newDelegate*(): PDelegate =
## Creates a new delegate. ## Creates a new delegate.
new(result) new(result)

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2014 Dominik Picheta # (c) Copyright 2014 Dominik Picheta
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -19,9 +19,9 @@
## ##
## import asyncnet, asyncdispatch ## import asyncnet, asyncdispatch
## ##
## var clients: seq[PAsyncSocket] = @[] ## var clients: seq[AsyncSocket] = @[]
## ##
## proc processClient(client: PAsyncSocket) {.async.} = ## proc processClient(client: AsyncSocket) {.async.} =
## while true: ## while true:
## let line = await client.recvLine() ## let line = await client.recvLine()
## for c in clients: ## for c in clients:
@ -29,7 +29,7 @@
## ##
## proc serve() {.async.} = ## proc serve() {.async.} =
## var server = newAsyncSocket() ## var server = newAsyncSocket()
## server.bindAddr(TPort(12345)) ## server.bindAddr(Port(12345))
## server.listen() ## server.listen()
## ##
## while true: ## while true:
@ -54,8 +54,10 @@ when defined(ssl):
type type
# TODO: I would prefer to just do: # TODO: I would prefer to just do:
# PAsyncSocket* {.borrow: `.`.} = distinct PSocket. But that doesn't work. # PAsyncSocket* {.borrow: `.`.} = distinct PSocket. But that doesn't work.
TAsyncSocket {.borrow: `.`.} = distinct TSocketImpl AsyncSocketDesc {.borrow: `.`.} = distinct TSocketImpl
PAsyncSocket* = ref TAsyncSocket AsyncSocket* = ref AsyncSocketDesc
{.deprecated: [PAsyncSocket: AsyncSocket].}
# TODO: Save AF, domain etc info and reuse it in procs which need it like connect. # TODO: Save AF, domain etc info and reuse it in procs which need it like connect.

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2010 Andreas Rumpf # (c) Copyright 2010 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2013 Robert Persson # (c) Copyright 2013 Robert Persson
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2013 Robert Persson # (c) Copyright 2013 Robert Persson
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -44,5 +44,5 @@ proc openDefaultBrowser*(url: string) =
# we use ``startProcess`` here because we don't want to block! # we use ``startProcess`` here because we don't want to block!
discard startProcess(command=b, args=[url], options={poUseShell}) discard startProcess(command=b, args=[url], options={poUseShell})
return return
except EOS: except OSError:
discard discard

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -9,7 +9,7 @@
## This module implements helper procs for CGI applications. Example: ## This module implements helper procs for CGI applications. Example:
## ##
## .. code-block:: Nimrod ## .. code-block:: Nim
## ##
## import strtabs, cgi ## import strtabs, cgi
## ##
@ -93,12 +93,14 @@ proc XMLencode*(s: string): string =
for i in 0..len(s)-1: addXmlChar(result, s[i]) for i in 0..len(s)-1: addXmlChar(result, s[i])
type type
ECgi* = object of IOError ## the exception that is raised, if a CGI error occurs CgiError* = object of IOError ## exception that is raised if a CGI error occurs
TRequestMethod* = enum ## the used request method RequestMethod* = enum ## the used request method
methodNone, ## no REQUEST_METHOD environment variable methodNone, ## no REQUEST_METHOD environment variable
methodPost, ## query uses the POST method methodPost, ## query uses the POST method
methodGet ## query uses the GET method methodGet ## query uses the GET method
{.deprecated: [TRequestMethod: RequestMethod, ECgi: CgiError].}
proc cgiError*(msg: string) {.noreturn.} = proc cgiError*(msg: string) {.noreturn.} =
## raises an ECgi exception with message `msg`. ## raises an ECgi exception with message `msg`.
var e: ref ECgi var e: ref ECgi
@ -377,7 +379,7 @@ proc setCookie*(name, value: string) =
write(stdout, "Set-Cookie: ", name, "=", value, "\n") write(stdout, "Set-Cookie: ", name, "=", value, "\n")
var var
gcookies {.threadvar.}: PStringTable gcookies {.threadvar.}: StringTableRef
proc getCookie*(name: string): TaintedString = proc getCookie*(name: string): TaintedString =
## Gets a cookie. If no cookie of `name` exists, "" is returned. ## Gets a cookie. If no cookie of `name` exists, "" is returned.

View file

@ -1,8 +1,40 @@
#nimrod c -t:-march=i686 --cpu:amd64 --threads:on -d:release lockfreehash.nim #nimrod c -t:-march=i686 --cpu:amd64 --threads:on -d:release lockfreehash.nim
import baseutils, unsigned, math, hashes import unsigned, math, hashes
#------------------------------------------------------------------------------
## Memory Utility Functions
proc newHeap*[T](): ptr T =
result = cast[ptr T](alloc0(sizeof(T)))
proc copyNew*[T](x: var T): ptr T =
var
size = sizeof(T)
mem = alloc(size)
copyMem(mem, x.addr, size)
return cast[ptr T](mem)
proc copyTo*[T](val: var T, dest: int) =
copyMem(pointer(dest), val.addr, sizeof(T))
proc allocType*[T](): pointer = alloc(sizeof(T))
proc newShared*[T](): ptr T =
result = cast[ptr T](allocShared0(sizeof(T)))
proc copyShared*[T](x: var T): ptr T =
var
size = sizeof(T)
mem = allocShared(size)
copyMem(mem, x.addr, size)
return cast[ptr T](mem)
#------------------------------------------------------------------------------
## Pointer arithmetic
proc `+`*(p: pointer, i: int): pointer {.inline.} =
cast[pointer](cast[int](p) + i)
const const
minTableSize = 8 minTableSize = 8
@ -194,7 +226,7 @@ proc copySlot[K,V](idx: int, oldTbl: var PConcTable[K,V], newTbl: var PConcTable
#Prevent new values from appearing in the old table by priming #Prevent new values from appearing in the old table by priming
oldVal = atomic_load_n(oldTbl[idx].value.addr, ATOMIC_RELAXED) oldVal = atomic_load_n(oldTbl[idx].value.addr, ATOMIC_RELAXED)
while not isPrime(oldVal): while not isPrime(oldVal):
var box = if oldVal == NULL or isTomb(oldVal) : oldVal.setTomb.setPrime var box = if oldVal == 0 or isTomb(oldVal) : oldVal.setTomb.setPrime
else: oldVal.setPrime else: oldVal.setPrime
if atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr, if atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr,
box, false, ATOMIC_RELAXED, ATOMIC_RELAXED): box, false, ATOMIC_RELAXED, ATOMIC_RELAXED):
@ -209,8 +241,8 @@ proc copySlot[K,V](idx: int, oldTbl: var PConcTable[K,V], newTbl: var PConcTable
return false return false
if isTomb(oldVal): if isTomb(oldVal):
echo("oldVal is Tomb!!!, should not happen") echo("oldVal is Tomb!!!, should not happen")
if pop(oldVal) != NULL: if pop(oldVal) != 0:
result = setVal(newTbl, pop(oldKey), pop(oldVal), NULL, true) == NULL result = setVal(newTbl, pop(oldKey), pop(oldVal), 0, true) == 0
if result: if result:
#echo("Copied a Slot! idx= " & $idx & " key= " & $oldKey & " val= " & $oldVal) #echo("Copied a Slot! idx= " & $idx & " key= " & $oldKey & " val= " & $oldVal)
else: else:
@ -323,7 +355,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
idx = idx and (table.len - 1) idx = idx and (table.len - 1)
#echo("try set idx = " & $idx & "for" & $key) #echo("try set idx = " & $idx & "for" & $key)
var var
probedKey = NULL probedKey = 0
openKey = atomic_compare_exchange_n(table[idx].key.addr, probedKey.addr, openKey = atomic_compare_exchange_n(table[idx].key.addr, probedKey.addr,
key, false, ATOMIC_RELAXED, ATOMIC_RELAXED) key, false, ATOMIC_RELAXED, ATOMIC_RELAXED)
if openKey: if openKey:
@ -339,7 +371,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
if keyEQ[K](probedKey, key): if keyEQ[K](probedKey, key):
#echo("we found the matching slot") #echo("we found the matching slot")
break # We found a matching slot break # We found a matching slot
if (not(expVal != NULL and match)) and (probes >= reProbeLimit or key.isTomb): if (not(expVal != 0 and match)) and (probes >= reProbeLimit or key.isTomb):
if key.isTomb: echo("Key is Tombstone") if key.isTomb: echo("Key is Tombstone")
#if probes >= reProbeLimit: echo("Too much probing " & $probes) #if probes >= reProbeLimit: echo("Too much probing " & $probes)
#echo("try to resize") #echo("try to resize")
@ -361,7 +393,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
return oldVal return oldVal
nextTable = atomic_load_n(table.next.addr, ATOMIC_SEQ_CST) nextTable = atomic_load_n(table.next.addr, ATOMIC_SEQ_CST)
if nextTable == nil and if nextTable == nil and
((oldVal == NULL and ((oldVal == 0 and
(probes >= reProbeLimit or table.used / table.len > 0.8)) or (probes >= reProbeLimit or table.used / table.len > 0.8)) or
(isPrime(oldVal))): (isPrime(oldVal))):
if table.used / table.len > 0.8: echo("resize because usage ratio = " & if table.used / table.len > 0.8: echo("resize because usage ratio = " &
@ -380,12 +412,12 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
if atomic_compare_exchange_n(table[idx].value.addr, oldVal.addr, if atomic_compare_exchange_n(table[idx].value.addr, oldVal.addr,
val, false, ATOMIC_RELEASE, ATOMIC_RELAXED): val, false, ATOMIC_RELEASE, ATOMIC_RELAXED):
#echo("val set at table " & $cast[int](table)) #echo("val set at table " & $cast[int](table))
if expVal != NULL: if expVal != 0:
if (oldVal == NULL or isTomb(oldVal)) and not isTomb(val): if (oldVal == 0 or isTomb(oldVal)) and not isTomb(val):
discard atomic_add_fetch(table.active.addr, 1, ATOMIC_RELAXED) discard atomic_add_fetch(table.active.addr, 1, ATOMIC_RELAXED)
elif not (oldVal == NULL or isTomb(oldVal)) and isTomb(val): elif not (oldVal == 0 or isTomb(oldVal)) and isTomb(val):
discard atomic_add_fetch(table.active.addr, -1, ATOMIC_RELAXED) discard atomic_add_fetch(table.active.addr, -1, ATOMIC_RELAXED)
if oldVal == NULL and expVal != NULL: if oldVal == 0 and expVal != 0:
return setTomb(oldVal) return setTomb(oldVal)
else: return oldVal else: return oldVal
if isPrime(oldVal): if isPrime(oldVal):
@ -415,7 +447,7 @@ proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
if not isPrime(val): if not isPrime(val):
if isTomb(val): if isTomb(val):
#echo("val was tomb but not prime") #echo("val was tomb but not prime")
return NULL return 0
else: else:
#echo("-GotIt- idx = ", idx, " key = ", key, " val ", val ) #echo("-GotIt- idx = ", idx, " key = ", key, " val ", val )
return val return val
@ -427,7 +459,7 @@ proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
if probes >= reProbeLimit*4 or key.isTomb: if probes >= reProbeLimit*4 or key.isTomb:
if newTable == nil: if newTable == nil:
#echo("too many probes and no new table ", key, " ", idx ) #echo("too many probes and no new table ", key, " ", idx )
return NULL return 0
else: else:
newTable = helpCopy(table) newTable = helpCopy(table)
return getVal(newTable, key) return getVal(newTable, key)
@ -437,10 +469,10 @@ proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
#proc set*(table: var PConcTable[TRaw,TRaw], key: TRaw, val: TRaw) = #proc set*(table: var PConcTable[TRaw,TRaw], key: TRaw, val: TRaw) =
# discard setVal(table, pack(key), pack(key), NULL, false) # discard setVal(table, pack(key), pack(key), 0, false)
#proc set*[V](table: var PConcTable[TRaw,V], key: TRaw, val: ptr V) = #proc set*[V](table: var PConcTable[TRaw,V], key: TRaw, val: ptr V) =
# discard setVal(table, pack(key), cast[int](val), NULL, false) # discard setVal(table, pack(key), cast[int](val), 0, false)
proc set*[K,V](table: var PConcTable[K,V], key: var K, val: var V) = proc set*[K,V](table: var PConcTable[K,V], key: var K, val: var V) =
when not (K is TRaw): when not (K is TRaw):
@ -451,10 +483,10 @@ proc set*[K,V](table: var PConcTable[K,V], key: var K, val: var V) =
var newVal = cast[int](copyShared(val)) var newVal = cast[int](copyShared(val))
else: else:
var newVal = pack(val) var newVal = pack(val)
var oldPtr = pop(setVal(table, newKey, newVal, NULL, false)) var oldPtr = pop(setVal(table, newKey, newVal, 0, false))
#echo("oldPtr = ", cast[int](oldPtr), " newPtr = ", cast[int](newPtr)) #echo("oldPtr = ", cast[int](oldPtr), " newPtr = ", cast[int](newPtr))
when not (V is TRaw): when not (V is TRaw):
if newVal != oldPtr and oldPtr != NULL: if newVal != oldPtr and oldPtr != 0:
deallocShared(cast[ptr V](oldPtr)) deallocShared(cast[ptr V](oldPtr))
@ -573,10 +605,3 @@ when isMainModule:
# echo(i, " = ", hashInt(i) and 8191) # echo(i, " = ", hashInt(i) and 8191)
deleteConcTable(table) deleteConcTable(table)

View file

@ -1,41 +0,0 @@
#------------------------------------------------------------------------------
## Useful Constants
const NULL* = 0
#------------------------------------------------------------------------------
## Memory Utility Functions
proc newHeap*[T](): ptr T =
result = cast[ptr T](alloc0(sizeof(T)))
proc copyNew*[T](x: var T): ptr T =
var
size = sizeof(T)
mem = alloc(size)
copyMem(mem, x.addr, size)
return cast[ptr T](mem)
proc copyTo*[T](val: var T, dest: int) =
copyMem(pointer(dest), val.addr, sizeof(T))
proc allocType*[T](): pointer = alloc(sizeof(T))
proc newShared*[T](): ptr T =
result = cast[ptr T](allocShared0(sizeof(T)))
proc copyShared*[T](x: var T): ptr T =
var
size = sizeof(T)
mem = allocShared(size)
copyMem(mem, x.addr, size)
return cast[ptr T](mem)
#------------------------------------------------------------------------------
## Pointer arithmetic
proc `+`*(p: pointer, i: int): pointer {.inline.} =
cast[pointer](cast[int](p) + i)

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -12,30 +12,32 @@
## by Adam Langley. ## by Adam Langley.
type type
TNode[T] = object {.pure, final, acyclic.} NodeObj[T] = object {.pure, final, acyclic.}
byte: int ## byte index of the difference byte: int ## byte index of the difference
otherbits: char otherbits: char
case isLeaf: bool case isLeaf: bool
of false: child: array[0..1, ref TNode[T]] of false: child: array[0..1, ref NodeObj[T]]
of true: of true:
key: string key: string
when T isnot void: when T isnot void:
val: T val: T
PNode[T] = ref TNode[T] Node[T] = ref NodeObj[T]
TCritBitTree*[T] = object {. CritBitTree*[T] = object {.
pure, final.} ## The crit bit tree can either be used pure, final.} ## The crit bit tree can either be used
## as a mapping from strings to ## as a mapping from strings to
## some type ``T`` or as a set of ## some type ``T`` or as a set of
## strings if ``T`` is void. ## strings if ``T`` is void.
root: PNode[T] root: Node[T]
count: int count: int
proc len*[T](c: TCritBitTree[T]): int = {.deprecated: [TCritBitTree: CritBitTree].}
proc len*[T](c: CritBitTree[T]): int =
## returns the number of elements in `c` in O(1). ## returns the number of elements in `c` in O(1).
result = c.count result = c.count
proc rawGet[T](c: TCritBitTree[T], key: string): PNode[T] = proc rawGet[T](c: CritBitTree[T], key: string): Node[T] =
var it = c.root var it = c.root
while it != nil: while it != nil:
if not it.isLeaf: if not it.isLeaf:
@ -45,15 +47,15 @@ proc rawGet[T](c: TCritBitTree[T], key: string): PNode[T] =
else: else:
return if it.key == key: it else: nil return if it.key == key: it else: nil
proc contains*[T](c: TCritBitTree[T], key: string): bool {.inline.} = proc contains*[T](c: CritBitTree[T], key: string): bool {.inline.} =
## returns true iff `c` contains the given `key`. ## returns true iff `c` contains the given `key`.
result = rawGet(c, key) != nil result = rawGet(c, key) != nil
proc hasKey*[T](c: TCritBitTree[T], key: string): bool {.inline.} = proc hasKey*[T](c: CritBitTree[T], key: string): bool {.inline.} =
## alias for `contains`. ## alias for `contains`.
result = rawGet(c, key) != nil result = rawGet(c, key) != nil
proc rawInsert[T](c: var TCritBitTree[T], key: string): PNode[T] = proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] =
if c.root == nil: if c.root == nil:
new c.root new c.root
c.root.isleaf = true c.root.isleaf = true
@ -84,7 +86,7 @@ proc rawInsert[T](c: var TCritBitTree[T], key: string): PNode[T] =
let ch = it.key[newByte] let ch = it.key[newByte]
let dir = (1 + (ord(ch) or newOtherBits)) shr 8 let dir = (1 + (ord(ch) or newOtherBits)) shr 8
var inner: PNode[T] var inner: Node[T]
new inner new inner
new result new result
result.isLeaf = true result.isLeaf = true
@ -106,7 +108,7 @@ proc rawInsert[T](c: var TCritBitTree[T], key: string): PNode[T] =
wherep[] = inner wherep[] = inner
inc c.count inc c.count
proc containsOrIncl*[T](c: var TCritBitTree[T], key: string, val: T): bool = proc containsOrIncl*[T](c: var CritBitTree[T], key: string, val: T): bool =
## returns true iff `c` contains the given `key`. If the key does not exist ## returns true iff `c` contains the given `key`. If the key does not exist
## ``c[key] = val`` is performed. ## ``c[key] = val`` is performed.
let oldCount = c.count let oldCount = c.count
@ -115,23 +117,23 @@ proc containsOrIncl*[T](c: var TCritBitTree[T], key: string, val: T): bool =
when T isnot void: when T isnot void:
if not result: n.val = val if not result: n.val = val
proc containsOrIncl*(c: var TCritBitTree[void], key: string): bool = proc containsOrIncl*(c: var CritBitTree[void], key: string): bool =
## returns true iff `c` contains the given `key`. If the key does not exist ## returns true iff `c` contains the given `key`. If the key does not exist
## it is inserted into `c`. ## it is inserted into `c`.
let oldCount = c.count let oldCount = c.count
var n = rawInsert(c, key) var n = rawInsert(c, key)
result = c.count == oldCount result = c.count == oldCount
proc incl*(c: var TCritBitTree[void], key: string) = proc incl*(c: var CritBitTree[void], key: string) =
## includes `key` in `c`. ## includes `key` in `c`.
discard rawInsert(c, key) discard rawInsert(c, key)
proc `[]=`*[T](c: var TCritBitTree[T], key: string, val: T) = proc `[]=`*[T](c: var CritBitTree[T], key: string, val: T) =
## puts a (key, value)-pair into `t`. ## puts a (key, value)-pair into `t`.
var n = rawInsert(c, key) var n = rawInsert(c, key)
n.val = val n.val = val
proc `[]`*[T](c: TCritBitTree[T], key: string): T {.inline.} = proc `[]`*[T](c: CritBitTree[T], key: string): T {.inline.} =
## retrieves the value at ``c[key]``. If `key` is not in `t`, ## retrieves the value at ``c[key]``. If `key` is not in `t`,
## default empty value for the type `B` is returned ## default empty value for the type `B` is returned
## and no exception is raised. One can check with ``hasKey`` whether the key ## and no exception is raised. One can check with ``hasKey`` whether the key
@ -139,22 +141,22 @@ proc `[]`*[T](c: TCritBitTree[T], key: string): T {.inline.} =
let n = rawGet(c, key) let n = rawGet(c, key)
if n != nil: result = n.val if n != nil: result = n.val
proc mget*[T](c: var TCritBitTree[T], key: string): var T {.inline.} = proc mget*[T](c: var CritBitTree[T], key: string): var T {.inline.} =
## retrieves the value at ``c[key]``. The value can be modified. ## retrieves the value at ``c[key]``. The value can be modified.
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised. ## If `key` is not in `t`, the ``KeyError`` exception is raised.
let n = rawGet(c, key) let n = rawGet(c, key)
if n != nil: result = n.val if n != nil: result = n.val
else: raise newException(EInvalidKey, "key not found: " & $key) else: raise newException(KeyError, "key not found: " & $key)
proc excl*[T](c: var TCritBitTree[T], key: string) = proc excl*[T](c: var CritBitTree[T], key: string) =
## removes `key` (and its associated value) from the set `c`. ## removes `key` (and its associated value) from the set `c`.
## If the `key` does not exist, nothing happens. ## If the `key` does not exist, nothing happens.
var p = c.root var p = c.root
var wherep = addr(c.root) var wherep = addr(c.root)
var whereq: ptr PNode = nil var whereq: ptr Node[T] = nil
if p == nil: return if p == nil: return
var dir = 0 var dir = 0
var q: PNode var q: Node[T]
while not p.isLeaf: while not p.isLeaf:
whereq = wherep whereq = wherep
q = p q = p
@ -170,7 +172,7 @@ proc excl*[T](c: var TCritBitTree[T], key: string) =
whereq[] = q.child[1 - dir] whereq[] = q.child[1 - dir]
dec c.count dec c.count
iterator leaves[T](n: PNode[T]): PNode[T] = iterator leaves[T](n: Node[T]): Node[T] =
if n != nil: if n != nil:
# XXX actually we could compute the necessary stack size in advance: # XXX actually we could compute the necessary stack size in advance:
# it's rougly log2(c.count). # it's rougly log2(c.count).
@ -183,33 +185,33 @@ iterator leaves[T](n: PNode[T]): PNode[T] =
assert(it != nil) assert(it != nil)
yield it yield it
iterator keys*[T](c: TCritBitTree[T]): string = iterator keys*[T](c: CritBitTree[T]): string =
## yields all keys in lexicographical order. ## yields all keys in lexicographical order.
for x in leaves(c.root): yield x.key for x in leaves(c.root): yield x.key
iterator values*[T](c: TCritBitTree[T]): T = iterator values*[T](c: CritBitTree[T]): T =
## yields all values of `c` in the lexicographical order of the ## yields all values of `c` in the lexicographical order of the
## corresponding keys. ## corresponding keys.
for x in leaves(c.root): yield x.val for x in leaves(c.root): yield x.val
iterator mvalues*[T](c: var TCritBitTree[T]): var T = iterator mvalues*[T](c: var CritBitTree[T]): var T =
## yields all values of `c` in the lexicographical order of the ## yields all values of `c` in the lexicographical order of the
## corresponding keys. The values can be modified. ## corresponding keys. The values can be modified.
for x in leaves(c.root): yield x.val for x in leaves(c.root): yield x.val
iterator items*[T](c: TCritBitTree[T]): string = iterator items*[T](c: CritBitTree[T]): string =
## yields all keys in lexicographical order. ## yields all keys in lexicographical order.
for x in leaves(c.root): yield x.key for x in leaves(c.root): yield x.key
iterator pairs*[T](c: TCritBitTree[T]): tuple[key: string, val: T] = iterator pairs*[T](c: CritBitTree[T]): tuple[key: string, val: T] =
## yields all (key, value)-pairs of `c`. ## yields all (key, value)-pairs of `c`.
for x in leaves(c.root): yield (x.key, x.val) for x in leaves(c.root): yield (x.key, x.val)
iterator mpairs*[T](c: var TCritBitTree[T]): tuple[key: string, val: var T] = iterator mpairs*[T](c: var CritBitTree[T]): tuple[key: string, val: var T] =
## yields all (key, value)-pairs of `c`. The yielded values can be modified. ## yields all (key, value)-pairs of `c`. The yielded values can be modified.
for x in leaves(c.root): yield (x.key, x.val) for x in leaves(c.root): yield (x.key, x.val)
proc allprefixedAux[T](c: TCritBitTree[T], key: string): PNode[T] = proc allprefixedAux[T](c: CritBitTree[T], key: string): Node[T] =
var p = c.root var p = c.root
var top = p var top = p
if p != nil: if p != nil:
@ -223,42 +225,42 @@ proc allprefixedAux[T](c: TCritBitTree[T], key: string): PNode[T] =
if p.key[i] != key[i]: return if p.key[i] != key[i]: return
result = top result = top
iterator itemsWithPrefix*[T](c: TCritBitTree[T], prefix: string): string = iterator itemsWithPrefix*[T](c: CritBitTree[T], prefix: string): string =
## yields all keys starting with `prefix`. ## yields all keys starting with `prefix`.
let top = allprefixedAux(c, prefix) let top = allprefixedAux(c, prefix)
for x in leaves(top): yield x.key for x in leaves(top): yield x.key
iterator keysWithPrefix*[T](c: TCritBitTree[T], prefix: string): string = iterator keysWithPrefix*[T](c: CritBitTree[T], prefix: string): string =
## yields all keys starting with `prefix`. ## yields all keys starting with `prefix`.
let top = allprefixedAux(c, prefix) let top = allprefixedAux(c, prefix)
for x in leaves(top): yield x.key for x in leaves(top): yield x.key
iterator valuesWithPrefix*[T](c: TCritBitTree[T], prefix: string): T = iterator valuesWithPrefix*[T](c: CritBitTree[T], prefix: string): T =
## yields all values of `c` starting with `prefix` of the ## yields all values of `c` starting with `prefix` of the
## corresponding keys. ## corresponding keys.
let top = allprefixedAux(c, prefix) let top = allprefixedAux(c, prefix)
for x in leaves(top): yield x.val for x in leaves(top): yield x.val
iterator mvaluesWithPrefix*[T](c: var TCritBitTree[T], prefix: string): var T = iterator mvaluesWithPrefix*[T](c: var CritBitTree[T], prefix: string): var T =
## yields all values of `c` starting with `prefix` of the ## yields all values of `c` starting with `prefix` of the
## corresponding keys. The values can be modified. ## corresponding keys. The values can be modified.
let top = allprefixedAux(c, prefix) let top = allprefixedAux(c, prefix)
for x in leaves(top): yield x.val for x in leaves(top): yield x.val
iterator pairsWithPrefix*[T](c: TCritBitTree[T], iterator pairsWithPrefix*[T](c: CritBitTree[T],
prefix: string): tuple[key: string, val: T] = prefix: string): tuple[key: string, val: T] =
## yields all (key, value)-pairs of `c` starting with `prefix`. ## yields all (key, value)-pairs of `c` starting with `prefix`.
let top = allprefixedAux(c, prefix) let top = allprefixedAux(c, prefix)
for x in leaves(top): yield (x.key, x.val) for x in leaves(top): yield (x.key, x.val)
iterator mpairsWithPrefix*[T](c: var TCritBitTree[T], iterator mpairsWithPrefix*[T](c: var CritBitTree[T],
prefix: string): tuple[key: string, val: var T] = prefix: string): tuple[key: string, val: var T] =
## yields all (key, value)-pairs of `c` starting with `prefix`. ## yields all (key, value)-pairs of `c` starting with `prefix`.
## The yielded values can be modified. ## The yielded values can be modified.
let top = allprefixedAux(c, prefix) let top = allprefixedAux(c, prefix)
for x in leaves(top): yield (x.key, x.val) for x in leaves(top): yield (x.key, x.val)
proc `$`*[T](c: TCritBitTree[T]): string = proc `$`*[T](c: CritBitTree[T]): string =
## turns `c` into a string representation. Example outputs: ## turns `c` into a string representation. Example outputs:
## ``{keyA: value, keyB: value}``, ``{:}`` ## ``{keyA: value, keyB: value}``, ``{:}``
## If `T` is void the outputs look like: ## If `T` is void the outputs look like:
@ -285,7 +287,7 @@ proc `$`*[T](c: TCritBitTree[T]): string =
result.add("}") result.add("}")
when isMainModule: when isMainModule:
var r: TCritBitTree[void] var r: CritBitTree[void]
r.incl "abc" r.incl "abc"
r.incl "xyz" r.incl "xyz"
r.incl "def" r.incl "def"

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -9,7 +9,7 @@
## The ``intsets`` module implements an efficient int set implemented as a ## The ``intsets`` module implements an efficient int set implemented as a
## sparse bit set. ## sparse bit set.
## **Note**: Since Nimrod currently does not allow the assignment operator to ## **Note**: Since Nim currently does not allow the assignment operator to
## be overloaded, ``=`` for int sets performs some rather meaningless shallow ## be overloaded, ``=`` for int sets performs some rather meaningless shallow
## copy; use ``assign`` to get a deep copy. ## copy; use ``assign`` to get a deep copy.
@ -17,7 +17,7 @@ import
os, hashes, math os, hashes, math
type type
TBitScalar = int BitScalar = int
const const
InitIntSetSize = 8 # must be a power of two! InitIntSetSize = 8 # must be a power of two!
@ -25,8 +25,8 @@ const
BitsPerTrunk = 1 shl TrunkShift # needs to be a power of 2 and BitsPerTrunk = 1 shl TrunkShift # needs to be a power of 2 and
# divisible by 64 # divisible by 64
TrunkMask = BitsPerTrunk - 1 TrunkMask = BitsPerTrunk - 1
IntsPerTrunk = BitsPerTrunk div (sizeof(TBitScalar) * 8) IntsPerTrunk = BitsPerTrunk div (sizeof(BitScalar) * 8)
IntShift = 5 + ord(sizeof(TBitScalar) == 8) # 5 or 6, depending on int width IntShift = 5 + ord(sizeof(BitScalar) == 8) # 5 or 6, depending on int width
IntMask = 1 shl IntShift - 1 IntMask = 1 shl IntShift - 1
type type
@ -34,15 +34,16 @@ type
TTrunk {.final.} = object TTrunk {.final.} = object
next: PTrunk # all nodes are connected with this pointer next: PTrunk # all nodes are connected with this pointer
key: int # start address at bit 0 key: int # start address at bit 0
bits: array[0..IntsPerTrunk - 1, TBitScalar] # a bit vector bits: array[0..IntsPerTrunk - 1, BitScalar] # a bit vector
TTrunkSeq = seq[PTrunk] TTrunkSeq = seq[PTrunk]
TIntSet* {.final.} = object ## an efficient set of 'int' implemented as a IntSet* = object ## an efficient set of 'int' implemented as a sparse bit set
## sparse bit set
counter, max: int counter, max: int
head: PTrunk head: PTrunk
data: TTrunkSeq data: TTrunkSeq
{.deprecated: [TIntSet: IntSet].}
proc mustRehash(length, counter: int): bool {.inline.} = proc mustRehash(length, counter: int): bool {.inline.} =
assert(length > counter) assert(length > counter)
result = (length * 2 < counter * 3) or (length - counter < 4) result = (length * 2 < counter * 3) or (length - counter < 4)

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -15,52 +15,58 @@ when not defined(nimhygiene):
{.pragma: dirty.} {.pragma: dirty.}
type type
TDoublyLinkedNode* {.pure, DoublyLinkedNodeObj*[T] = object ## a node a doubly linked list consists of
final.}[T] = object ## a node a doubly linked list consists of next*, prev*: ref DoublyLinkedNodeObj[T]
next*, prev*: ref TDoublyLinkedNode[T]
value*: T value*: T
PDoublyLinkedNode*[T] = ref TDoublyLinkedNode[T] DoublyLinkedNode*[T] = ref DoublyLinkedNodeObj[T]
TSinglyLinkedNode* {.pure, SinglyLinkedNodeObj*[T] = object ## a node a singly linked list consists of
final.}[T] = object ## a node a singly linked list consists of next*: ref SinglyLinkedNodeObj[T]
next*: ref TSinglyLinkedNode[T]
value*: T value*: T
PSinglyLinkedNode*[T] = ref TSinglyLinkedNode[T] SinglyLinkedNode*[T] = ref SinglyLinkedNodeObj[T]
TSinglyLinkedList* {.pure, final.}[T] = object ## a singly linked list SinglyLinkedList*[T] = object ## a singly linked list
head*, tail*: PSinglyLinkedNode[T] head*, tail*: SinglyLinkedNode[T]
TDoublyLinkedList* {.pure, final.}[T] = object ## a doubly linked list DoublyLinkedList*[T] = object ## a doubly linked list
head*, tail*: PDoublyLinkedNode[T] head*, tail*: DoublyLinkedNode[T]
TSinglyLinkedRing* {.pure, final.}[T] = object ## a singly linked ring SinglyLinkedRing*[T] = object ## a singly linked ring
head*: PSinglyLinkedNode[T] head*: SinglyLinkedNode[T]
TDoublyLinkedRing* {.pure, final.}[T] = object ## a doubly linked ring DoublyLinkedRing*[T] = object ## a doubly linked ring
head*: PDoublyLinkedNode[T] head*: DoublyLinkedNode[T]
proc initSinglyLinkedList*[T](): TSinglyLinkedList[T] = {.deprecated: [TDoublyLinkedNode: DoublyLinkedNodeObj,
PDoublyLinkedNode: DoublyLinkedNode,
TSinglyLinkedNode: SinglyLinkedNodeObj,
PSinglyLinkedNode: SinglyLinkedNode,
TDoublyLinkedList: DoublyLinkedList,
TSinglyLinkedRing: SinglyLinkedRing,
TDoublyLinkedRing: DoublyLinkedRing].}
proc initSinglyLinkedList*[T](): SinglyLinkedList[T] =
## creates a new singly linked list that is empty. ## creates a new singly linked list that is empty.
discard discard
proc initDoublyLinkedList*[T](): TDoublyLinkedList[T] = proc initDoublyLinkedList*[T](): DoublyLinkedList[T] =
## creates a new doubly linked list that is empty. ## creates a new doubly linked list that is empty.
discard discard
proc initSinglyLinkedRing*[T](): TSinglyLinkedRing[T] = proc initSinglyLinkedRing*[T](): SinglyLinkedRing[T] =
## creates a new singly linked ring that is empty. ## creates a new singly linked ring that is empty.
discard discard
proc initDoublyLinkedRing*[T](): TDoublyLinkedRing[T] = proc initDoublyLinkedRing*[T](): DoublyLinkedRing[T] =
## creates a new doubly linked ring that is empty. ## creates a new doubly linked ring that is empty.
discard discard
proc newDoublyLinkedNode*[T](value: T): PDoublyLinkedNode[T] = proc newDoublyLinkedNode*[T](value: T): DoublyLinkedNode[T] =
## creates a new doubly linked node with the given `value`. ## creates a new doubly linked node with the given `value`.
new(result) new(result)
result.value = value result.value = value
proc newSinglyLinkedNode*[T](value: T): PSinglyLinkedNode[T] = proc newSinglyLinkedNode*[T](value: T): SinglyLinkedNode[T] =
## creates a new singly linked node with the given `value`. ## creates a new singly linked node with the given `value`.
new(result) new(result)
result.value = value result.value = value
@ -99,38 +105,38 @@ template findImpl() {.dirty.} =
for x in nodes(L): for x in nodes(L):
if x.value == value: return x if x.value == value: return x
iterator items*[T](L: TDoublyLinkedList[T]): T = iterator items*[T](L: DoublyLinkedList[T]): T =
## yields every value of `L`. ## yields every value of `L`.
itemsListImpl() itemsListImpl()
iterator items*[T](L: TSinglyLinkedList[T]): T = iterator items*[T](L: SinglyLinkedList[T]): T =
## yields every value of `L`. ## yields every value of `L`.
itemsListImpl() itemsListImpl()
iterator items*[T](L: TSinglyLinkedRing[T]): T = iterator items*[T](L: SinglyLinkedRing[T]): T =
## yields every value of `L`. ## yields every value of `L`.
itemsRingImpl() itemsRingImpl()
iterator items*[T](L: TDoublyLinkedRing[T]): T = iterator items*[T](L: DoublyLinkedRing[T]): T =
## yields every value of `L`. ## yields every value of `L`.
itemsRingImpl() itemsRingImpl()
iterator nodes*[T](L: TSinglyLinkedList[T]): PSinglyLinkedNode[T] = iterator nodes*[T](L: SinglyLinkedList[T]): SinglyLinkedNode[T] =
## iterates over every node of `x`. Removing the current node from the ## iterates over every node of `x`. Removing the current node from the
## list during traversal is supported. ## list during traversal is supported.
nodesListImpl() nodesListImpl()
iterator nodes*[T](L: TDoublyLinkedList[T]): PDoublyLinkedNode[T] = iterator nodes*[T](L: DoublyLinkedList[T]): DoublyLinkedNode[T] =
## iterates over every node of `x`. Removing the current node from the ## iterates over every node of `x`. Removing the current node from the
## list during traversal is supported. ## list during traversal is supported.
nodesListImpl() nodesListImpl()
iterator nodes*[T](L: TSinglyLinkedRing[T]): PSinglyLinkedNode[T] = iterator nodes*[T](L: SinglyLinkedRing[T]): SinglyLinkedNode[T] =
## iterates over every node of `x`. Removing the current node from the ## iterates over every node of `x`. Removing the current node from the
## list during traversal is supported. ## list during traversal is supported.
nodesRingImpl() nodesRingImpl()
iterator nodes*[T](L: TDoublyLinkedRing[T]): PDoublyLinkedNode[T] = iterator nodes*[T](L: DoublyLinkedRing[T]): DoublyLinkedNode[T] =
## iterates over every node of `x`. Removing the current node from the ## iterates over every node of `x`. Removing the current node from the
## list during traversal is supported. ## list during traversal is supported.
nodesRingImpl() nodesRingImpl()
@ -142,33 +148,33 @@ template dollarImpl() {.dirty.} =
result.add($x.value) result.add($x.value)
result.add("]") result.add("]")
proc `$`*[T](L: TSinglyLinkedList[T]): string = proc `$`*[T](L: SinglyLinkedList[T]): string =
## turns a list into its string representation. ## turns a list into its string representation.
dollarImpl() dollarImpl()
proc `$`*[T](L: TDoublyLinkedList[T]): string = proc `$`*[T](L: DoublyLinkedList[T]): string =
## turns a list into its string representation. ## turns a list into its string representation.
dollarImpl() dollarImpl()
proc `$`*[T](L: TSinglyLinkedRing[T]): string = proc `$`*[T](L: SinglyLinkedRing[T]): string =
## turns a list into its string representation. ## turns a list into its string representation.
dollarImpl() dollarImpl()
proc `$`*[T](L: TDoublyLinkedRing[T]): string = proc `$`*[T](L: DoublyLinkedRing[T]): string =
## turns a list into its string representation. ## turns a list into its string representation.
dollarImpl() dollarImpl()
proc find*[T](L: TSinglyLinkedList[T], value: T): PSinglyLinkedNode[T] = proc find*[T](L: SinglyLinkedList[T], value: T): SinglyLinkedNode[T] =
## searches in the list for a value. Returns nil if the value does not ## searches in the list for a value. Returns nil if the value does not
## exist. ## exist.
findImpl() findImpl()
proc find*[T](L: TDoublyLinkedList[T], value: T): PDoublyLinkedNode[T] = proc find*[T](L: DoublyLinkedList[T], value: T): DoublyLinkedNode[T] =
## searches in the list for a value. Returns nil if the value does not ## searches in the list for a value. Returns nil if the value does not
## exist. ## exist.
findImpl() findImpl()
proc find*[T](L: TSinglyLinkedRing[T], value: T): PSinglyLinkedNode[T] = proc find*[T](L: SinglyLinkedRing[T], value: T): SinglyLinkedNode[T] =
## searches in the list for a value. Returns nil if the value does not ## searches in the list for a value. Returns nil if the value does not
## exist. ## exist.
findImpl() findImpl()
@ -300,5 +306,3 @@ proc remove*[T](L: var TDoublyLinkedRing[T], n: PDoublyLinkedNode[T]) =
L.head = nil L.head = nil
else: else:
L.head = L.head.prev L.head = L.head.prev

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -12,13 +12,15 @@
import math import math
type type
TQueue* {.pure, final.}[T] = object ## a queue Queue*[T] = object ## a queue
data: seq[T] data: seq[T]
rd, wr, count, mask: int rd, wr, count, mask: int
{.deprecated: [TQueue: Queue].}
proc initQueue*[T](initialSize=4): TQueue[T] = proc initQueue*[T](initialSize=4): TQueue[T] =
## creates a new queue. `initialSize` needs to be a power of 2. ## creates a new queue. `initialSize` needs to be a power of 2.
assert IsPowerOfTwo(initialSize) assert isPowerOfTwo(initialSize)
result.mask = initialSize-1 result.mask = initialSize-1
newSeq(result.data, initialSize) newSeq(result.data, initialSize)

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2011 Alex Mitchell # (c) Copyright 2011 Alex Mitchell
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -31,7 +31,7 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
## ##
## Example: ## Example:
## ##
## .. code-block:: nimrod ## .. code-block::
## let ## let
## s1 = @[1, 2, 3] ## s1 = @[1, 2, 3]
## s2 = @[4, 5] ## s2 = @[4, 5]
@ -50,7 +50,7 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
proc deduplicate*[T](seq1: seq[T]): seq[T] = proc deduplicate*[T](seq1: seq[T]): seq[T] =
## Returns a new sequence without duplicates. ## Returns a new sequence without duplicates.
## ##
## .. code-block:: nimrod ## .. code-block::
## let ## let
## dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4] ## dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4]
## dup2 = @["a", "a", "c", "d", "d"] ## dup2 = @["a", "a", "c", "d", "d"]
@ -69,7 +69,7 @@ proc zip*[S, T](seq1: seq[S], seq2: seq[T]): seq[tuple[a: S, b: T]] =
## fields `a` and `b`. If one sequence is shorter, the remaining items in the ## fields `a` and `b`. If one sequence is shorter, the remaining items in the
## longer sequence are discarded. Example: ## longer sequence are discarded. Example:
## ##
## .. code-block:: nimrod ## .. code-block::
## let ## let
## short = @[1, 2, 3] ## short = @[1, 2, 3]
## long = @[6, 5, 4, 3, 2, 1] ## long = @[6, 5, 4, 3, 2, 1]
@ -104,7 +104,7 @@ proc distribute*[T](s: seq[T], num: int, spread = true): seq[seq[T]] =
## ##
## Example: ## Example:
## ##
## .. code-block:: nimrod ## .. code-block::
## let numbers = @[1, 2, 3, 4, 5, 6, 7] ## let numbers = @[1, 2, 3, 4, 5, 6, 7]
## assert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]] ## assert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
## assert numbers.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]] ## assert numbers.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]]
@ -155,7 +155,7 @@ iterator filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): T =
## ##
## Example: ## Example:
## ##
## .. code-block:: nimrod ## .. code-block::
## let numbers = @[1, 4, 5, 8, 9, 7, 4] ## let numbers = @[1, 4, 5, 8, 9, 7, 4]
## for n in filter(numbers, proc (x: int): bool = x mod 2 == 0): ## for n in filter(numbers, proc (x: int): bool = x mod 2 == 0):
## echo($n) ## echo($n)
@ -169,7 +169,7 @@ proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T] =
## ##
## Example: ## Example:
## ##
## .. code-block:: nimrod ## .. code-block::
## let ## let
## colors = @["red", "yellow", "black"] ## colors = @["red", "yellow", "black"]
## f1 = filter(colors, proc(x: string): bool = x.len < 6) ## f1 = filter(colors, proc(x: string): bool = x.len < 6)
@ -184,7 +184,7 @@ proc keepIf*[T](seq1: var seq[T], pred: proc(item: T): bool {.closure.}) =
## ##
## Example: ## Example:
## ##
## .. code-block:: nimrod ## .. code-block::
## var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1] ## var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1]
## keepIf(floats, proc(x: float): bool = x > 10) ## keepIf(floats, proc(x: float): bool = x > 10)
## assert floats == @[13.0, 12.5, 10.1] ## assert floats == @[13.0, 12.5, 10.1]
@ -202,7 +202,7 @@ proc delete*[T](s: var seq[T], first=0, last=0) =
## ##
## Example: ## Example:
## ##
##.. code-block:: nimrod ##.. code-block::
## let outcome = @[1,1,1,1,1,1,1,1] ## let outcome = @[1,1,1,1,1,1,1,1]
## var dest = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1] ## var dest = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
## dest.delete(3, 8) ## dest.delete(3, 8)
@ -223,7 +223,7 @@ proc insert*[T](dest: var seq[T], src: openArray[T], pos=0) =
## ##
## Example: ## Example:
## ##
##.. code-block:: nimrod ##.. code-block::
## var dest = @[1,1,1,1,1,1,1,1] ## var dest = @[1,1,1,1,1,1,1,1]
## let ## let
## src = @[2,2,2,2,2,2] ## src = @[2,2,2,2,2,2]
@ -254,7 +254,7 @@ template filterIt*(seq1, pred: expr): expr {.immediate.} =
## the ``it`` variable for testing, like: ``filterIt("abcxyz", it == 'x')``. ## the ``it`` variable for testing, like: ``filterIt("abcxyz", it == 'x')``.
## Example: ## Example:
## ##
## .. code-block:: nimrod ## .. code-block::
## let ## let
## temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44] ## temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44]
## acceptable = filterIt(temperatures, it < 50 and it > -10) ## acceptable = filterIt(temperatures, it < 50 and it > -10)
@ -273,7 +273,7 @@ template keepItIf*(varSeq, pred: expr) =
## the ``it`` variable for testing, like: ``keepItIf("abcxyz", it == 'x')``. ## the ``it`` variable for testing, like: ``keepItIf("abcxyz", it == 'x')``.
## Example: ## Example:
## ##
## .. code-block:: nimrod ## .. code-block::
## var candidates = @["foo", "bar", "baz", "foobar"] ## var candidates = @["foo", "bar", "baz", "foobar"]
## keepItIf(candidates, it.len == 3 and it[0] == 'b') ## keepItIf(candidates, it.len == 3 and it[0] == 'b')
## assert candidates == @["bar", "baz"] ## assert candidates == @["bar", "baz"]
@ -292,7 +292,7 @@ template toSeq*(iter: expr): expr {.immediate.} =
## ##
## Example: ## Example:
## ##
## .. code-block:: nimrod ## .. code-block::
## let ## let
## numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] ## numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
## odd_numbers = toSeq(filter(numeric) do (x: int) -> bool: ## odd_numbers = toSeq(filter(numeric) do (x: int) -> bool:
@ -318,18 +318,18 @@ template foldl*(sequence, operation: expr): expr =
## the sequence of numbers 1, 2 and 3 will be parenthesized as (((1) - 2) - ## the sequence of numbers 1, 2 and 3 will be parenthesized as (((1) - 2) -
## 3). Example: ## 3). Example:
## ##
## .. code-block:: nimrod ## .. code-block::
## let ## let
## numbers = @[5, 9, 11] ## numbers = @[5, 9, 11]
## addition = foldl(numbers, a + b) ## addition = foldl(numbers, a + b)
## substraction = foldl(numbers, a - b) ## substraction = foldl(numbers, a - b)
## multiplication = foldl(numbers, a * b) ## multiplication = foldl(numbers, a * b)
## words = @["nim", "rod", "is", "cool"] ## words = @["nim", "is", "cool"]
## concatenation = foldl(words, a & b) ## concatenation = foldl(words, a & b)
## assert addition == 25, "Addition is (((5)+9)+11)" ## assert addition == 25, "Addition is (((5)+9)+11)"
## assert substraction == -15, "Substraction is (((5)-9)-11)" ## assert substraction == -15, "Substraction is (((5)-9)-11)"
## assert multiplication == 495, "Multiplication is (((5)*9)*11)" ## assert multiplication == 495, "Multiplication is (((5)*9)*11)"
## assert concatenation == "nimrodiscool" ## assert concatenation == "nimiscool"
assert sequence.len > 0, "Can't fold empty sequences" assert sequence.len > 0, "Can't fold empty sequences"
var result {.gensym.}: type(sequence[0]) var result {.gensym.}: type(sequence[0])
result = sequence[0] result = sequence[0]
@ -354,18 +354,18 @@ template foldr*(sequence, operation: expr): expr =
## the sequence of numbers 1, 2 and 3 will be parenthesized as (1 - (2 - ## the sequence of numbers 1, 2 and 3 will be parenthesized as (1 - (2 -
## (3))). Example: ## (3))). Example:
## ##
## .. code-block:: nimrod ## .. code-block::
## let ## let
## numbers = @[5, 9, 11] ## numbers = @[5, 9, 11]
## addition = foldr(numbers, a + b) ## addition = foldr(numbers, a + b)
## substraction = foldr(numbers, a - b) ## substraction = foldr(numbers, a - b)
## multiplication = foldr(numbers, a * b) ## multiplication = foldr(numbers, a * b)
## words = @["nim", "rod", "is", "cool"] ## words = @["nim", "is", "cool"]
## concatenation = foldr(words, a & b) ## concatenation = foldr(words, a & b)
## assert addition == 25, "Addition is (5+(9+(11)))" ## assert addition == 25, "Addition is (5+(9+(11)))"
## assert substraction == 7, "Substraction is (5-(9-(11)))" ## assert substraction == 7, "Substraction is (5-(9-(11)))"
## assert multiplication == 495, "Multiplication is (5*(9*(11)))" ## assert multiplication == 495, "Multiplication is (5*(9*(11)))"
## assert concatenation == "nimrodiscool" ## assert concatenation == "nimiscool"
assert sequence.len > 0, "Can't fold empty sequences" assert sequence.len > 0, "Can't fold empty sequences"
var result {.gensym.}: type(sequence[0]) var result {.gensym.}: type(sequence[0])
result = sequence[sequence.len - 1] result = sequence[sequence.len - 1]
@ -384,7 +384,7 @@ template mapIt*(seq1, typ, pred: expr): expr =
## since the new returned sequence can have a different type than the ## since the new returned sequence can have a different type than the
## original. Example: ## original. Example:
## ##
## .. code-block:: nimrod ## .. code-block::
## let ## let
## nums = @[1, 2, 3, 4] ## nums = @[1, 2, 3, 4]
## strings = nums.mapIt(string, $(4 * it)) ## strings = nums.mapIt(string, $(4 * it))
@ -401,7 +401,7 @@ template mapIt*(varSeq, pred: expr) =
## expression. The expression has to return the same type as the sequence you ## expression. The expression has to return the same type as the sequence you
## are mutating. Example: ## are mutating. Example:
## ##
## .. code-block:: nimrod ## .. code-block::
## var nums = @[1, 2, 3, 4] ## var nums = @[1, 2, 3, 4]
## nums.mapIt(it * 3) ## nums.mapIt(it * 3)
## assert nums[0] + nums[3] == 15 ## assert nums[0] + nums[3] == 15
@ -412,7 +412,7 @@ template mapIt*(varSeq, pred: expr) =
template newSeqWith*(len: int, init: expr): expr = template newSeqWith*(len: int, init: expr): expr =
## creates a new sequence, calling `init` to initialize each value. Example: ## creates a new sequence, calling `init` to initialize each value. Example:
## ##
## .. code-block:: nimrod ## .. code-block::
## var seq2D = newSeqWith(20, newSeq[bool](10)) ## var seq2D = newSeqWith(20, newSeq[bool](10))
## seq2D[0][0] = true ## seq2D[0][0] = true
## seq2D[1][0] = true ## seq2D[1][0] = true
@ -503,12 +503,12 @@ when isMainModule:
addition = foldl(numbers, a + b) addition = foldl(numbers, a + b)
substraction = foldl(numbers, a - b) substraction = foldl(numbers, a - b)
multiplication = foldl(numbers, a * b) multiplication = foldl(numbers, a * b)
words = @["nim", "rod", "is", "cool"] words = @["nim", "is", "cool"]
concatenation = foldl(words, a & b) concatenation = foldl(words, a & b)
assert addition == 25, "Addition is (((5)+9)+11)" assert addition == 25, "Addition is (((5)+9)+11)"
assert substraction == -15, "Substraction is (((5)-9)-11)" assert substraction == -15, "Substraction is (((5)-9)-11)"
assert multiplication == 495, "Multiplication is (((5)*9)*11)" assert multiplication == 495, "Multiplication is (((5)*9)*11)"
assert concatenation == "nimrodiscool" assert concatenation == "nimiscool"
block: # foldr tests block: # foldr tests
let let
@ -516,12 +516,12 @@ when isMainModule:
addition = foldr(numbers, a + b) addition = foldr(numbers, a + b)
substraction = foldr(numbers, a - b) substraction = foldr(numbers, a - b)
multiplication = foldr(numbers, a * b) multiplication = foldr(numbers, a * b)
words = @["nim", "rod", "is", "cool"] words = @["nim", "is", "cool"]
concatenation = foldr(words, a & b) concatenation = foldr(words, a & b)
assert addition == 25, "Addition is (5+(9+(11)))" assert addition == 25, "Addition is (5+(9+(11)))"
assert substraction == 7, "Substraction is (5-(9-(11)))" assert substraction == 7, "Substraction is (5-(9-(11)))"
assert multiplication == 495, "Multiplication is (5*(9*(11)))" assert multiplication == 495, "Multiplication is (5*(9*(11)))"
assert concatenation == "nimrodiscool" assert concatenation == "nimiscool"
block: # delete tests block: # delete tests
let outcome = @[1,1,1,1,1,1,1,1] let outcome = @[1,1,1,1,1,1,1,1]

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -24,17 +24,19 @@ when not defined(nimhygiene):
{.pragma: dirty.} {.pragma: dirty.}
type type
TSlotEnum = enum seEmpty, seFilled, seDeleted SlotEnum = enum seEmpty, seFilled, seDeleted
TKeyValuePair[A] = tuple[slot: TSlotEnum, key: A] KeyValuePair[A] = tuple[slot: SlotEnum, key: A]
TKeyValuePairSeq[A] = seq[TKeyValuePair[A]] KeyValuePairSeq[A] = seq[KeyValuePair[A]]
TSet* {.final, myShallow.}[A] = object ## \ HashSet* {.myShallow.}[A] = object ## \
## A generic hash set. ## A generic hash set.
## ##
## Use `init() <#init,TSet[A],int>`_ or `initSet[type]() <#initSet>`_ ## Use `init() <#init,HashSet[A],int>`_ or `initSet[type]() <#initSet>`_
## before calling other procs on it. ## before calling other procs on it.
data: TKeyValuePairSeq[A] data: KeyValuePairSeq[A]
counter: int counter: int
{.deprecated: [TSet: HashSet].}
proc isValid*[A](s: TSet[A]): bool = proc isValid*[A](s: TSet[A]): bool =
## Returns `true` if the set has been initialized with `initSet <#initSet>`_. ## Returns `true` if the set has been initialized with `initSet <#initSet>`_.
## ##
@ -43,7 +45,7 @@ proc isValid*[A](s: TSet[A]): bool =
## your own procs to verify that sets passed to your procs are correctly ## your own procs to verify that sets passed to your procs are correctly
## initialized. Example: ## initialized. Example:
## ##
## .. code-block :: nimrod ## .. code-block ::
## proc savePreferences(options: TSet[string]) = ## proc savePreferences(options: TSet[string]) =
## assert options.isValid, "Pass an initialized set!" ## assert options.isValid, "Pass an initialized set!"
## # Do stuff here, may crash in release builds! ## # Do stuff here, may crash in release builds!
@ -490,19 +492,20 @@ proc map*[A, B](data: TSet[A], op: proc (x: A): B {.closure.}): TSet[B] =
# ------------------------------ ordered set ------------------------------ # ------------------------------ ordered set ------------------------------
type type
TOrderedKeyValuePair[A] = tuple[ OrderedKeyValuePair[A] = tuple[
slot: TSlotEnum, next: int, key: A] slot: SlotEnum, next: int, key: A]
TOrderedKeyValuePairSeq[A] = seq[TOrderedKeyValuePair[A]] OrderedKeyValuePairSeq[A] = seq[OrderedKeyValuePair[A]]
TOrderedSet* {. OrderedSet* {.myShallow.}[A] = object ## \
final, myShallow.}[A] = object ## \
## A generic hash set that remembers insertion order. ## A generic hash set that remembers insertion order.
## ##
## Use `init() <#init,TOrderedSet[A],int>`_ or `initOrderedSet[type]() ## Use `init() <#init,OrderedSet[A],int>`_ or `initOrderedSet[type]()
## <#initOrderedSet>`_ before calling other procs on it. ## <#initOrderedSet>`_ before calling other procs on it.
data: TOrderedKeyValuePairSeq[A] data: TOrderedKeyValuePairSeq[A]
counter, first, last: int counter, first, last: int
proc isValid*[A](s: TOrderedSet[A]): bool = {.deprecated: [TOrderedSet: OrderedSet].}
proc isValid*[A](s: OrderedSet[A]): bool =
## Returns `true` if the ordered set has been initialized with `initSet ## Returns `true` if the ordered set has been initialized with `initSet
## <#initOrderedSet>`_. ## <#initOrderedSet>`_.
## ##
@ -511,13 +514,13 @@ proc isValid*[A](s: TOrderedSet[A]): bool =
## in your own procs to verify that ordered sets passed to your procs are ## in your own procs to verify that ordered sets passed to your procs are
## correctly initialized. Example: ## correctly initialized. Example:
## ##
## .. code-block :: nimrod ## .. code-block::
## proc saveTarotCards(cards: TOrderedSet[int]) = ## proc saveTarotCards(cards: TOrderedSet[int]) =
## assert cards.isValid, "Pass an initialized set!" ## assert cards.isValid, "Pass an initialized set!"
## # Do stuff here, may crash in release builds! ## # Do stuff here, may crash in release builds!
result = not s.data.isNil result = not s.data.isNil
proc len*[A](s: TOrderedSet[A]): int {.inline.} = proc len*[A](s: OrderedSet[A]): int {.inline.} =
## Returns the number of keys in `s`. ## Returns the number of keys in `s`.
## ##
## Due to an implementation detail you can call this proc on variables which ## Due to an implementation detail you can call this proc on variables which
@ -734,14 +737,14 @@ proc `==`*[A](s, t: TOrderedSet[A]): bool =
proc testModule() = proc testModule() =
## Internal micro test to validate docstrings and such. ## Internal micro test to validate docstrings and such.
block isValidTest: block isValidTest:
var options: TSet[string] var options: HashSet[string]
proc savePreferences(options: TSet[string]) = proc savePreferences(options: TSet[string]) =
assert options.isValid, "Pass an initialized set!" assert options.isValid, "Pass an initialized set!"
options = initSet[string]() options = initSet[string]()
options.savePreferences options.savePreferences
block lenTest: block lenTest:
var values: TSet[int] var values: HashSet[int]
assert(not values.isValid) assert(not values.isValid)
assert values.len == 0 assert values.len == 0
assert values.card == 0 assert values.card == 0
@ -835,14 +838,14 @@ proc testModule() =
assert b == toSet(["1", "2", "3"]) assert b == toSet(["1", "2", "3"])
block isValidTest: block isValidTest:
var cards: TOrderedSet[string] var cards: OrderedSet[string]
proc saveTarotCards(cards: TOrderedSet[string]) = proc saveTarotCards(cards: TOrderedSet[string]) =
assert cards.isValid, "Pass an initialized set!" assert cards.isValid, "Pass an initialized set!"
cards = initOrderedSet[string]() cards = initOrderedSet[string]()
cards.saveTarotCards cards.saveTarotCards
block lenTest: block lenTest:
var values: TOrderedSet[int] var values: OrderedSet[int]
assert(not values.isValid) assert(not values.isValid)
assert values.len == 0 assert values.len == 0
assert values.card == 0 assert values.card == 0
@ -879,7 +882,7 @@ proc testModule() =
assert(a == b) # https://github.com/Araq/Nimrod/issues/1413 assert(a == b) # https://github.com/Araq/Nimrod/issues/1413
block initBlocks: block initBlocks:
var a: TOrderedSet[int] var a: OrderedSet[int]
a.init(4) a.init(4)
a.incl(2) a.incl(2)
a.init a.init

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2013 Andreas Rumpf # (c) Copyright 2013 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -28,7 +28,7 @@
## you add such a proc for your custom type everything will work. See this ## you add such a proc for your custom type everything will work. See this
## example: ## example:
## ##
## .. code-block:: nimrod ## .. code-block::
## type ## type
## Person = object ## Person = object
## firstName, lastName: string ## firstName, lastName: string
@ -61,13 +61,15 @@ import
{.pragma: myShallow.} {.pragma: myShallow.}
type type
TSlotEnum = enum seEmpty, seFilled, seDeleted SlotEnum = enum seEmpty, seFilled, seDeleted
TKeyValuePair[A, B] = tuple[slot: TSlotEnum, key: A, val: B] KeyValuePair[A, B] = tuple[slot: SlotEnum, key: A, val: B]
TKeyValuePairSeq[A, B] = seq[TKeyValuePair[A, B]] KeyValuePairSeq[A, B] = seq[KeyValuePair[A, B]]
TTable* {.final, myShallow.}[A, B] = object ## generic hash table Table* {.myShallow.}[A, B] = object ## generic hash table
data: TKeyValuePairSeq[A, B] data: KeyValuePairSeq[A, B]
counter: int counter: int
PTable*[A,B] = ref TTable[A, B] TableRef*[A,B] = ref Table[A, B]
{.deprecated: [TTable: Table, PTable: TableRef].}
when not defined(nimhygiene): when not defined(nimhygiene):
{.pragma: dirty.} {.pragma: dirty.}
@ -158,12 +160,12 @@ proc hasKey*[A, B](t: TTable[A, B], key: A): bool =
## returns true iff `key` is in the table `t`. ## returns true iff `key` is in the table `t`.
result = rawGet(t, key) >= 0 result = rawGet(t, key) >= 0
proc rawInsert[A, B](t: var TTable[A, B], data: var TKeyValuePairSeq[A, B], proc rawInsert[A, B](t: var TTable[A, B], data: var KeyValuePairSeq[A, B],
key: A, val: B) = key: A, val: B) =
rawInsertImpl() rawInsertImpl()
proc enlarge[A, B](t: var TTable[A, B]) = proc enlarge[A, B](t: var TTable[A, B]) =
var n: TKeyValuePairSeq[A, B] var n: KeyValuePairSeq[A, B]
newSeq(n, len(t.data) * growthFactor) newSeq(n, len(t.data) * growthFactor)
for i in countup(0, high(t.data)): for i in countup(0, high(t.data)):
if t.data[i].slot == seFilled: rawInsert(t, n, t.data[i].key, t.data[i].val) if t.data[i].slot == seFilled: rawInsert(t, n, t.data[i].key, t.data[i].val)
@ -347,14 +349,16 @@ proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): PTable[C, B] =
# ------------------------------ ordered table ------------------------------ # ------------------------------ ordered table ------------------------------
type type
TOrderedKeyValuePair[A, B] = tuple[ OrderedKeyValuePair[A, B] = tuple[
slot: TSlotEnum, next: int, key: A, val: B] slot: SlotEnum, next: int, key: A, val: B]
TOrderedKeyValuePairSeq[A, B] = seq[TOrderedKeyValuePair[A, B]] OrderedKeyValuePairSeq[A, B] = seq[OrderedKeyValuePair[A, B]]
TOrderedTable* {. OrderedTable* {.
final, myShallow.}[A, B] = object ## table that remembers insertion order myShallow.}[A, B] = object ## table that remembers insertion order
data: TOrderedKeyValuePairSeq[A, B] data: OrderedKeyValuePairSeq[A, B]
counter, first, last: int counter, first, last: int
POrderedTable*[A, B] = ref TOrderedTable[A, B] OrderedTableRef*[A, B] = ref OrderedTable[A, B]
{.deprecated: [TOrderedTable: OrderedTable, POrderedTable: OrderedTableRef].}
proc len*[A, B](t: TOrderedTable[A, B]): int {.inline.} = proc len*[A, B](t: TOrderedTable[A, B]): int {.inline.} =
## returns the number of keys in `t`. ## returns the number of keys in `t`.
@ -608,11 +612,13 @@ proc sort*[A, B](t: POrderedTable[A, B],
# ------------------------------ count tables ------------------------------- # ------------------------------ count tables -------------------------------
type type
TCountTable* {.final, myShallow.}[ CountTable* {.myShallow.}[
A] = object ## table that counts the number of each key A] = object ## table that counts the number of each key
data: seq[tuple[key: A, val: int]] data: seq[tuple[key: A, val: int]]
counter: int counter: int
PCountTable*[A] = ref TCountTable[A] CountTableRef*[A] = ref CountTable[A]
{.deprecated: [TCountTable: CountTable, PCountTable: CountTableRef].}
proc len*[A](t: TCountTable[A]): int = proc len*[A](t: TCountTable[A]): int =
## returns the number of keys in `t`. ## returns the number of keys in `t`.

View file

@ -1,5 +1,5 @@
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2010 Andreas Rumpf # (c) Copyright 2010 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -12,18 +12,20 @@
import strutils import strutils
type type
TColor* = distinct int ## a color stored as RGB Color* = distinct int ## a color stored as RGB
proc `==` *(a, b: TColor): bool {.borrow.} {.deprecated: [TColor: Color].}
proc `==` *(a, b: Color): bool {.borrow.}
## compares two colors. ## compares two colors.
template extract(a: TColor, r, g, b: expr) {.immediate.}= template extract(a: Color, r, g, b: expr) {.immediate.}=
var r = a.int shr 16 and 0xff var r = a.int shr 16 and 0xff
var g = a.int shr 8 and 0xff var g = a.int shr 8 and 0xff
var b = a.int and 0xff var b = a.int and 0xff
template rawRGB(r, g, b: int): expr = template rawRGB(r, g, b: int): expr =
TColor(r shl 16 or g shl 8 or b) Color(r shl 16 or g shl 8 or b)
template colorOp(op: expr) {.immediate.} = template colorOp(op: expr) {.immediate.} =
extract(a, ar, ag, ab) extract(a, ar, ag, ab)
@ -38,23 +40,23 @@ proc satMinus(a, b: int): int {.inline.} =
result = a -% b result = a -% b
if result < 0: result = 0 if result < 0: result = 0
proc `+`*(a, b: TColor): TColor = proc `+`*(a, b: Color): Color =
## adds two colors: This uses saturated artithmetic, so that each color ## adds two colors: This uses saturated artithmetic, so that each color
## component cannot overflow (255 is used as a maximum). ## component cannot overflow (255 is used as a maximum).
colorOp(satPlus) colorOp(satPlus)
proc `-`*(a, b: TColor): TColor = proc `-`*(a, b: Color): Color =
## substracts two colors: This uses saturated artithmetic, so that each color ## substracts two colors: This uses saturated artithmetic, so that each color
## component cannot overflow (255 is used as a maximum). ## component cannot overflow (255 is used as a maximum).
colorOp(satMinus) colorOp(satMinus)
proc extractRGB*(a: TColor): tuple[r, g, b: range[0..255]] = proc extractRGB*(a: Color): tuple[r, g, b: range[0..255]] =
## extracts the red/green/blue components of the color `a`. ## extracts the red/green/blue components of the color `a`.
result.r = a.int shr 16 and 0xff result.r = a.int shr 16 and 0xff
result.g = a.int shr 8 and 0xff result.g = a.int shr 8 and 0xff
result.b = a.int and 0xff result.b = a.int and 0xff
proc intensity*(a: TColor, f: float): TColor = proc intensity*(a: Color, f: float): Color =
## returns `a` with intensity `f`. `f` should be a float from 0.0 (completely ## returns `a` with intensity `f`. `f` should be a float from 0.0 (completely
## dark) to 1.0 (full color intensity). ## dark) to 1.0 (full color intensity).
var r = toInt(toFloat(a.int shr 16 and 0xff) * f) var r = toInt(toFloat(a.int shr 16 and 0xff) * f)
@ -65,7 +67,7 @@ proc intensity*(a: TColor, f: float): TColor =
if b >% 255: b = 255 if b >% 255: b = 255
result = rawRGB(r, g, b) result = rawRGB(r, g, b)
template mix*(a, b: TColor, fn: expr): expr = template mix*(a, b: Color, fn: expr): expr =
## uses `fn` to mix the colors `a` and `b`. `fn` is invoked for each component ## uses `fn` to mix the colors `a` and `b`. `fn` is invoked for each component
## R, G, and B. This is a template because `fn` should be inlined and the ## R, G, and B. This is a template because `fn` should be inlined and the
## compiler cannot inline proc pointers yet. If `fn`'s result is not in the ## compiler cannot inline proc pointers yet. If `fn`'s result is not in the
@ -84,146 +86,146 @@ template mix*(a, b: TColor, fn: expr): expr =
const const
colAliceBlue* = TColor(0xF0F8FF) colAliceBlue* = Color(0xF0F8FF)
colAntiqueWhite* = TColor(0xFAEBD7) colAntiqueWhite* = Color(0xFAEBD7)
colAqua* = TColor(0x00FFFF) colAqua* = Color(0x00FFFF)
colAquamarine* = TColor(0x7FFFD4) colAquamarine* = Color(0x7FFFD4)
colAzure* = TColor(0xF0FFFF) colAzure* = Color(0xF0FFFF)
colBeige* = TColor(0xF5F5DC) colBeige* = Color(0xF5F5DC)
colBisque* = TColor(0xFFE4C4) colBisque* = Color(0xFFE4C4)
colBlack* = TColor(0x000000) colBlack* = Color(0x000000)
colBlanchedAlmond* = TColor(0xFFEBCD) colBlanchedAlmond* = Color(0xFFEBCD)
colBlue* = TColor(0x0000FF) colBlue* = Color(0x0000FF)
colBlueViolet* = TColor(0x8A2BE2) colBlueViolet* = Color(0x8A2BE2)
colBrown* = TColor(0xA52A2A) colBrown* = Color(0xA52A2A)
colBurlyWood* = TColor(0xDEB887) colBurlyWood* = Color(0xDEB887)
colCadetBlue* = TColor(0x5F9EA0) colCadetBlue* = Color(0x5F9EA0)
colChartreuse* = TColor(0x7FFF00) colChartreuse* = Color(0x7FFF00)
colChocolate* = TColor(0xD2691E) colChocolate* = Color(0xD2691E)
colCoral* = TColor(0xFF7F50) colCoral* = Color(0xFF7F50)
colCornflowerBlue* = TColor(0x6495ED) colCornflowerBlue* = Color(0x6495ED)
colCornsilk* = TColor(0xFFF8DC) colCornsilk* = Color(0xFFF8DC)
colCrimson* = TColor(0xDC143C) colCrimson* = Color(0xDC143C)
colCyan* = TColor(0x00FFFF) colCyan* = Color(0x00FFFF)
colDarkBlue* = TColor(0x00008B) colDarkBlue* = Color(0x00008B)
colDarkCyan* = TColor(0x008B8B) colDarkCyan* = Color(0x008B8B)
colDarkGoldenRod* = TColor(0xB8860B) colDarkGoldenRod* = Color(0xB8860B)
colDarkGray* = TColor(0xA9A9A9) colDarkGray* = Color(0xA9A9A9)
colDarkGreen* = TColor(0x006400) colDarkGreen* = Color(0x006400)
colDarkKhaki* = TColor(0xBDB76B) colDarkKhaki* = Color(0xBDB76B)
colDarkMagenta* = TColor(0x8B008B) colDarkMagenta* = Color(0x8B008B)
colDarkOliveGreen* = TColor(0x556B2F) colDarkOliveGreen* = Color(0x556B2F)
colDarkorange* = TColor(0xFF8C00) colDarkorange* = Color(0xFF8C00)
colDarkOrchid* = TColor(0x9932CC) colDarkOrchid* = Color(0x9932CC)
colDarkRed* = TColor(0x8B0000) colDarkRed* = Color(0x8B0000)
colDarkSalmon* = TColor(0xE9967A) colDarkSalmon* = Color(0xE9967A)
colDarkSeaGreen* = TColor(0x8FBC8F) colDarkSeaGreen* = Color(0x8FBC8F)
colDarkSlateBlue* = TColor(0x483D8B) colDarkSlateBlue* = Color(0x483D8B)
colDarkSlateGray* = TColor(0x2F4F4F) colDarkSlateGray* = Color(0x2F4F4F)
colDarkTurquoise* = TColor(0x00CED1) colDarkTurquoise* = Color(0x00CED1)
colDarkViolet* = TColor(0x9400D3) colDarkViolet* = Color(0x9400D3)
colDeepPink* = TColor(0xFF1493) colDeepPink* = Color(0xFF1493)
colDeepSkyBlue* = TColor(0x00BFFF) colDeepSkyBlue* = Color(0x00BFFF)
colDimGray* = TColor(0x696969) colDimGray* = Color(0x696969)
colDodgerBlue* = TColor(0x1E90FF) colDodgerBlue* = Color(0x1E90FF)
colFireBrick* = TColor(0xB22222) colFireBrick* = Color(0xB22222)
colFloralWhite* = TColor(0xFFFAF0) colFloralWhite* = Color(0xFFFAF0)
colForestGreen* = TColor(0x228B22) colForestGreen* = Color(0x228B22)
colFuchsia* = TColor(0xFF00FF) colFuchsia* = Color(0xFF00FF)
colGainsboro* = TColor(0xDCDCDC) colGainsboro* = Color(0xDCDCDC)
colGhostWhite* = TColor(0xF8F8FF) colGhostWhite* = Color(0xF8F8FF)
colGold* = TColor(0xFFD700) colGold* = Color(0xFFD700)
colGoldenRod* = TColor(0xDAA520) colGoldenRod* = Color(0xDAA520)
colGray* = TColor(0x808080) colGray* = Color(0x808080)
colGreen* = TColor(0x008000) colGreen* = Color(0x008000)
colGreenYellow* = TColor(0xADFF2F) colGreenYellow* = Color(0xADFF2F)
colHoneyDew* = TColor(0xF0FFF0) colHoneyDew* = Color(0xF0FFF0)
colHotPink* = TColor(0xFF69B4) colHotPink* = Color(0xFF69B4)
colIndianRed* = TColor(0xCD5C5C) colIndianRed* = Color(0xCD5C5C)
colIndigo* = TColor(0x4B0082) colIndigo* = Color(0x4B0082)
colIvory* = TColor(0xFFFFF0) colIvory* = Color(0xFFFFF0)
colKhaki* = TColor(0xF0E68C) colKhaki* = Color(0xF0E68C)
colLavender* = TColor(0xE6E6FA) colLavender* = Color(0xE6E6FA)
colLavenderBlush* = TColor(0xFFF0F5) colLavenderBlush* = Color(0xFFF0F5)
colLawnGreen* = TColor(0x7CFC00) colLawnGreen* = Color(0x7CFC00)
colLemonChiffon* = TColor(0xFFFACD) colLemonChiffon* = Color(0xFFFACD)
colLightBlue* = TColor(0xADD8E6) colLightBlue* = Color(0xADD8E6)
colLightCoral* = TColor(0xF08080) colLightCoral* = Color(0xF08080)
colLightCyan* = TColor(0xE0FFFF) colLightCyan* = Color(0xE0FFFF)
colLightGoldenRodYellow* = TColor(0xFAFAD2) colLightGoldenRodYellow* = Color(0xFAFAD2)
colLightGrey* = TColor(0xD3D3D3) colLightGrey* = Color(0xD3D3D3)
colLightGreen* = TColor(0x90EE90) colLightGreen* = Color(0x90EE90)
colLightPink* = TColor(0xFFB6C1) colLightPink* = Color(0xFFB6C1)
colLightSalmon* = TColor(0xFFA07A) colLightSalmon* = Color(0xFFA07A)
colLightSeaGreen* = TColor(0x20B2AA) colLightSeaGreen* = Color(0x20B2AA)
colLightSkyBlue* = TColor(0x87CEFA) colLightSkyBlue* = Color(0x87CEFA)
colLightSlateGray* = TColor(0x778899) colLightSlateGray* = Color(0x778899)
colLightSteelBlue* = TColor(0xB0C4DE) colLightSteelBlue* = Color(0xB0C4DE)
colLightYellow* = TColor(0xFFFFE0) colLightYellow* = Color(0xFFFFE0)
colLime* = TColor(0x00FF00) colLime* = Color(0x00FF00)
colLimeGreen* = TColor(0x32CD32) colLimeGreen* = Color(0x32CD32)
colLinen* = TColor(0xFAF0E6) colLinen* = Color(0xFAF0E6)
colMagenta* = TColor(0xFF00FF) colMagenta* = Color(0xFF00FF)
colMaroon* = TColor(0x800000) colMaroon* = Color(0x800000)
colMediumAquaMarine* = TColor(0x66CDAA) colMediumAquaMarine* = Color(0x66CDAA)
colMediumBlue* = TColor(0x0000CD) colMediumBlue* = Color(0x0000CD)
colMediumOrchid* = TColor(0xBA55D3) colMediumOrchid* = Color(0xBA55D3)
colMediumPurple* = TColor(0x9370D8) colMediumPurple* = Color(0x9370D8)
colMediumSeaGreen* = TColor(0x3CB371) colMediumSeaGreen* = Color(0x3CB371)
colMediumSlateBlue* = TColor(0x7B68EE) colMediumSlateBlue* = Color(0x7B68EE)
colMediumSpringGreen* = TColor(0x00FA9A) colMediumSpringGreen* = Color(0x00FA9A)
colMediumTurquoise* = TColor(0x48D1CC) colMediumTurquoise* = Color(0x48D1CC)
colMediumVioletRed* = TColor(0xC71585) colMediumVioletRed* = Color(0xC71585)
colMidnightBlue* = TColor(0x191970) colMidnightBlue* = Color(0x191970)
colMintCream* = TColor(0xF5FFFA) colMintCream* = Color(0xF5FFFA)
colMistyRose* = TColor(0xFFE4E1) colMistyRose* = Color(0xFFE4E1)
colMoccasin* = TColor(0xFFE4B5) colMoccasin* = Color(0xFFE4B5)
colNavajoWhite* = TColor(0xFFDEAD) colNavajoWhite* = Color(0xFFDEAD)
colNavy* = TColor(0x000080) colNavy* = Color(0x000080)
colOldLace* = TColor(0xFDF5E6) colOldLace* = Color(0xFDF5E6)
colOlive* = TColor(0x808000) colOlive* = Color(0x808000)
colOliveDrab* = TColor(0x6B8E23) colOliveDrab* = Color(0x6B8E23)
colOrange* = TColor(0xFFA500) colOrange* = Color(0xFFA500)
colOrangeRed* = TColor(0xFF4500) colOrangeRed* = Color(0xFF4500)
colOrchid* = TColor(0xDA70D6) colOrchid* = Color(0xDA70D6)
colPaleGoldenRod* = TColor(0xEEE8AA) colPaleGoldenRod* = Color(0xEEE8AA)
colPaleGreen* = TColor(0x98FB98) colPaleGreen* = Color(0x98FB98)
colPaleTurquoise* = TColor(0xAFEEEE) colPaleTurquoise* = Color(0xAFEEEE)
colPaleVioletRed* = TColor(0xD87093) colPaleVioletRed* = Color(0xD87093)
colPapayaWhip* = TColor(0xFFEFD5) colPapayaWhip* = Color(0xFFEFD5)
colPeachPuff* = TColor(0xFFDAB9) colPeachPuff* = Color(0xFFDAB9)
colPeru* = TColor(0xCD853F) colPeru* = Color(0xCD853F)
colPink* = TColor(0xFFC0CB) colPink* = Color(0xFFC0CB)
colPlum* = TColor(0xDDA0DD) colPlum* = Color(0xDDA0DD)
colPowderBlue* = TColor(0xB0E0E6) colPowderBlue* = Color(0xB0E0E6)
colPurple* = TColor(0x800080) colPurple* = Color(0x800080)
colRed* = TColor(0xFF0000) colRed* = Color(0xFF0000)
colRosyBrown* = TColor(0xBC8F8F) colRosyBrown* = Color(0xBC8F8F)
colRoyalBlue* = TColor(0x4169E1) colRoyalBlue* = Color(0x4169E1)
colSaddleBrown* = TColor(0x8B4513) colSaddleBrown* = Color(0x8B4513)
colSalmon* = TColor(0xFA8072) colSalmon* = Color(0xFA8072)
colSandyBrown* = TColor(0xF4A460) colSandyBrown* = Color(0xF4A460)
colSeaGreen* = TColor(0x2E8B57) colSeaGreen* = Color(0x2E8B57)
colSeaShell* = TColor(0xFFF5EE) colSeaShell* = Color(0xFFF5EE)
colSienna* = TColor(0xA0522D) colSienna* = Color(0xA0522D)
colSilver* = TColor(0xC0C0C0) colSilver* = Color(0xC0C0C0)
colSkyBlue* = TColor(0x87CEEB) colSkyBlue* = Color(0x87CEEB)
colSlateBlue* = TColor(0x6A5ACD) colSlateBlue* = Color(0x6A5ACD)
colSlateGray* = TColor(0x708090) colSlateGray* = Color(0x708090)
colSnow* = TColor(0xFFFAFA) colSnow* = Color(0xFFFAFA)
colSpringGreen* = TColor(0x00FF7F) colSpringGreen* = Color(0x00FF7F)
colSteelBlue* = TColor(0x4682B4) colSteelBlue* = Color(0x4682B4)
colTan* = TColor(0xD2B48C) colTan* = Color(0xD2B48C)
colTeal* = TColor(0x008080) colTeal* = Color(0x008080)
colThistle* = TColor(0xD8BFD8) colThistle* = Color(0xD8BFD8)
colTomato* = TColor(0xFF6347) colTomato* = Color(0xFF6347)
colTurquoise* = TColor(0x40E0D0) colTurquoise* = Color(0x40E0D0)
colViolet* = TColor(0xEE82EE) colViolet* = Color(0xEE82EE)
colWheat* = TColor(0xF5DEB3) colWheat* = Color(0xF5DEB3)
colWhite* = TColor(0xFFFFFF) colWhite* = Color(0xFFFFFF)
colWhiteSmoke* = TColor(0xF5F5F5) colWhiteSmoke* = Color(0xF5F5F5)
colYellow* = TColor(0xFFFF00) colYellow* = Color(0xFFFF00)
colYellowGreen* = TColor(0x9ACD32) colYellowGreen* = Color(0x9ACD32)
colorNames = [ colorNames = [
("aliceblue", colAliceBlue), ("aliceblue", colAliceBlue),
@ -367,11 +369,11 @@ const
("yellow", colYellow), ("yellow", colYellow),
("yellowgreen", colYellowGreen)] ("yellowgreen", colYellowGreen)]
proc `$`*(c: TColor): string = proc `$`*(c: Color): string =
## converts a color into its textual representation. Example: ``#00FF00``. ## converts a color into its textual representation. Example: ``#00FF00``.
result = '#' & toHex(int(c), 6) result = '#' & toHex(int(c), 6)
proc binaryStrSearch(x: openarray[tuple[name: string, col: TColor]], proc binaryStrSearch(x: openArray[tuple[name: string, col: Color]],
y: string): int = y: string): int =
var a = 0 var a = 0
var b = len(x) - 1 var b = len(x) - 1
@ -383,14 +385,14 @@ proc binaryStrSearch(x: openarray[tuple[name: string, col: TColor]],
else: return mid else: return mid
result = - 1 result = - 1
proc parseColor*(name: string): TColor = proc parseColor*(name: string): Color =
## parses `name` to a color value. If no valid color could be ## parses `name` to a color value. If no valid color could be
## parsed ``EInvalidValue`` is raised. ## parsed ``EInvalidValue`` is raised.
if name[0] == '#': if name[0] == '#':
result = TColor(parseHexInt(name)) result = Color(parseHexInt(name))
else: else:
var idx = binaryStrSearch(colorNames, name) var idx = binaryStrSearch(colorNames, name)
if idx < 0: raise newException(EInvalidValue, "unkown color: " & name) if idx < 0: raise newException(ValueError, "unkown color: " & name)
result = colorNames[idx][1] result = colorNames[idx][1]
proc isColor*(name: string): bool = proc isColor*(name: string): bool =
@ -403,7 +405,7 @@ proc isColor*(name: string): bool =
else: else:
result = binaryStrSearch(colorNames, name) >= 0 result = binaryStrSearch(colorNames, name) >= 0
proc rgb*(r, g, b: range[0..255]): TColor = proc rgb*(r, g, b: range[0..255]): Color =
## constructs a color from RGB values. ## constructs a color from RGB values.
result = rawRGB(r, g, b) result = rawRGB(r, g, b)

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2010 Andreas Rumpf # (c) Copyright 2010 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -25,9 +25,11 @@ const
type type
TComplex* = tuple[re, im: float] Complex* = tuple[re, im: float]
## a complex number, consisting of a real and an imaginary part ## a complex number, consisting of a real and an imaginary part
{.deprecated: [TComplex: Complex].}
proc `==` *(x, y: TComplex): bool = proc `==` *(x, y: TComplex): bool =
## Compare two complex numbers `x` and `y` for equality. ## Compare two complex numbers `x` and `y` for equality.
result = x.re == y.re and x.im == y.im result = x.re == y.re and x.im == y.im

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2014 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this

View file

@ -1,13 +1,13 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2014 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.
# #
## Implements Nimrod's 'spawn'. ## Implements Nim's 'spawn'.
when not compileOption("threads"): when not compileOption("threads"):
{.error: "Threadpool requires --threads:on option.".} {.error: "Threadpool requires --threads:on option.".}

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -11,7 +11,7 @@
import strtabs, times import strtabs, times
proc parseCookies*(s: string): PStringTable = proc parseCookies*(s: string): StringTableRef =
## parses cookies into a string table. ## parses cookies into a string table.
result = newStringTable(modeCaseInsensitive) result = newStringTable(modeCaseInsensitive)
var i = 0 var i = 0
@ -39,7 +39,7 @@ proc setCookie*(key, value: string, domain = "", path = "",
if path != "": result.add("; Path=" & path) if path != "": result.add("; Path=" & path)
if expires != "": result.add("; Expires=" & expires) if expires != "": result.add("; Expires=" & expires)
proc setCookie*(key, value: string, expires: TTimeInfo, proc setCookie*(key, value: string, expires: TimeInfo,
domain = "", path = "", noName = false): string = domain = "", path = "", noName = false): string =
## Creates a command in the format of ## Creates a command in the format of
## ``Set-Cookie: key=value; Domain=...; ...`` ## ``Set-Cookie: key=value; Domain=...; ...``
@ -50,7 +50,7 @@ proc setCookie*(key, value: string, expires: TTimeInfo,
format(expires, "ddd',' dd MMM yyyy HH:mm:ss 'UTC'"), noName) format(expires, "ddd',' dd MMM yyyy HH:mm:ss 'UTC'"), noName)
when isMainModule: when isMainModule:
var tim = TTime(int(getTime()) + 76 * (60 * 60 * 24)) var tim = Time(int(getTime()) + 76 * (60 * 60 * 24))
echo(setCookie("test", "value", tim.getGMTime())) echo(setCookie("test", "value", tim.getGMTime()))

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -12,7 +12,9 @@
## Windows ``LoadLibrary``. ## Windows ``LoadLibrary``.
type type
TLibHandle* = pointer ## a handle to a dynamically loaded library LibHandle* = pointer ## a handle to a dynamically loaded library
{.deprecated: [TLibHandle: LibHandle].}
proc loadLib*(path: string, global_symbols=false): TLibHandle proc loadLib*(path: string, global_symbols=false): TLibHandle
## loads a library from `path`. Returns nil if the library could not ## loads a library from `path`. Returns nil if the library could not

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2014 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -14,18 +14,20 @@ import os, parseutils, strutils
when not defined(windows): when not defined(windows):
type type
TConverter = object ConverterObj = object
PConverter* = ptr TConverter ## can convert between two character sets EncodingConverter* = ptr ConverterObj ## can convert between two character sets
else: else:
type type
TCodePage = distinct int32 CodePage = distinct int32
PConverter* = object EncodingConverter* = object
dest, src: TCodePage dest, src: CodePage
type type
EInvalidEncoding* = object of EInvalidValue ## exception that is raised EncodingError* = object of ValueError ## exception that is raised
## for encoding errors ## for encoding errors
{.deprecated: [EInvalidEncoding: EncodingError, PConverter: EncodingConverter].}
when defined(windows): when defined(windows):
proc eqEncodingNames(a, b: string): bool = proc eqEncodingNames(a, b: string): bool =
@ -214,26 +216,26 @@ when defined(windows):
defaultChar: array[0..1, char] defaultChar: array[0..1, char]
leadByte: array[0..12-1, char] leadByte: array[0..12-1, char]
proc getCPInfo(codePage: TCodePage, lpCPInfo: var TCpInfo): int32 {. proc getCPInfo(codePage: CodePage, lpCPInfo: var TCpInfo): int32 {.
stdcall, importc: "GetCPInfo", dynlib: "kernel32".} stdcall, importc: "GetCPInfo", dynlib: "kernel32".}
proc nameToCodePage(name: string): TCodePage = proc nameToCodePage(name: string): CodePage =
var nameAsInt: int var nameAsInt: int
if parseInt(name, nameAsInt) == 0: nameAsInt = -1 if parseInt(name, nameAsInt) == 0: nameAsInt = -1
for no, na in items(winEncodings): for no, na in items(winEncodings):
if no == nameAsInt or eqEncodingNames(na, name): return TCodePage(no) if no == nameAsInt or eqEncodingNames(na, name): return CodePage(no)
result = TCodePage(-1) result = CodePage(-1)
proc codePageToName(c: TCodePage): string = proc codePageToName(c: CodePage): string =
for no, na in items(winEncodings): for no, na in items(winEncodings):
if no == int(c): if no == int(c):
return if na.len != 0: na else: $no return if na.len != 0: na else: $no
result = "" result = ""
proc getACP(): TCodePage {.stdcall, importc: "GetACP", dynlib: "kernel32".} proc getACP(): CodePage {.stdcall, importc: "GetACP", dynlib: "kernel32".}
proc multiByteToWideChar( proc multiByteToWideChar(
codePage: TCodePage, codePage: CodePage,
dwFlags: int32, dwFlags: int32,
lpMultiByteStr: cstring, lpMultiByteStr: cstring,
cbMultiByte: cint, cbMultiByte: cint,
@ -242,7 +244,7 @@ when defined(windows):
stdcall, importc: "MultiByteToWideChar", dynlib: "kernel32".} stdcall, importc: "MultiByteToWideChar", dynlib: "kernel32".}
proc wideCharToMultiByte( proc wideCharToMultiByte(
codePage: TCodePage, codePage: CodePage,
dwFlags: int32, dwFlags: int32,
lpWideCharStr: cstring, lpWideCharStr: cstring,
cchWideChar: cint, cchWideChar: cint,

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2011 Alex Mitchell # (c) Copyright 2011 Alex Mitchell
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -14,10 +14,10 @@
## it was inspired by Python's PyEE module. There are two ways you can use ## it was inspired by Python's PyEE module. There are two ways you can use
## events: one is a python-inspired way; the other is more of a C-style way. ## events: one is a python-inspired way; the other is more of a C-style way.
## ##
## .. code-block:: Nimrod ## .. code-block:: Nim
## var ee = initEventEmitter() ## var ee = initEventEmitter()
## var genericargs: TEventArgs ## var genericargs: EventArgs
## proc handleevent(e: TEventArgs) = ## proc handleevent(e: EventArgs) =
## echo("Handled!") ## echo("Handled!")
## ##
## # Python way ## # Python way
@ -27,21 +27,24 @@
## # C/Java way ## # C/Java way
## # Declare a type ## # Declare a type
## type ## type
## TSomeObject = object of TObject ## SomeObject = object of RootObj
## SomeEvent: TEventHandler ## SomeEvent: EventHandler
## var myobj: TSomeObject ## var myobj: SomeObject
## myobj.SomeEvent = initEventHandler("SomeEvent") ## myobj.SomeEvent = initEventHandler("SomeEvent")
## myobj.SomeEvent.addHandler(handleevent) ## myobj.SomeEvent.addHandler(handleevent)
## ee.emit(myobj.SomeEvent, genericargs) ## ee.emit(myobj.SomeEvent, genericargs)
type type
TEventArgs* = object of TObject ## Base object for event arguments that are passed to callback functions. EventArgs* = object of RootObj ## Base object for event arguments that are passed to callback functions.
TEventHandler* = tuple[name: string, handlers: seq[proc(e:TEventArgs) {.closure.}]] ## An eventhandler for an event. EventHandler* = tuple[name: string, handlers: seq[proc(e: EventArgs) {.closure.}]] ## An eventhandler for an event.
type type
TEventEmitter* = object {.pure, final.} ## An object that fires events and holds event handlers for an object. EventEmitter* = object ## An object that fires events and holds event handlers for an object.
s: seq[TEventHandler] s: seq[EventHandler]
EInvalidEvent* = object of EInvalidValue EventError* = object of ValueError
{.deprecated: [TEventArgs: EventArgs, TEventHandler: EventHandler,
TEventEmitter: EventEmitter, EInvalidEvent: EventError].}
proc initEventHandler*(name: string): TEventHandler = proc initEventHandler*(name: string): TEventHandler =
## Initializes an EventHandler with the specified name and returns it. ## Initializes an EventHandler with the specified name and returns it.

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Dominik Picheta # (c) Copyright 2012 Dominik Picheta
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -25,13 +25,13 @@ else:
import inotify, os, asyncio, tables import inotify, os, asyncio, tables
type type
PFSMonitor* = ref TFSMonitor FSMonitor* = ref FSMonitorObj
TFSMonitor = object of TObject FSMonitorObj = object of RootObj
fd: cint fd: cint
handleEvent: proc (m: PFSMonitor, ev: TMonitorEvent) {.closure.} handleEvent: proc (m: FSMonitor, ev: MonitorEvent) {.closure.}
targets: TTable[cint, string] targets: TTable[cint, string]
TMonitorEventType* = enum ## Monitor event type MonitorEventType* = enum ## Monitor event type
MonitorAccess, ## File was accessed. MonitorAccess, ## File was accessed.
MonitorAttrib, ## Metadata changed. MonitorAttrib, ## Metadata changed.
MonitorCloseWrite, ## Writtable file was closed. MonitorCloseWrite, ## Writtable file was closed.
@ -45,8 +45,8 @@ type
MonitorOpen, ## File was opened. MonitorOpen, ## File was opened.
MonitorAll ## Filter for all event types. MonitorAll ## Filter for all event types.
TMonitorEvent* = object MonitorEvent* = object
case kind*: TMonitorEventType ## Type of the event. case kind*: MonitorEventType ## Type of the event.
of MonitorMoveSelf, MonitorMoved: of MonitorMoveSelf, MonitorMoved:
oldPath*: string ## Old absolute location oldPath*: string ## Old absolute location
newPath*: string ## New absolute location newPath*: string ## New absolute location
@ -58,6 +58,9 @@ type
## watched. ## watched.
wd*: cint ## Watch descriptor. wd*: cint ## Watch descriptor.
{.deprecated: [PFSMonitor: FSMonitor, TFSMonitor: FSMonitorObj,
TMonitorEventType: MonitorEventType, TMonitorEvent: MonitorEvent].}
const const
MaxEvents = 100 MaxEvents = 100
@ -67,7 +70,7 @@ proc newMonitor*(): PFSMonitor =
result.targets = initTable[cint, string]() result.targets = initTable[cint, string]()
result.fd = inotifyInit() result.fd = inotifyInit()
if result.fd < 0: if result.fd < 0:
OSError(OSLastError()) raiseOSError(osLastError())
proc add*(monitor: PFSMonitor, target: string, proc add*(monitor: PFSMonitor, target: string,
filters = {MonitorAll}): cint {.discardable.} = filters = {MonitorAll}): cint {.discardable.} =
@ -101,7 +104,7 @@ proc del*(monitor: PFSMonitor, wd: cint) =
## ##
## If ``wd`` is not a part of ``monitor`` an EOS error is raised. ## If ``wd`` is not a part of ``monitor`` an EOS error is raised.
if inotifyRmWatch(monitor.fd, wd) < 0: if inotifyRmWatch(monitor.fd, wd) < 0:
OSError(OSLastError()) raiseOSError(osLastError())
proc getEvent(m: PFSMonitor, fd: cint): seq[TMonitorEvent] = proc getEvent(m: PFSMonitor, fd: cint): seq[TMonitorEvent] =
result = @[] result = @[]
@ -197,18 +200,19 @@ proc register*(d: PDispatcher, monitor: PFSMonitor,
d.register(deleg) d.register(deleg)
when isMainModule: when isMainModule:
var disp = newDispatcher() proc main =
var monitor = newMonitor() var disp = newDispatcher()
echo monitor.add("/home/dom/inotifytests/") var monitor = newMonitor()
disp.register(monitor, echo monitor.add("/home/dom/inotifytests/")
proc (m: PFSMonitor, ev: TMonitorEvent) = disp.register(monitor,
echo("Got event: ", ev.kind) proc (m: PFSMonitor, ev: TMonitorEvent) =
if ev.kind == MonitorMoved: echo("Got event: ", ev.kind)
echo("From ", ev.oldPath, " to ", ev.newPath) if ev.kind == MonitorMoved:
echo("Name is ", ev.name) echo("From ", ev.oldPath, " to ", ev.newPath)
else: echo("Name is ", ev.name)
echo("Name ", ev.name, " fullname ", ev.fullName)) else:
echo("Name ", ev.name, " fullname ", ev.fullName))
while true:
if not disp.poll(): break while true:
if not disp.poll(): break
main()

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Dominik Picheta # (c) Copyright 2012 Dominik Picheta
# 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.
@ -14,8 +14,8 @@ import sockets, strutils, parseutils, times, os, asyncio
## by `RFC 959 <http://tools.ietf.org/html/rfc959>`_. ## by `RFC 959 <http://tools.ietf.org/html/rfc959>`_.
## ##
## This module provides both a synchronous and asynchronous implementation. ## This module provides both a synchronous and asynchronous implementation.
## The asynchronous implementation requires you to use the ``AsyncFTPClient`` ## The asynchronous implementation requires you to use the ``asyncFTPClient``
## function. You are then required to register the ``PAsyncFTPClient`` with a ## function. You are then required to register the ``AsyncFTPClient`` with a
## asyncio dispatcher using the ``register`` function. Take a look at the ## asyncio dispatcher using the ``register`` function. Take a look at the
## asyncio module documentation for more information. ## asyncio module documentation for more information.
## ##
@ -24,64 +24,63 @@ import sockets, strutils, parseutils, times, os, asyncio
## ##
## Here is some example usage of this module: ## Here is some example usage of this module:
## ##
## .. code-block:: Nimrod ## .. code-block:: Nim
## var ftp = FTPClient("example.org", user = "user", pass = "pass") ## var ftp = ftpClient("example.org", user = "user", pass = "pass")
## ftp.connect() ## ftp.connect()
## ftp.retrFile("file.ext", "file.ext") ## ftp.retrFile("file.ext", "file.ext")
## ##
## **Warning:** The API of this module is unstable, and therefore is subject ## **Warning:** The API of this module is unstable, and therefore is subject
## to change. ## to change.
type type
TFTPClient* = object of TObject FTPClientObj* = object of RootObj
case isAsync: bool case isAsync: bool
of false: of false:
csock: TSocket # Command connection socket csock: Socket # Command connection socket
dsock: TSocket # Data connection socket dsock: Socket # Data connection socket
else: else:
dummyA, dummyB: pointer # workaround a Nimrod API issue dummyA, dummyB: pointer # workaround a Nimrod API issue
asyncCSock: PAsyncSocket asyncCSock: AsyncSocket
asyncDSock: PAsyncSocket asyncDSock: AsyncSocket
handleEvent*: proc (ftp: PAsyncFTPClient, ev: TFTPEvent){.closure,gcsafe.} handleEvent*: proc (ftp: AsyncFTPClient, ev: FTPEvent){.closure,gcsafe.}
disp: PDispatcher disp: Dispatcher
asyncDSockID: PDelegate asyncDSockID: Delegate
user, pass: string user, pass: string
address: string address: string
port: TPort port: Port
jobInProgress: bool jobInProgress: bool
job: ref TFTPJob job: ref FTPJob
dsockConnected: bool dsockConnected: bool
PFTPClient* = ref TFTPClient FTPClient* = ref FTPClientObj
FTPJobType* = enum FTPJobType* = enum
JRetrText, JRetr, JStore JRetrText, JRetr, JStore
TFTPJob = object FTPJob = object
prc: proc (ftp: PFTPClient, async: bool): bool {.nimcall, gcsafe.} prc: proc (ftp: PFTPClient, async: bool): bool {.nimcall, gcsafe.}
case typ*: FTPJobType case typ*: FTPJobType
of JRetrText: of JRetrText:
lines: string lines: string
of JRetr, JStore: of JRetr, JStore:
file: TFile file: File
filename: string filename: string
total: biggestInt # In bytes. total: BiggestInt # In bytes.
progress: biggestInt # In bytes. progress: BiggestInt # In bytes.
oneSecond: biggestInt # Bytes transferred in one second. oneSecond: BiggestInt # Bytes transferred in one second.
lastProgressReport: float # Time lastProgressReport: float # Time
toStore: string # Data left to upload (Only used with async) toStore: string # Data left to upload (Only used with async)
else: nil else: nil
PAsyncFTPClient* = ref TAsyncFTPClient ## Async alternative to TFTPClient. AsyncFTPClient* = ref AsyncFTPClientObj ## Async alternative to TFTPClient.
TAsyncFTPClient* = object of TFTPClient AsyncFTPClientObj* = object of FTPClientObj
FTPEventType* = enum FTPEventType* = enum
EvTransferProgress, EvLines, EvRetr, EvStore EvTransferProgress, EvLines, EvRetr, EvStore
TFTPEvent* = object ## Event FTPEvent* = object ## Event
filename*: string filename*: string
case typ*: FTPEventType case typ*: FTPEventType
of EvLines: of EvLines:
@ -89,13 +88,19 @@ type
of EvRetr, EvStore: ## Retr/Store operation finished. of EvRetr, EvStore: ## Retr/Store operation finished.
nil nil
of EvTransferProgress: of EvTransferProgress:
bytesTotal*: biggestInt ## Bytes total. bytesTotal*: BiggestInt ## Bytes total.
bytesFinished*: biggestInt ## Bytes transferred. bytesFinished*: BiggestInt ## Bytes transferred.
speed*: biggestInt ## Speed in bytes/s speed*: BiggestInt ## Speed in bytes/s
currentJob*: FTPJobType ## The current job being performed. currentJob*: FTPJobType ## The current job being performed.
EInvalidReply* = object of ESynch ReplyError* = object of IOError
EFTP* = object of ESynch FTPError* = object of IOError
{.deprecated: [
TFTPClient: FTPClientObj, TFTPJob: FTPJob, PAsyncFTPClient: AsyncFTPClient,
TAsyncFTPClient: AsyncFTPClientObj, TFTPEvent: FTPEvent,
EInvalidReply: ReplyError, EFTP: FTPError
].}
proc ftpClient*(address: string, port = TPort(21), proc ftpClient*(address: string, port = TPort(21),
user, pass = ""): PFTPClient = user, pass = ""): PFTPClient =

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2014 Dominik Picheta # (c) Copyright 2014 Dominik Picheta
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -12,6 +12,8 @@
## may be any Nimrod or user defined type. This module supports matching ## may be any Nimrod or user defined type. This module supports matching
## of keys in case-sensitive, case-insensitive and style-insensitive modes. ## of keys in case-sensitive, case-insensitive and style-insensitive modes.
{.deprecated.}
import import
os, hashes, strutils os, hashes, strutils

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -8,13 +8,13 @@
# #
## This module implements efficient computations of hash values for diverse ## This module implements efficient computations of hash values for diverse
## Nimrod types. All the procs are based on these two building blocks: the `!& ## Nim types. All the procs are based on these two building blocks: the `!&
## proc <#!&>`_ used to start or mix a hash value, and the `!$ proc <#!$>`_ ## proc <#!&>`_ used to start or mix a hash value, and the `!$ proc <#!$>`_
## used to *finish* the hash value. If you want to implement hash procs for ## used to *finish* the hash value. If you want to implement hash procs for
## your custom types you will end up writing the following kind of skeleton of ## your custom types you will end up writing the following kind of skeleton of
## code: ## code:
## ##
## .. code-block:: nimrod ## .. code-block:: Nim
## proc hash(x: Something): THash = ## proc hash(x: Something): THash =
## ## Computes a THash from `x`. ## ## Computes a THash from `x`.
## var h: THash = 0 ## var h: THash = 0
@ -29,7 +29,7 @@
## like for example objects made up of ``strings``, you can simply hash ## like for example objects made up of ``strings``, you can simply hash
## together the hash value of the individual fields: ## together the hash value of the individual fields:
## ##
## .. code-block:: nimrod ## .. code-block:: Nim
## proc hash(x: Something): THash = ## proc hash(x: Something): THash =
## ## Computes a THash from `x`. ## ## Computes a THash from `x`.
## var h: THash = 0 ## var h: THash = 0

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2014 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -18,13 +18,13 @@
## ##
## Example: ## Example:
## ##
## .. code-block:: nimrod ## .. code-block:: Nim
## var nim = "Nimrod" ## var nim = "Nim"
## echo h1(a(href="http://nimrod-lang.org", nim)) ## echo h1(a(href="http://nim-lang.org", nim))
## ##
## Writes the string:: ## Writes the string::
## ##
## <h1><a href="http://nimrod-lang.org">Nimrod</a></h1> ## <h1><a href="http://nim-lang.org">Nim</a></h1>
## ##
import import
@ -53,8 +53,7 @@ proc delete[T](s: var seq[T], attr: T): bool =
setLen(s, L-1) setLen(s, L-1)
result = true result = true
proc xmlCheckedTag*(e: PNimrodNode, tag: string, proc xmlCheckedTag*(e: PNimrodNode, tag: string, optAttr = "", reqAttr = "",
optAttr = "", reqAttr = "",
isLeaf = false): PNimrodNode {.compileTime.} = isLeaf = false): PNimrodNode {.compileTime.} =
## use this procedure to define a new XML tag ## use this procedure to define a new XML tag
@ -484,7 +483,7 @@ macro `var`*(e: expr): expr {.immediate.} =
result = xmlCheckedTag(e, "var", commonAttr) result = xmlCheckedTag(e, "var", commonAttr)
when isMainModule: when isMainModule:
var nim = "Nimrod" var nim = "Nim"
echo h1(a(href="http://nimrod-code.org", nim)) echo h1(a(href="http://nim-lang.org", nim))
echo form(action="test", `accept-charset` = "Content-Type") echo form(action="test", `accept-charset` = "Content-Type")

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2013 Andreas Rumpf # (c) Copyright 2013 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -13,7 +13,7 @@
## It can be used to parse a wild HTML document and output it as valid XHTML ## It can be used to parse a wild HTML document and output it as valid XHTML
## document (well, if you are lucky): ## document (well, if you are lucky):
## ##
## .. code-block:: nimrod ## .. code-block:: Nim
## ##
## echo loadHtml("mydirty.html") ## echo loadHtml("mydirty.html")
## ##
@ -29,7 +29,7 @@
## and write back the modified version. In this case we look for hyperlinks ## and write back the modified version. In this case we look for hyperlinks
## ending with the extension ``.rst`` and convert them to ``.html``. ## ending with the extension ``.rst`` and convert them to ``.html``.
## ##
## .. code-block:: nimrod ## .. code-block:: Nim
## ##
## import htmlparser ## import htmlparser
## import xmltree # To use '$' for PXmlNode ## import xmltree # To use '$' for PXmlNode

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2010 Dominik Picheta, Andreas Rumpf # (c) Copyright 2010 Dominik Picheta, Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -21,7 +21,7 @@
## This example uses HTTP GET to retrieve ## This example uses HTTP GET to retrieve
## ``http://google.com`` ## ``http://google.com``
## ##
## .. code-block:: nimrod ## .. code-block:: Nim
## echo(getContent("http://google.com")) ## echo(getContent("http://google.com"))
## ##
## Using HTTP POST ## Using HTTP POST
@ -31,7 +31,7 @@
## uses ``multipart/form-data`` as the ``Content-Type`` to send the HTML to ## uses ``multipart/form-data`` as the ``Content-Type`` to send the HTML to
## the server. ## the server.
## ##
## .. code-block:: nimrod ## .. code-block:: Nim
## var headers: string = "Content-Type: multipart/form-data; boundary=xyz\c\L" ## var headers: string = "Content-Type: multipart/form-data; boundary=xyz\c\L"
## var body: string = "--xyz\c\L" ## var body: string = "--xyz\c\L"
## # soap 1.2 output ## # soap 1.2 output
@ -54,7 +54,7 @@
## on many operating systems. httpclient will use SSL automatically if you give ## on many operating systems. httpclient will use SSL automatically if you give
## any of the functions a url with the ``https`` schema, for example: ## any of the functions a url with the ``https`` schema, for example:
## ``https://github.com/``, you also have to compile with ``ssl`` defined like so: ## ``https://github.com/``, you also have to compile with ``ssl`` defined like so:
## ``nimrod c -d:ssl ...``. ## ``nim c -d:ssl ...``.
## ##
## Timeouts ## Timeouts
## ======== ## ========
@ -80,34 +80,38 @@ import asyncnet, asyncdispatch
import rawsockets import rawsockets
type type
TResponse* = tuple[ Response* = tuple[
version: string, version: string,
status: string, status: string,
headers: PStringTable, headers: StringTableRef,
body: string] body: string]
PProxy* = ref object Proxy* = ref object
url*: TUrl url*: TUrl
auth*: string auth*: string
EInvalidProtocol* = object of ESynch ## exception that is raised when server ProtocolError* = object of IOError ## exception that is raised when server
## does not conform to the implemented ## does not conform to the implemented
## protocol ## protocol
EHttpRequestErr* = object of ESynch ## Thrown in the ``getContent`` proc HttpRequestError* = object of IOError ## Thrown in the ``getContent`` proc
## and ``postContent`` proc, ## and ``postContent`` proc,
## when the server returns an error ## when the server returns an error
const defUserAgent* = "Nimrod httpclient/0.1" {.deprecated: [TResponse: Response, PProxy: Proxy,
EInvalidProtocol: ProtocolError, EHttpRequestErr: HttpRequestError
].}
const defUserAgent* = "Nim httpclient/0.1"
proc httpError(msg: string) = proc httpError(msg: string) =
var e: ref EInvalidProtocol var e: ref ProtocolError
new(e) new(e)
e.msg = msg e.msg = msg
raise e raise e
proc fileError(msg: string) = proc fileError(msg: string) =
var e: ref EIO var e: ref IOError
new(e) new(e)
e.msg = msg e.msg = msg
raise e raise e
@ -232,7 +236,7 @@ proc parseResponse(s: TSocket, getBody: bool, timeout: int): TResponse =
result.body = "" result.body = ""
type type
THttpMethod* = enum ## the requested HttpMethod HttpMethod* = enum ## the requested HttpMethod
httpHEAD, ## Asks for the response identical to the one that would httpHEAD, ## Asks for the response identical to the one that would
## correspond to a GET request, but without the response ## correspond to a GET request, but without the response
## body. ## body.
@ -250,6 +254,8 @@ type
httpCONNECT ## Converts the request connection to a transparent httpCONNECT ## Converts the request connection to a transparent
## TCP/IP tunnel, usually used for proxies. ## TCP/IP tunnel, usually used for proxies.
{.deprecated: [THttpMethod: HttpMethod].}
when not defined(ssl): when not defined(ssl):
type PSSLContext = ref object type PSSLContext = ref object
let defaultSSLContext: PSSLContext = nil let defaultSSLContext: PSSLContext = nil
@ -288,7 +294,7 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
add(headers, "\c\L") add(headers, "\c\L")
var s = socket() var s = socket()
if s == InvalidSocket: osError(osLastError()) if s == InvalidSocket: raiseOSError(osLastError())
var port = sockets.TPort(80) var port = sockets.TPort(80)
if r.scheme == "https": if r.scheme == "https":
when defined(ssl): when defined(ssl):
@ -431,16 +437,18 @@ proc generateHeaders(r: TURL, httpMethod: THttpMethod,
add(result, "\c\L") add(result, "\c\L")
type type
PAsyncHttpClient* = ref object AsyncHttpClient* = ref object
socket: PAsyncSocket socket: AsyncSocket
connected: bool connected: bool
currentURL: TURL ## Where we are currently connected. currentURL: TURL ## Where we are currently connected.
headers: PStringTable headers: StringTableRef
maxRedirects: int maxRedirects: int
userAgent: string userAgent: string
{.deprecated: [PAsyncHttpClient: AsyncHttpClient].}
proc newAsyncHttpClient*(userAgent = defUserAgent, proc newAsyncHttpClient*(userAgent = defUserAgent,
maxRedirects = 5): PAsyncHttpClient = maxRedirects = 5): AsyncHttpClient =
## Creates a new PAsyncHttpClient instance. ## Creates a new PAsyncHttpClient instance.
## ##
## ``userAgent`` specifies the user agent that will be used when making ## ``userAgent`` specifies the user agent that will be used when making
@ -453,7 +461,7 @@ proc newAsyncHttpClient*(userAgent = defUserAgent,
result.userAgent = defUserAgent result.userAgent = defUserAgent
result.maxRedirects = maxRedirects result.maxRedirects = maxRedirects
proc close*(client: PAsyncHttpClient) = proc close*(client: AsyncHttpClient) =
## Closes any connections held by the HTTP client. ## Closes any connections held by the HTTP client.
if client.connected: if client.connected:
client.socket.close() client.socket.close()

View file

@ -145,7 +145,7 @@ when false:
var buf = alloc(contentLength) var buf = alloc(contentLength)
if recv(client, buf, contentLength) != contentLength: if recv(client, buf, contentLength) != contentLength:
dealloc(buf) dealloc(buf)
OSError() raiseOSError()
var inp = process.inputStream var inp = process.inputStream
inp.writeData(buf, contentLength) inp.writeData(buf, contentLength)
dealloc(buf) dealloc(buf)

View file

@ -1,7 +1,7 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf, Dominik Picheta # (c) Copyright 2014 Andreas Rumpf, Dominik Picheta
# #
# 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.
@ -35,7 +35,7 @@ import
hashes, strutils, lexbase, streams, unicode hashes, strutils, lexbase, streams, unicode
type type
TJsonEventKind* = enum ## enumeration of all events that may occur when parsing JsonEventKind* = enum ## enumeration of all events that may occur when parsing
jsonError, ## an error ocurred during parsing jsonError, ## an error ocurred during parsing
jsonEof, ## end of file reached jsonEof, ## end of file reached
jsonString, ## a string literal jsonString, ## a string literal
@ -65,7 +65,7 @@ type
tkColon, tkColon,
tkComma tkComma
TJsonError* = enum ## enumeration that lists all errors that can occur JsonError* = enum ## enumeration that lists all errors that can occur
errNone, ## no error errNone, ## no error
errInvalidToken, ## invalid token errInvalidToken, ## invalid token
errStringExpected, ## string expected errStringExpected, ## string expected
@ -78,20 +78,23 @@ type
errEofExpected, ## EOF expected errEofExpected, ## EOF expected
errExprExpected ## expr expected errExprExpected ## expr expected
TParserState = enum ParserState = enum
stateEof, stateStart, stateObject, stateArray, stateExpectArrayComma, stateEof, stateStart, stateObject, stateArray, stateExpectArrayComma,
stateExpectObjectComma, stateExpectColon, stateExpectValue stateExpectObjectComma, stateExpectColon, stateExpectValue
TJsonParser* = object of TBaseLexer ## the parser object. JsonParser* = object of TBaseLexer ## the parser object.
a: string a: string
tok: TTokKind tok: TTokKind
kind: TJsonEventKind kind: JsonEventKind
err: TJsonError err: JsonError
state: seq[TParserState] state: seq[ParserState]
filename: string filename: string
{.deprecated: [TJsonEventKind: JsonEventKind, TJsonError: JsonError,
TJsonParser: JsonParser].}
const const
errorMessages: array [TJsonError, string] = [ errorMessages: array [JsonError, string] = [
"no error", "no error",
"invalid token", "invalid token",
"string expected", "string expected",
@ -116,7 +119,7 @@ const
"{", "}", "[", "]", ":", "," "{", "}", "[", "]", ":", ","
] ]
proc open*(my: var TJsonParser, input: PStream, filename: string) = proc open*(my: var JsonParser, input: PStream, filename: string) =
## initializes the parser with an input stream. `Filename` is only used ## initializes the parser with an input stream. `Filename` is only used
## for nice error messages. ## for nice error messages.
lexbase.open(my, input) lexbase.open(my, input)
@ -125,49 +128,49 @@ proc open*(my: var TJsonParser, input: PStream, filename: string) =
my.kind = jsonError my.kind = jsonError
my.a = "" my.a = ""
proc close*(my: var TJsonParser) {.inline.} = proc close*(my: var JsonParser) {.inline.} =
## closes the parser `my` and its associated input stream. ## closes the parser `my` and its associated input stream.
lexbase.close(my) lexbase.close(my)
proc str*(my: TJsonParser): string {.inline.} = proc str*(my: JsonParser): string {.inline.} =
## returns the character data for the events: ``jsonInt``, ``jsonFloat``, ## returns the character data for the events: ``jsonInt``, ``jsonFloat``,
## ``jsonString`` ## ``jsonString``
assert(my.kind in {jsonInt, jsonFloat, jsonString}) assert(my.kind in {jsonInt, jsonFloat, jsonString})
return my.a return my.a
proc getInt*(my: TJsonParser): BiggestInt {.inline.} = proc getInt*(my: JsonParser): BiggestInt {.inline.} =
## returns the number for the event: ``jsonInt`` ## returns the number for the event: ``jsonInt``
assert(my.kind == jsonInt) assert(my.kind == jsonInt)
return parseBiggestInt(my.a) return parseBiggestInt(my.a)
proc getFloat*(my: TJsonParser): float {.inline.} = proc getFloat*(my: JsonParser): float {.inline.} =
## returns the number for the event: ``jsonFloat`` ## returns the number for the event: ``jsonFloat``
assert(my.kind == jsonFloat) assert(my.kind == jsonFloat)
return parseFloat(my.a) return parseFloat(my.a)
proc kind*(my: TJsonParser): TJsonEventKind {.inline.} = proc kind*(my: JsonParser): JsonEventKind {.inline.} =
## returns the current event type for the JSON parser ## returns the current event type for the JSON parser
return my.kind return my.kind
proc getColumn*(my: TJsonParser): int {.inline.} = proc getColumn*(my: JsonParser): int {.inline.} =
## get the current column the parser has arrived at. ## get the current column the parser has arrived at.
result = getColNumber(my, my.bufpos) result = getColNumber(my, my.bufpos)
proc getLine*(my: TJsonParser): int {.inline.} = proc getLine*(my: JsonParser): int {.inline.} =
## get the current line the parser has arrived at. ## get the current line the parser has arrived at.
result = my.lineNumber result = my.lineNumber
proc getFilename*(my: TJsonParser): string {.inline.} = proc getFilename*(my: JsonParser): string {.inline.} =
## get the filename of the file that the parser processes. ## get the filename of the file that the parser processes.
result = my.filename result = my.filename
proc errorMsg*(my: TJsonParser): string = proc errorMsg*(my: JsonParser): string =
## returns a helpful error message for the event ``jsonError`` ## returns a helpful error message for the event ``jsonError``
assert(my.kind == jsonError) assert(my.kind == jsonError)
result = "$1($2, $3) Error: $4" % [ result = "$1($2, $3) Error: $4" % [
my.filename, $getLine(my), $getColumn(my), errorMessages[my.err]] my.filename, $getLine(my), $getColumn(my), errorMessages[my.err]]
proc errorMsgExpected*(my: TJsonParser, e: string): string = proc errorMsgExpected*(my: JsonParser, e: string): string =
## returns an error message "`e` expected" in the same format as the ## returns an error message "`e` expected" in the same format as the
## other error messages ## other error messages
result = "$1($2, $3) Error: $4" % [ result = "$1($2, $3) Error: $4" % [
@ -181,7 +184,7 @@ proc handleHexChar(c: char, x: var int): bool =
of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10) of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
else: result = false # error else: result = false # error
proc parseString(my: var TJsonParser): TTokKind = proc parseString(my: var JsonParser): TTokKind =
result = tkString result = tkString
var pos = my.bufpos + 1 var pos = my.bufpos + 1
var buf = my.buf var buf = my.buf
@ -239,7 +242,7 @@ proc parseString(my: var TJsonParser): TTokKind =
inc(pos) inc(pos)
my.bufpos = pos # store back my.bufpos = pos # store back
proc skip(my: var TJsonParser) = proc skip(my: var JsonParser) =
var pos = my.bufpos var pos = my.bufpos
var buf = my.buf var buf = my.buf
while true: while true:
@ -297,7 +300,7 @@ proc skip(my: var TJsonParser) =
break break
my.bufpos = pos my.bufpos = pos
proc parseNumber(my: var TJsonParser) = proc parseNumber(my: var JsonParser) =
var pos = my.bufpos var pos = my.bufpos
var buf = my.buf var buf = my.buf
if buf[pos] == '-': if buf[pos] == '-':
@ -328,7 +331,7 @@ proc parseNumber(my: var TJsonParser) =
inc(pos) inc(pos)
my.bufpos = pos my.bufpos = pos
proc parseName(my: var TJsonParser) = proc parseName(my: var JsonParser) =
var pos = my.bufpos var pos = my.bufpos
var buf = my.buf var buf = my.buf
if buf[pos] in IdentStartChars: if buf[pos] in IdentStartChars:
@ -337,7 +340,7 @@ proc parseName(my: var TJsonParser) =
inc(pos) inc(pos)
my.bufpos = pos my.bufpos = pos
proc getTok(my: var TJsonParser): TTokKind = proc getTok(my: var JsonParser): TTokKind =
setLen(my.a, 0) setLen(my.a, 0)
skip(my) # skip whitespace, comments skip(my) # skip whitespace, comments
case my.buf[my.bufpos] case my.buf[my.bufpos]
@ -381,7 +384,7 @@ proc getTok(my: var TJsonParser): TTokKind =
result = tkError result = tkError
my.tok = result my.tok = result
proc next*(my: var TJsonParser) = proc next*(my: var JsonParser) =
## retrieves the first/next event. This controls the parser. ## retrieves the first/next event. This controls the parser.
var tk = getTok(my) var tk = getTok(my)
var i = my.state.len-1 var i = my.state.len-1
@ -529,7 +532,10 @@ type
of JArray: of JArray:
elems*: seq[PJsonNode] elems*: seq[PJsonNode]
EJsonParsingError* = object of ValueError ## is raised for a JSON error JsonParsingError* = object of ValueError ## is raised for a JSON error
{.deprecated: [EJsonParsingError: JsonParsingError, TJsonNode: JsonNodeObj,
PJsonNode: JsonNode, TJsonNodeKind: JsonNodeKind].}
proc raiseParseErr*(p: TJsonParser, msg: string) {.noinline, noreturn.} = proc raiseParseErr*(p: TJsonParser, msg: string) {.noinline, noreturn.} =
## raises an `EJsonParsingError` exception. ## raises an `EJsonParsingError` exception.

View file

@ -1,6 +1,6 @@
# #
# #
# The Nimrod Compiler # The Nim Compiler
# (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
@ -36,6 +36,8 @@ type
lineStart: int # index of last line start in buffer lineStart: int # index of last line start in buffer
fileOpened: bool fileOpened: bool
{.deprecated: [TBaseLexer: BaseLexer].}
proc open*(L: var TBaseLexer, input: PStream, bufLen: int = 8192) proc open*(L: var TBaseLexer, input: PStream, bufLen: int = 8192)
## inits the TBaseLexer with a stream to read from ## inits the TBaseLexer with a stream to read from

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2014 Andreas Rumpf, Dominik Picheta # (c) Copyright 2014 Andreas Rumpf, Dominik Picheta
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -26,7 +26,7 @@
## The following example demonstrates logging to three different handlers ## The following example demonstrates logging to three different handlers
## simultaneously: ## simultaneously:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## ##
## var L = newConsoleLogger() ## var L = newConsoleLogger()
## var fL = newFileLogger("test.log", fmtStr = verboseFmtStr) ## var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
@ -42,7 +42,7 @@
import strutils, os, times import strutils, os, times
type type
TLevel* = enum ## logging level Level* = enum ## logging level
lvlAll, ## all levels active lvlAll, ## all levels active
lvlDebug, ## debug level (and any above) active lvlDebug, ## debug level (and any above) active
lvlInfo, ## info level (and any above) active lvlInfo, ## info level (and any above) active
@ -52,7 +52,7 @@ type
lvlNone ## no levels active lvlNone ## no levels active
const const
LevelNames*: array [TLevel, string] = [ LevelNames*: array [Level, string] = [
"DEBUG", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "NONE" "DEBUG", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "NONE"
] ]
@ -60,26 +60,29 @@ const
verboseFmtStr* = "$date $time " verboseFmtStr* = "$date $time "
type type
PLogger* = ref object of PObject ## abstract logger; the base type of all loggers Logger* = ref object of RootObj ## abstract logger; the base type of all loggers
levelThreshold*: TLevel ## only messages of level >= levelThreshold levelThreshold*: Leve l ## only messages of level >= levelThreshold
## should be processed ## should be processed
fmtStr: string ## = defaultFmtStr by default, see substituteLog for $date etc. fmtStr: string ## = defaultFmtStr by default, see substituteLog for $date etc.
PConsoleLogger* = ref object of PLogger ## logger that writes the messages to the ConsoleLogger* = ref object of Logger ## logger that writes the messages to the
## console ## console
PFileLogger* = ref object of PLogger ## logger that writes the messages to a file FileLogger* = ref object of Logger ## logger that writes the messages to a file
f: TFile f: TFile
PRollingFileLogger* = ref object of PFileLogger ## logger that writes the RollingFileLogger* = ref object of FileLogger ## logger that writes the
## messages to a file and ## messages to a file and
## performs log rotation ## performs log rotation
maxLines: int # maximum number of lines maxLines: int # maximum number of lines
curLine : int curLine : int
baseName: string # initial filename baseName: string # initial filename
baseMode: TFileMode # initial file mode baseMode: FileMode # initial file mode
logFiles: int # how many log files already created, e.g. basename.1, basename.2... logFiles: int # how many log files already created, e.g. basename.1, basename.2...
{.deprecated: [TLevel: Level, PLogger: Logger, PConsoleLogger: ConsoleLogger,
PFileLogger: FileLogger, PRollingFileLogger: RollingFileLogger].}
proc substituteLog(frmt: string): string = proc substituteLog(frmt: string): string =
## converts $date to the current date ## converts $date to the current date
## converts $time to the current time ## converts $time to the current time
@ -105,7 +108,7 @@ proc substituteLog(frmt: string): string =
of "appdir": result.add(app.splitFile.dir) of "appdir": result.add(app.splitFile.dir)
of "appname": result.add(app.splitFile.name) of "appname": result.add(app.splitFile.name)
method log*(logger: PLogger, level: TLevel, method log*(logger: Logger, level: Level,
frmt: string, args: varargs[string, `$`]) {.raises: [EBase], tags: [FTime, FWriteIO, FReadIO].} = frmt: string, args: varargs[string, `$`]) {.raises: [EBase], tags: [FTime, FWriteIO, FReadIO].} =
## Override this method in custom loggers. Default implementation does ## Override this method in custom loggers. Default implementation does
## nothing. ## nothing.

View file

@ -1,20 +1,20 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2013 Andreas Rumpf # (c) Copyright 2014 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 contains procs for serialization and deseralization of ## This module contains procs for serialization and deseralization of
## arbitrary Nimrod data structures. The serialization format uses JSON. ## arbitrary Nim data structures. The serialization format uses JSON.
## ##
## **Restriction**: For objects their type is **not** serialized. This means ## **Restriction**: For objects their type is **not** serialized. This means
## essentially that it does not work if the object has some other runtime ## essentially that it does not work if the object has some other runtime
## type than its compiletime type: ## type than its compiletime type:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## ##
## type ## type
## TA = object ## TA = object
@ -211,7 +211,7 @@ proc loadAny(p: var TJsonParser, a: TAny, t: var TTable[biggestInt, pointer]) =
raiseParseErr(p, "float expected") raiseParseErr(p, "float expected")
of akRange: loadAny(p, a.skipRange, t) of akRange: loadAny(p, a.skipRange, t)
proc loadAny(s: PStream, a: TAny, t: var TTable[biggestInt, pointer]) = proc loadAny(s: PStream, a: TAny, t: var TTable[BiggestInt, pointer]) =
var p: TJsonParser var p: TJsonParser
open(p, s, "unknown file") open(p, s, "unknown file")
next(p) next(p)

View file

@ -1,7 +1,7 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2014 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.
@ -44,7 +44,7 @@ proc validEmailAddress*(s: string): bool {.noSideEffect,
"aero", "jobs", "museum": return true "aero", "jobs", "museum": return true
return false return false
proc parseInt*(s: string, value: var int, validRange: TSlice[int]) {. proc parseInt*(s: string, value: var int, validRange: Slice[int]) {.
noSideEffect, rtl, extern: "nmatchParseInt".} = noSideEffect, rtl, extern: "nmatchParseInt".} =
## parses `s` into an integer in the range `validRange`. If successful, ## parses `s` into an integer in the range `validRange`. If successful,
## `value` is modified to contain the result. Otherwise no exception is ## `value` is modified to contain the result. Otherwise no exception is
@ -53,7 +53,7 @@ proc parseInt*(s: string, value: var int, validRange: TSlice[int]) {.
var x = value var x = value
try: try:
discard parseutils.parseInt(s, x, 0) discard parseutils.parseInt(s, x, 0)
except EOverflow: except OverflowError:
discard discard
if x in validRange: value = x if x in validRange: value = x

View file

@ -1,7 +1,7 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2014 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.
@ -9,7 +9,7 @@
## Constructive mathematics is naturally typed. -- Simon Thompson ## Constructive mathematics is naturally typed. -- Simon Thompson
## ##
## Basic math routines for Nimrod. ## Basic math routines for Nim.
## This module is available for the `JavaScript target ## This module is available for the `JavaScript target
## <backends.html#the-javascript-target>`_. ## <backends.html#the-javascript-target>`_.
@ -41,8 +41,8 @@ const
## for Nimrod's ``float`` type. ## for Nimrod's ``float`` type.
type type
TFloatClass* = enum ## describes the class a floating point value belongs to. FloatClass* = enum ## describes the class a floating point value belongs to.
## This is the type that is returned by `classify`. ## This is the type that is returned by `classify`.
fcNormal, ## value is an ordinary nonzero floating point value fcNormal, ## value is an ordinary nonzero floating point value
fcSubnormal, ## value is a subnormal (a very small) floating point value fcSubnormal, ## value is a subnormal (a very small) floating point value
fcZero, ## value is zero fcZero, ## value is zero
@ -51,10 +51,10 @@ type
fcInf, ## value is positive infinity fcInf, ## value is positive infinity
fcNegInf ## value is negative infinity fcNegInf ## value is negative infinity
proc classify*(x: float): TFloatClass = proc classify*(x: float): FloatClass =
## classifies a floating point value. Returns `x`'s class as specified by ## classifies a floating point value. Returns `x`'s class as specified by
## `TFloatClass`. ## `FloatClass`.
# JavaScript and most C compilers have no classify: # JavaScript and most C compilers have no classify:
if x == 0.0: if x == 0.0:
if 1.0/x == Inf: if 1.0/x == Inf:
@ -287,12 +287,14 @@ proc random[T](a: openArray[T]): T =
result = a[random(a.low..a.len)] result = a[random(a.low..a.len)]
type type
TRunningStat* {.pure,final.} = object ## an accumulator for statistical data RunningStat* = object ## an accumulator for statistical data
n*: int ## number of pushed data n*: int ## number of pushed data
sum*, min*, max*, mean*: float ## self-explaining sum*, min*, max*, mean*: float ## self-explaining
oldM, oldS, newS: float oldM, oldS, newS: float
proc push*(s: var TRunningStat, x: float) = {.deprecated: [TFloatClass: FloatClass, TRunningStat: RunningStat].}
proc push*(s: var RunningStat, x: float) =
## pushes a value `x` for processing ## pushes a value `x` for processing
inc(s.n) inc(s.n)
# See Knuth TAOCP vol 2, 3rd edition, page 232 # See Knuth TAOCP vol 2, 3rd edition, page 232
@ -313,16 +315,16 @@ proc push*(s: var TRunningStat, x: float) =
s.oldS = s.newS s.oldS = s.newS
s.sum = s.sum + x s.sum = s.sum + x
proc push*(s: var TRunningStat, x: int) = proc push*(s: var RunningStat, x: int) =
## pushes a value `x` for processing. `x` is simply converted to ``float`` ## pushes a value `x` for processing. `x` is simply converted to ``float``
## and the other push operation is called. ## and the other push operation is called.
push(s, toFloat(x)) push(s, toFloat(x))
proc variance*(s: TRunningStat): float = proc variance*(s: RunningStat): float =
## computes the current variance of `s` ## computes the current variance of `s`
if s.n > 1: result = s.newS / (toFloat(s.n - 1)) if s.n > 1: result = s.newS / (toFloat(s.n - 1))
proc standardDeviation*(s: TRunningStat): float = proc standardDeviation*(s: RunningStat): float =
## computes the current standard deviation of `s` ## computes the current standard deviation of `s`
result = sqrt(variance(s)) result = sqrt(variance(s))

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2010 Andreas Rumpf # (c) Copyright 2010 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2014 Nimrod Contributors # (c) Copyright 2014 Nimrod Contributors
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -22,7 +22,7 @@ else:
import os import os
type type
TMemFile* = object {.pure.} ## represents a memory mapped file MemFile* = object ## represents a memory mapped file
mem*: pointer ## a pointer to the memory mapped file. The pointer mem*: pointer ## a pointer to the memory mapped file. The pointer
## can be used directly to change the contents of the ## can be used directly to change the contents of the
## file, if it was opened with write access. ## file, if it was opened with write access.
@ -34,8 +34,9 @@ type
else: else:
handle: cint handle: cint
{.deprecated: [TMemFile: MemFile].}
proc mapMem*(m: var TMemFile, mode: FileMode = fmRead, proc mapMem*(m: var MemFile, mode: FileMode = fmRead,
mappedSize = -1, offset = 0): pointer = mappedSize = -1, offset = 0): pointer =
var readonly = mode == fmRead var readonly = mode == fmRead
when defined(windows): when defined(windows):
@ -60,27 +61,27 @@ proc mapMem*(m: var TMemFile, mode: FileMode = fmRead,
osError(osLastError()) osError(osLastError())
proc unmapMem*(f: var TMemFile, p: pointer, size: int) = proc unmapMem*(f: var MemFile, p: pointer, size: int) =
## unmaps the memory region ``(p, <p+size)`` of the mapped file `f`. ## unmaps the memory region ``(p, <p+size)`` of the mapped file `f`.
## All changes are written back to the file system, if `f` was opened ## All changes are written back to the file system, if `f` was opened
## with write access. ``size`` must be of exactly the size that was requested ## with write access. ``size`` must be of exactly the size that was requested
## via ``mapMem``. ## via ``mapMem``.
when defined(windows): when defined(windows):
if unmapViewOfFile(p) == 0: osError(osLastError()) if unmapViewOfFile(p) == 0: raiseOSError(osLastError())
else: else:
if munmap(p, size) != 0: osError(osLastError()) if munmap(p, size) != 0: raiseOSError(osLastError())
proc open*(filename: string, mode: FileMode = fmRead, proc open*(filename: string, mode: FileMode = fmRead,
mappedSize = -1, offset = 0, newFileSize = -1): TMemFile = mappedSize = -1, offset = 0, newFileSize = -1): MemFile =
## opens a memory mapped file. If this fails, ``EOS`` is raised. ## opens a memory mapped file. If this fails, ``EOS`` is raised.
## `newFileSize` can only be set if the file does not exist and is opened ## `newFileSize` can only be set if the file does not exist and is opened
## with write access (e.g., with fmReadWrite). `mappedSize` and `offset` ## with write access (e.g., with fmReadWrite). `mappedSize` and `offset`
## can be used to map only a slice of the file. Example: ## can be used to map only a slice of the file. Example:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## var ## var
## mm, mm_full, mm_half: TMemFile ## mm, mm_full, mm_half: MemFile
## ##
## mm = memfiles.open("/tmp/test.mmap", mode = fmWrite, newFileSize = 1024) # Create a new file ## mm = memfiles.open("/tmp/test.mmap", mode = fmWrite, newFileSize = 1024) # Create a new file
## mm.close() ## mm.close()
@ -100,11 +101,11 @@ proc open*(filename: string, mode: FileMode = fmRead,
result.size = 0 result.size = 0
when defined(windows): when defined(windows):
template fail(errCode: TOSErrorCode, msg: expr) = template fail(errCode: OSErrorCode, msg: expr) =
rollback() rollback()
if result.fHandle != 0: discard closeHandle(result.fHandle) if result.fHandle != 0: discard closeHandle(result.fHandle)
if result.mapHandle != 0: discard closeHandle(result.mapHandle) if result.mapHandle != 0: discard closeHandle(result.mapHandle)
osError(errCode) raiseOSError(errCode)
# return false # return false
#raise newException(EIO, msg) #raise newException(EIO, msg)
@ -169,10 +170,10 @@ proc open*(filename: string, mode: FileMode = fmRead,
else: result.size = fileSize.int else: result.size = fileSize.int
else: else:
template fail(errCode: TOSErrorCode, msg: expr) = template fail(errCode: OSErrorCode, msg: expr) =
rollback() rollback()
if result.handle != 0: discard close(result.handle) if result.handle != 0: discard close(result.handle)
osError(errCode) raiseOSError(errCode)
var flags = if readonly: O_RDONLY else: O_RDWR var flags = if readonly: O_RDONLY else: O_RDWR
@ -214,12 +215,12 @@ proc open*(filename: string, mode: FileMode = fmRead,
if result.mem == cast[pointer](MAP_FAILED): if result.mem == cast[pointer](MAP_FAILED):
fail(osLastError(), "file mapping failed") fail(osLastError(), "file mapping failed")
proc close*(f: var TMemFile) = proc close*(f: var MemFile) =
## closes the memory mapped file `f`. All changes are written back to the ## closes the memory mapped file `f`. All changes are written back to the
## file system, if `f` was opened with write access. ## file system, if `f` was opened with write access.
var error = false var error = false
var lastErr: TOSErrorCode var lastErr: OSErrorCode
when defined(windows): when defined(windows):
if f.fHandle != INVALID_HANDLE_VALUE: if f.fHandle != INVALID_HANDLE_VALUE:
@ -242,5 +243,5 @@ proc close*(f: var TMemFile) =
else: else:
f.handle = 0 f.handle = 0
if error: osError(lastErr) if error: raiseOSError(lastErr)

View file

@ -1,24 +1,26 @@
import unsigned import unsigned
type type
TMersenneTwister* = object MersenneTwister* = object
mt: array[0..623, uint32] mt: array[0..623, uint32]
index: int index: int
proc newMersenneTwister*(seed: int): TMersenneTwister = {.deprecated: [TMersenneTwister: MersenneTwister].}
proc newMersenneTwister*(seed: int): MersenneTwister =
result.index = 0 result.index = 0
result.mt[0]= uint32(seed) result.mt[0]= uint32(seed)
for i in 1..623'u32: for i in 1..623'u32:
result.mt[i]= (0x6c078965'u32 * (result.mt[i-1] xor (result.mt[i-1] shr 30'u32)) + i) result.mt[i]= (0x6c078965'u32 * (result.mt[i-1] xor (result.mt[i-1] shr 30'u32)) + i)
proc generateNumbers(m: var TMersenneTwister) = proc generateNumbers(m: var MersenneTwister) =
for i in 0..623: for i in 0..623:
var y = (m.mt[i] and 0x80000000'u32) + (m.mt[(i+1) mod 624] and 0x7fffffff'u32) var y = (m.mt[i] and 0x80000000'u32) + (m.mt[(i+1) mod 624] and 0x7fffffff'u32)
m.mt[i] = m.mt[(i+397) mod 624] xor uint32(y shr 1'u32) m.mt[i] = m.mt[(i+397) mod 624] xor uint32(y shr 1'u32)
if (y mod 2'u32) != 0: if (y mod 2'u32) != 0:
m.mt[i] = m.mt[i] xor 0x9908b0df'u32 m.mt[i] = m.mt[i] xor 0x9908b0df'u32
proc getNum*(m: var TMersenneTwister): int = proc getNum*(m: var MersenneTwister): int =
if m.index == 0: if m.index == 0:
generateNumbers(m) generateNumbers(m)
var y = m.mt[m.index] var y = m.mt[m.index]
@ -29,11 +31,9 @@ proc getNum*(m: var TMersenneTwister): int =
m.index = (m.index+1) mod 624 m.index = (m.index+1) mod 624
return int(y) return int(y)
# Test # Test
when isMainModule: when isMainModule:
var mt = newMersenneTwister(2525) var mt = newMersenneTwister(2525)
for i in 0..99: for i in 0..99:
echo mt.getNum echo mt.getNum

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Dominik Picheta # (c) Copyright 2012 Dominik Picheta
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -10,8 +10,10 @@
## This module implements a mimetypes database ## This module implements a mimetypes database
import strtabs import strtabs
type type
TMimeDB* = object MimeDB* = object
mimes: PStringTable mimes: StringTableRef
{.deprecated: [TMimeDB: MimeDB].}
const mimes* = { const mimes* = {
"ez": "application/andrew-inset", "ez": "application/andrew-inset",
@ -489,20 +491,19 @@ const mimes* = {
"vrml": "x-world/x-vrml", "vrml": "x-world/x-vrml",
"wrl": "x-world/x-vrml"} "wrl": "x-world/x-vrml"}
proc newMimetypes*(): TMimeDB = proc newMimetypes*(): MimeDB =
## Creates a new Mimetypes database. The database will contain the most ## Creates a new Mimetypes database. The database will contain the most
## common mimetypes. ## common mimetypes.
result.mimes = mimes.newStringTable() result.mimes = mimes.newStringTable()
proc getMimetype*(mimedb: TMimeDB, ext: string, default = "text/plain"): string = proc getMimetype*(mimedb: MimeDB, ext: string, default = "text/plain"): string =
## Gets mimetype which corresponds to ``ext``. Returns ``default`` if ``ext`` ## Gets mimetype which corresponds to ``ext``. Returns ``default`` if ``ext``
## could not be found. ## could not be found.
result = mimedb.mimes[ext] result = mimedb.mimes[ext]
if result == "": if result == "":
return default return default
proc getExt*(mimedb: TMimeDB, mimetype: string, default = "txt"): string = proc getExt*(mimedb: MimeDB, mimetype: string, default = "txt"): string =
## Gets extension which corresponds to ``mimetype``. Returns ``default`` if ## Gets extension which corresponds to ``mimetype``. Returns ``default`` if
## ``mimetype`` could not be found. Extensions are returned without the ## ``mimetype`` could not be found. Extensions are returned without the
## leading dot. ## leading dot.
@ -511,11 +512,11 @@ proc getExt*(mimedb: TMimeDB, mimetype: string, default = "txt"): string =
if m == mimetype: if m == mimetype:
result = e result = e
proc register*(mimedb: var TMimeDB, ext: string, mimetype: string) = proc register*(mimedb: var MimeDB, ext: string, mimetype: string) =
## Adds ``mimetype`` to the ``mimedb``. ## Adds ``mimetype`` to the ``mimedb``.
mimedb.mimes[ext] = mimetype mimedb.mimes[ext] = mimetype
when isMainModule: when isMainModule:
var m = newMimetypes() var m = newMimetypes()
echo m.getMimetype("mp4") echo m.getMimetype("mp4")
echo m.getExt("text/html") echo m.getExt("text/html")

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2014 Dominik Picheta # (c) Copyright 2014 Dominik Picheta
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -11,7 +11,7 @@
{.deadCodeElim: on.} {.deadCodeElim: on.}
import rawsockets, os, strutils, unsigned, parseutils, times import rawsockets, os, strutils, unsigned, parseutils, times
export TPort, `$`, `==` export Port, `$`, `==`
const useWinVersion = defined(Windows) or defined(nimdoc) const useWinVersion = defined(Windows) or defined(nimdoc)
@ -22,25 +22,25 @@ when defined(ssl):
when defined(ssl): when defined(ssl):
type type
ESSL* = object of ESynch SSLError* = object of Exception
TSSLCVerifyMode* = enum SSLCVerifyMode* = enum
CVerifyNone, CVerifyPeer CVerifyNone, CVerifyPeer
TSSLProtVersion* = enum SSLProtVersion* = enum
protSSLv2, protSSLv3, protTLSv1, protSSLv23 protSSLv2, protSSLv3, protTLSv1, protSSLv23
PSSLContext* = distinct PSSLCTX SSLContext* = distinct PSSLCTX
TSSLAcceptResult* = enum SSLAcceptResult* = enum
AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess
const const
BufferSize*: int = 4000 ## size of a buffered socket's buffer BufferSize*: int = 4000 ## size of a buffered socket's buffer
type type
TSocketImpl* = object ## socket type SocketImpl* = object ## socket type
fd*: TSocketHandle fd*: SocketHandle
case isBuffered*: bool # determines whether this socket is buffered. case isBuffered*: bool # determines whether this socket is buffered.
of true: of true:
buffer*: array[0..BufferSize, char] buffer*: array[0..BufferSize, char]
@ -57,34 +57,40 @@ type
sslPeekChar*: char sslPeekChar*: char
of false: nil of false: nil
PSocket* = ref TSocketImpl Socket* = ref SocketImpl
TSOBool* = enum ## Boolean socket options. SOBool* = enum ## Boolean socket options.
OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive, OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive,
OptOOBInline, OptReuseAddr OptOOBInline, OptReuseAddr
TReadLineResult* = enum ## result for readLineAsync ReadLineResult* = enum ## result for readLineAsync
ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone
ETimeout* = object of ESynch TimeoutError* = object of Exception
TSocketFlags* {.pure.} = enum SocketFlag* {.pure.} = enum
Peek, Peek,
SafeDisconn ## Ensures disconnection exceptions (ECONNRESET, EPIPE etc) are not thrown. SafeDisconn ## Ensures disconnection exceptions (ECONNRESET, EPIPE etc) are not thrown.
proc isDisconnectionError*(flags: set[TSocketFlags], {.deprecated: [TSocketFlags: SocketFlag, ETimeout: TimeoutError,
lastError: TOSErrorCode): bool = TReadLineResult: ReadLineResult, TSOBool: SOBool, PSocket: Socket,
TSocketImpl: SocketImpl, ESSL: SSLError, TSSLCVerifyMode: SSLCVerifyMode,
TSSLProtVersion: SSLProtVersion, PSSLContext: SSLContext,
TSSLAcceptResult: SSLAcceptResult].}
proc isDisconnectionError*(flags: set[SocketFlag],
lastError: OSErrorCode): bool =
## Determines whether ``lastError`` is a disconnection error. Only does this ## Determines whether ``lastError`` is a disconnection error. Only does this
## if flags contains ``SafeDisconn``. ## if flags contains ``SafeDisconn``.
when useWinVersion: when useWinVersion:
TSocketFlags.SafeDisconn in flags and SocketFlag.SafeDisconn in flags and
lastError.int32 in {WSAECONNRESET, WSAECONNABORTED, WSAENETRESET, lastError.int32 in {WSAECONNRESET, WSAECONNABORTED, WSAENETRESET,
WSAEDISCON, ERROR_NETNAME_DELETED} WSAEDISCON, ERROR_NETNAME_DELETED}
else: else:
TSocketFlags.SafeDisconn in flags and SocketFlag.SafeDisconn in flags and
lastError.int32 in {ECONNRESET, EPIPE, ENETRESET} lastError.int32 in {ECONNRESET, EPIPE, ENETRESET}
proc toOSFlags*(socketFlags: set[TSocketFlags]): cint = proc toOSFlags*(socketFlags: set[SocketFlag]): cint =
## Converts the flags into the underlying OS representation. ## Converts the flags into the underlying OS representation.
for f in socketFlags: for f in socketFlags:
case f case f

View file

@ -1,13 +1,13 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 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.
# #
## Profiling support for Nimrod. This is an embedded profiler that requires ## Profiling support for Nim. This is an embedded profiler that requires
## ``--profiler:on``. You only need to import this module to get a profiling ## ``--profiler:on``. You only need to import this module to get a profiling
## report at program exit. ## report at program exit.
@ -64,7 +64,7 @@ when withThreads:
var var
profilingLock: TLock profilingLock: TLock
InitLock profilingLock initLock profilingLock
proc hookAux(st: TStackTrace, costs: int) = proc hookAux(st: TStackTrace, costs: int) =
# this is quite performance sensitive! # this is quite performance sensitive!

View file

@ -1,16 +1,17 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2013 Robert Persson # (c) Copyright 2013 Robert Persson
# #
# 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.
# #
type OneVarFunction* = proc (x: float): float
type TOneVarFunction* =proc (x:float):float {.deprecated: [TOneVarFunction: OneVarFunction].}
proc brent*(xmin,xmax:float ,function:TOneVarFunction, tol:float,maxiter=1000): proc brent*(xmin,xmax:float, function:OneVarFunction, tol:float,maxiter=1000):
tuple[rootx, rooty: float, success: bool]= tuple[rootx, rooty: float, success: bool]=
## Searches `function` for a root between `xmin` and `xmax` ## Searches `function` for a root between `xmin` and `xmax`
## using brents method. If the function value at `xmin`and `xmax` has the ## using brents method. If the function value at `xmin`and `xmax` has the

View file

@ -1,13 +1,13 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2013 Andreas Rumpf # (c) Copyright 2013 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.
# #
## Nimrod OID support. An OID is a global ID that consists of a timestamp, ## Nim OID support. An OID is a global ID that consists of a timestamp,
## a unique counter and a random value. This combination should suffice to ## a unique counter and a random value. This combination should suffice to
## produce a globally distributed unique ID. This implementation was extracted ## produce a globally distributed unique ID. This implementation was extracted
## from the Mongodb interface and it thus binary compatible with a Mongo OID. ## from the Mongodb interface and it thus binary compatible with a Mongo OID.
@ -18,11 +18,13 @@
import times, endians import times, endians
type type
Toid* {.pure, final.} = object ## an OID Oid* = object ## an OID
time: int32 ## time: int32 ##
fuzz: int32 ## fuzz: int32 ##
count: int32 ## count: int32 ##
{.deprecated: [Toid: Oid].}
proc hexbyte*(hex: char): int = proc hexbyte*(hex: char): int =
case hex case hex
of '0'..'9': result = (ord(hex) - ord('0')) of '0'..'9': result = (ord(hex) - ord('0'))
@ -30,7 +32,7 @@ proc hexbyte*(hex: char): int =
of 'A'..'F': result = (ord(hex) - ord('A') + 10) of 'A'..'F': result = (ord(hex) - ord('A') + 10)
else: discard else: discard
proc parseOid*(str: cstring): TOid = proc parseOid*(str: cstring): Oid =
## parses an OID. ## parses an OID.
var bytes = cast[cstring](addr(result.time)) var bytes = cast[cstring](addr(result.time))
var i = 0 var i = 0
@ -38,7 +40,7 @@ proc parseOid*(str: cstring): TOid =
bytes[i] = chr((hexbyte(str[2 * i]) shl 4) or hexbyte(str[2 * i + 1])) bytes[i] = chr((hexbyte(str[2 * i]) shl 4) or hexbyte(str[2 * i + 1]))
inc(i) inc(i)
proc oidToString*(oid: TOid, str: cstring) = proc oidToString*(oid: Oid, str: cstring) =
const hex = "0123456789abcdef" const hex = "0123456789abcdef"
# work around a compiler bug: # work around a compiler bug:
var str = str var str = str
@ -52,7 +54,7 @@ proc oidToString*(oid: TOid, str: cstring) =
inc(i) inc(i)
str[24] = '\0' str[24] = '\0'
proc `$`*(oid: TOid): string = proc `$`*(oid: Oid): string =
result = newString(25) result = newString(25)
oidToString(oid, result) oidToString(oid, result)
@ -60,7 +62,7 @@ var
incr: int incr: int
fuzz: int32 fuzz: int32
proc genOid*(): TOid = proc genOid*(): Oid =
## generates a new OID. ## generates a new OID.
proc rand(): cint {.importc: "rand", header: "<stdlib.h>", nodecl.} proc rand(): cint {.importc: "rand", header: "<stdlib.h>", nodecl.}
proc gettime(dummy: ptr cint): cint {.importc: "time", header: "<time.h>".} proc gettime(dummy: ptr cint): cint {.importc: "time", header: "<time.h>".}
@ -79,12 +81,12 @@ proc genOid*(): TOid =
result.fuzz = fuzz result.fuzz = fuzz
bigEndian32(addr result.count, addr(i)) bigEndian32(addr result.count, addr(i))
proc generatedTime*(oid: TOid): TTime = proc generatedTime*(oid: Oid): Time =
## returns the generated timestamp of the OID. ## returns the generated timestamp of the OID.
var tmp: int32 var tmp: int32
var dummy = oid.time var dummy = oid.time
bigEndian32(addr(tmp), addr(dummy)) bigEndian32(addr(tmp), addr(dummy))
result = TTime(tmp) result = Time(tmp)
when isMainModule: when isMainModule:
let xo = genOID() let xo = genOID()

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2014 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -29,18 +29,23 @@ else:
include "system/ansi_c" include "system/ansi_c"
type type
FReadEnv* = object of ReadIOEffect ## effect that denotes a read ReadEnvEffect* = object of ReadIOEffect ## effect that denotes a read
## from an environment variable ## from an environment variable
FWriteEnv* = object of WriteIOEffect ## effect that denotes a write WriteEnvEffect* = object of WriteIOEffect ## effect that denotes a write
## to an environment variable ## to an environment variable
FReadDir* = object of ReadIOEffect ## effect that denotes a write operation to ReadDirEffect* = object of ReadIOEffect ## effect that denotes a write
## the directory structure ## operation to the directory structure
FWriteDir* = object of WriteIOEffect ## effect that denotes a write operation to WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write operation to
## the directory structure ## the directory structure
TOSErrorCode* = distinct int32 ## Specifies an OS Error Code. OSErrorCode* = distinct int32 ## Specifies an OS Error Code.
{.deprecated: [FReadEnv: ReadEnvEffect, FWriteEnv: WriteEnvEffect,
FReadDir: ReadDirEffect,
FWriteDir: WriteDirEffect,
TOSErrorCode: OSErrorCode
].}
const const
doslike = defined(windows) or defined(OS2) or defined(DOS) doslike = defined(windows) or defined(OS2) or defined(DOS)
# DOS-like filesystem # DOS-like filesystem
@ -204,7 +209,8 @@ proc osErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} =
result = $os.strerror(errno) result = $os.strerror(errno)
{.push warning[deprecated]: off.} {.push warning[deprecated]: off.}
proc osError*(msg: string = "") {.noinline, rtl, extern: "nos$1", deprecated.} = proc raiseOSError*(msg: string = "") {.noinline, rtl, extern: "nos$1",
deprecated.} =
## raises an EOS exception with the given message ``msg``. ## raises an EOS exception with the given message ``msg``.
## If ``msg == ""``, the operating system's error flag ## If ``msg == ""``, the operating system's error flag
## (``errno``) is converted to a readable error message. On Windows ## (``errno``) is converted to a readable error message. On Windows
@ -219,10 +225,12 @@ proc osError*(msg: string = "") {.noinline, rtl, extern: "nos$1", deprecated.} =
raise newException(OSError, msg) raise newException(OSError, msg)
{.pop.} {.pop.}
proc `==`*(err1, err2: TOSErrorCode): bool {.borrow.} {.deprecated: [osError: raiseOSError].}
proc `$`*(err: TOSErrorCode): string {.borrow.}
proc osErrorMsg*(errorCode: TOSErrorCode): string = proc `==`*(err1, err2: OSErrorCode): bool {.borrow.}
proc `$`*(err: OSErrorCode): string {.borrow.}
proc osErrorMsg*(errorCode: OSErrorCode): string =
## Converts an OS error code into a human readable string. ## Converts an OS error code into a human readable string.
## ##
## The error code can be retrieved using the ``OSLastError`` proc. ## The error code can be retrieved using the ``OSLastError`` proc.
@ -249,10 +257,10 @@ proc osErrorMsg*(errorCode: TOSErrorCode): string =
result = $msgbuf result = $msgbuf
if msgbuf != nil: localFree(msgbuf) if msgbuf != nil: localFree(msgbuf)
else: else:
if errorCode != TOSErrorCode(0'i32): if errorCode != OSErrorCode(0'i32):
result = $os.strerror(errorCode.int32) result = $os.strerror(errorCode.int32)
proc osError*(errorCode: TOSErrorCode) = proc raiseOSError*(errorCode: OSErrorCode) =
## Raises an ``EOS`` exception. The ``errorCode`` will determine the ## Raises an ``EOS`` exception. The ``errorCode`` will determine the
## message, ``OSErrorMsg`` will be used to get this message. ## message, ``OSErrorMsg`` will be used to get this message.
## ##
@ -268,7 +276,7 @@ proc osError*(errorCode: TOSErrorCode) =
raise e raise e
{.push stackTrace:off.} {.push stackTrace:off.}
proc osLastError*(): TOSErrorCode = proc osLastError*(): OSErrorCode =
## Retrieves the last operating system error code. ## Retrieves the last operating system error code.
## ##
## This procedure is useful in the event when an OS call fails. In that case ## This procedure is useful in the event when an OS call fails. In that case
@ -285,7 +293,7 @@ proc osLastError*(): TOSErrorCode =
when defined(windows): when defined(windows):
result = TOSErrorCode(getLastError()) result = TOSErrorCode(getLastError())
else: else:
result = TOSErrorCode(errno) result = OSErrorCode(errno)
{.pop.} {.pop.}
proc unixToNativePath*(path: string, drive=""): string {. proc unixToNativePath*(path: string, drive=""): string {.
@ -371,7 +379,7 @@ when defined(windows):
f.cFileName[1].int == dot and f.cFileName[2].int == 0) f.cFileName[1].int == dot and f.cFileName[2].int == 0)
proc existsFile*(filename: string): bool {.rtl, extern: "nos$1", proc existsFile*(filename: string): bool {.rtl, extern: "nos$1",
tags: [FReadDir].} = tags: [ReadDirEffect].} =
## Returns true if the file exists, false otherwise. ## Returns true if the file exists, false otherwise.
when defined(windows): when defined(windows):
when useWinUnicode: when useWinUnicode:
@ -384,7 +392,7 @@ proc existsFile*(filename: string): bool {.rtl, extern: "nos$1",
var res: TStat var res: TStat
return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode) return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode)
proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [FReadDir].} = proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect].} =
## Returns true iff the directory `dir` exists. If `dir` is a file, false ## Returns true iff the directory `dir` exists. If `dir` is a file, false
## is returned. ## is returned.
when defined(windows): when defined(windows):
@ -399,7 +407,7 @@ proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [FReadDir].} =
return stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode) return stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode)
proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1", proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
tags: [FReadDir].} = tags: [ReadDirEffect].} =
## Returns true iff the symlink `link` exists. Will return true ## Returns true iff the symlink `link` exists. Will return true
## regardless of whether the link points to a directory or file. ## regardless of whether the link points to a directory or file.
when defined(windows): when defined(windows):
@ -701,7 +709,7 @@ proc extractFilename*(path: string): string {.
result = splitPath(path).tail result = splitPath(path).tail
proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
tags: [FReadDir].} = tags: [ReadDirEffect].} =
## 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.
when defined(windows): when defined(windows):
const bufsize = 3072'i32 const bufsize = 3072'i32
@ -800,7 +808,7 @@ when defined(Windows):
) )
proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1", proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
tags: [FReadDir].} = tags: [ReadDirEffect].} =
## Returns True if both pathname arguments refer to the same physical ## Returns True if both pathname arguments refer to the same physical
## file or directory. Raises an exception if any of the files does not ## file or directory. Raises an exception if any of the files does not
## exist or information about it can not be obtained. ## exist or information about it can not be obtained.
@ -883,7 +891,7 @@ type
fpOthersRead ## read access for others fpOthersRead ## read access for others
proc getFilePermissions*(filename: string): set[TFilePermission] {. proc getFilePermissions*(filename: string): set[TFilePermission] {.
rtl, extern: "nos$1", tags: [FReadDir].} = rtl, extern: "nos$1", tags: [ReadDirEffect].} =
## retrieves file permissions for `filename`. `OSError` is raised in case of ## retrieves file permissions for `filename`. `OSError` is raised in case of
## an error. On Windows, only the ``readonly`` flag is checked, every other ## an error. On Windows, only the ``readonly`` flag is checked, every other
## permission is available in any case. ## permission is available in any case.
@ -915,7 +923,7 @@ proc getFilePermissions*(filename: string): set[TFilePermission] {.
result = {fpUserExec..fpOthersRead} result = {fpUserExec..fpOthersRead}
proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {. proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {.
rtl, extern: "nos$1", tags: [FWriteDir].} = rtl, extern: "nos$1", tags: [WriteDirEffect].} =
## sets the file permissions for `filename`. `OSError` is raised in case of ## sets the file permissions for `filename`. `OSError` is raised in case of
## an error. On Windows, only the ``readonly`` flag is changed, depending on ## an error. On Windows, only the ``readonly`` flag is changed, depending on
## ``fpUserWrite``. ## ``fpUserWrite``.
@ -1014,7 +1022,7 @@ when defined(Windows):
template setFileAttributes(file, attrs: expr): expr {.immediate.} = template setFileAttributes(file, attrs: expr): expr {.immediate.} =
setFileAttributesA(file, attrs) setFileAttributesA(file, attrs)
proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} = proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} =
## Removes the `file`. If this fails, `EOS` is raised. This does not fail ## Removes the `file`. If this fails, `EOS` is raised. This does not fail
## if the file never existed in the first place. ## if the file never existed in the first place.
## On Windows, ignores the read-only attribute. ## On Windows, ignores the read-only attribute.
@ -1076,7 +1084,7 @@ when defined(windows):
while true: while true:
var eend = strEnd(e) var eend = strEnd(e)
add(environment, $e) add(environment, $e)
e = cast[WideCString](cast[ByteAddress](eend)+2) e = cast[WideCString](cast[TAddress](eend)+2)
if eend[1].int == 0: break if eend[1].int == 0: break
discard freeEnvironmentStringsW(env) discard freeEnvironmentStringsW(env)
else: else:
@ -1130,7 +1138,7 @@ proc findEnvVar(key: string): int =
if startsWith(environment[i], temp): return i if startsWith(environment[i], temp): return i
return -1 return -1
proc getEnv*(key: string): TaintedString {.tags: [FReadEnv].} = proc getEnv*(key: string): TaintedString {.tags: [ReadEnvEffect].} =
## Returns the value of the `environment variable`:idx: named `key`. ## Returns the value of the `environment variable`:idx: named `key`.
## ##
## If the variable does not exist, "" is returned. To distinguish ## If the variable does not exist, "" is returned. To distinguish
@ -1144,13 +1152,13 @@ proc getEnv*(key: string): TaintedString {.tags: [FReadEnv].} =
if env == nil: return TaintedString("") if env == nil: return TaintedString("")
result = TaintedString($env) result = TaintedString($env)
proc existsEnv*(key: string): bool {.tags: [FReadEnv].} = proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
## Checks whether the environment variable named `key` exists. ## Checks whether the environment variable named `key` exists.
## Returns true if it exists, false otherwise. ## Returns true if it exists, false otherwise.
if c_getenv(key) != nil: return true if c_getenv(key) != nil: return true
else: return findEnvVar(key) >= 0 else: return findEnvVar(key) >= 0
proc putEnv*(key, val: string) {.tags: [FWriteEnv].} = proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
## Sets the value of the `environment variable`:idx: named `key` to `val`. ## Sets the value of the `environment variable`:idx: named `key` to `val`.
## If an error occurs, `EInvalidEnvVar` is raised. ## If an error occurs, `EInvalidEnvVar` is raised.
@ -1175,7 +1183,7 @@ proc putEnv*(key, val: string) {.tags: [FWriteEnv].} =
else: else:
if setEnvironmentVariableA(key, val) == 0'i32: osError(osLastError()) if setEnvironmentVariableA(key, val) == 0'i32: osError(osLastError())
iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [FReadEnv].} = iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} =
## Iterate over all `environments variables`:idx:. In the first component ## Iterate over all `environments variables`:idx:. In the first component
## of the tuple is the name of the current variable stored, in the second ## of the tuple is the name of the current variable stored, in the second
## its value. ## its value.
@ -1185,7 +1193,7 @@ iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [FReadEnv].} =
yield (TaintedString(substr(environment[i], 0, p-1)), yield (TaintedString(substr(environment[i], 0, p-1)),
TaintedString(substr(environment[i], p+1))) TaintedString(substr(environment[i], p+1)))
iterator walkFiles*(pattern: string): string {.tags: [FReadDir].} = iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect].} =
## Iterate over all the files that match the `pattern`. On POSIX this uses ## Iterate over all the files that match the `pattern`. On POSIX this uses
## the `glob`:idx: call. ## the `glob`:idx: call.
## ##
@ -1225,7 +1233,7 @@ type
pcLinkToDir ## path refers to a symbolic link to a directory pcLinkToDir ## path refers to a symbolic link to a directory
iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] {. iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] {.
tags: [FReadDir].} = tags: [ReadDirEffect].} =
## walks over the directory `dir` and yields for each directory or file in ## walks over the directory `dir` and yields for each directory or file in
## `dir`. The component type and full path for each item is returned. ## `dir`. The component type and full path for each item is returned.
## Walking is not recursive. ## Walking is not recursive.
@ -1278,7 +1286,7 @@ iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] {.
discard closedir(d) discard closedir(d)
iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string {. iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string {.
tags: [FReadDir].} = tags: [ReadDirEffect].} =
## walks over the directory `dir` and yields for each file in `dir`. The ## walks over the directory `dir` and yields for each file in `dir`. The
## full path for each file is returned. ## full path for each file is returned.
## **Warning**: ## **Warning**:
@ -1318,7 +1326,7 @@ proc rawRemoveDir(dir: string) =
if rmdir(dir) != 0'i32 and errno != ENOENT: osError(osLastError()) if rmdir(dir) != 0'i32 and errno != ENOENT: osError(osLastError())
proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [ proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [
FWriteDir, FReadDir].} = WriteDirEffect, ReadDirEffect].} =
## Removes the directory `dir` including all subdirectories and files ## Removes the directory `dir` including all subdirectories and files
## in `dir` (recursively). ## in `dir` (recursively).
## ##
@ -1345,7 +1353,7 @@ proc rawCreateDir(dir: string) =
if res == 0'i32 and getLastError() != 183'i32: if res == 0'i32 and getLastError() != 183'i32:
osError(osLastError()) osError(osLastError())
proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} = proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} =
## Creates the `directory`:idx: `dir`. ## Creates the `directory`:idx: `dir`.
## ##
## The directory may contain several subdirectories that do not exist yet. ## The directory may contain several subdirectories that do not exist yet.
@ -1568,7 +1576,7 @@ proc copyDirWithPermissions*(source, dest: string,
proc inclFilePermissions*(filename: string, proc inclFilePermissions*(filename: string,
permissions: set[TFilePermission]) {. permissions: set[TFilePermission]) {.
rtl, extern: "nos$1", tags: [FReadDir, FWriteDir].} = rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect].} =
## a convenience procedure for: ## a convenience procedure for:
## ##
## .. code-block:: nimrod ## .. code-block:: nimrod
@ -1577,14 +1585,14 @@ proc inclFilePermissions*(filename: string,
proc exclFilePermissions*(filename: string, proc exclFilePermissions*(filename: string,
permissions: set[TFilePermission]) {. permissions: set[TFilePermission]) {.
rtl, extern: "nos$1", tags: [FReadDir, FWriteDir].} = rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect].} =
## a convenience procedure for: ## a convenience procedure for:
## ##
## .. code-block:: nimrod ## .. code-block:: nimrod
## setFilePermissions(filename, getFilePermissions(filename)-permissions) ## setFilePermissions(filename, getFilePermissions(filename)-permissions)
setFilePermissions(filename, getFilePermissions(filename)-permissions) setFilePermissions(filename, getFilePermissions(filename)-permissions)
proc getHomeDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} = proc getHomeDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect].} =
## Returns the home directory of the current user. ## Returns the home directory of the current user.
## ##
## This proc is wrapped by the expandTilde proc for the convenience of ## This proc is wrapped by the expandTilde proc for the convenience of
@ -1592,12 +1600,12 @@ proc getHomeDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} =
when defined(windows): return string(getEnv("USERPROFILE")) & "\\" when defined(windows): return string(getEnv("USERPROFILE")) & "\\"
else: return string(getEnv("HOME")) & "/" else: return string(getEnv("HOME")) & "/"
proc getConfigDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} = proc getConfigDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect].} =
## Returns the config directory of the current user for applications. ## Returns the config directory of the current user for applications.
when defined(windows): return string(getEnv("APPDATA")) & "\\" when defined(windows): return string(getEnv("APPDATA")) & "\\"
else: return string(getEnv("HOME")) & "/.config/" else: return string(getEnv("HOME")) & "/.config/"
proc getTempDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} = proc getTempDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect].} =
## Returns the temporary directory of the current user for applications to ## Returns the temporary directory of the current user for applications to
## save temporary files in. ## save temporary files in.
when defined(windows): return string(getEnv("TEMP")) & "\\" when defined(windows): return string(getEnv("TEMP")) & "\\"
@ -1605,7 +1613,7 @@ proc getTempDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} =
when defined(nimdoc): when defined(nimdoc):
# Common forward declaration docstring block for parameter retrieval procs. # Common forward declaration docstring block for parameter retrieval procs.
proc paramCount*(): int {.tags: [FReadIO].} = proc paramCount*(): int {.tags: [ReadIOEffect].} =
## Returns the number of `command line arguments`:idx: given to the ## Returns the number of `command line arguments`:idx: given to the
## application. ## application.
## ##
@ -1625,7 +1633,7 @@ when defined(nimdoc):
## else: ## else:
## # Do something else! ## # Do something else!
proc paramStr*(i: int): TaintedString {.tags: [FReadIO].} = proc paramStr*(i: int): TaintedString {.tags: [ReadIOEffect].} =
## Returns the `i`-th `command line argument`:idx: given to the application. ## Returns the `i`-th `command line argument`:idx: given to the application.
## ##
## `i` should be in the range `1..paramCount()`, the `EInvalidIndex` ## `i` should be in the range `1..paramCount()`, the `EInvalidIndex`
@ -1826,7 +1834,7 @@ proc getFileSize*(file: string): BiggestInt {.rtl, extern: "nos$1",
close(f) close(f)
else: osError(osLastError()) else: osError(osLastError())
proc findExe*(exe: string): string {.tags: [FReadDir, FReadEnv].} = proc findExe*(exe: string): string {.tags: [ReadDirEffect, ReadEnvEffect].} =
## Searches for `exe` in the current working directory and then ## Searches for `exe` in the current working directory and then
## in directories listed in the ``PATH`` environment variable. ## in directories listed in the ``PATH`` environment variable.
## Returns "" if the `exe` cannot be found. On DOS-like platforms, `exe` ## Returns "" if the `exe` cannot be found. On DOS-like platforms, `exe`
@ -1854,7 +1862,6 @@ proc expandTilde*(path: string): string =
## let configFile = expandTilde("~" / "appname.cfg") ## let configFile = expandTilde("~" / "appname.cfg")
## echo configFile ## echo configFile
## # --> C:\Users\amber\appname.cfg ## # --> C:\Users\amber\appname.cfg
if len(path) > 1 and path[0] == '~' and (path[1] == '/' or path[1] == '\\'): if len(path) > 1 and path[0] == '~' and (path[1] == '/' or path[1] == '\\'):
result = getHomeDir() / path[2..len(path)-1] result = getHomeDir() / path[2..len(path)-1]
else: else:
@ -1961,7 +1968,7 @@ proc getFileInfo*(handle: FileHandle): FileInfo =
rawToFormalFileInfo(rawInfo, result) rawToFormalFileInfo(rawInfo, result)
proc getFileInfo*(file: File): FileInfo = proc getFileInfo*(file: File): FileInfo =
result = getFileInfo(file.fileHandle()) result = getFileInfo(file.getFileHandle())
proc getFileInfo*(path: string, followSymlink = true): FileInfo = proc getFileInfo*(path: string, followSymlink = true): FileInfo =
## Retrieves file information for the file object pointed to by `path`. ## Retrieves file information for the file object pointed to by `path`.

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2014 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -24,7 +24,7 @@ when defined(linux):
import linux import linux
type type
TProcess = object of RootObj ProcessObj = object of RootObj
when defined(windows): when defined(windows):
fProcessHandle: THandle fProcessHandle: THandle
inHandle, outHandle, errHandle: FileHandle inHandle, outHandle, errHandle: FileHandle
@ -35,9 +35,9 @@ type
id: TPid id: TPid
exitCode: cint exitCode: cint
PProcess* = ref TProcess ## represents an operating system process Process* = ref ProcessObj ## represents an operating system process
TProcessOption* = enum ## options that can be passed `startProcess` ProcessOption* = enum ## options that can be passed `startProcess`
poEchoCmd, ## echo the command before execution poEchoCmd, ## echo the command before execution
poUsePath, ## Asks system to search for executable using PATH environment poUsePath, ## Asks system to search for executable using PATH environment
## variable. ## variable.
@ -47,6 +47,9 @@ type
poStdErrToStdOut, ## merge stdout and stderr to the stdout stream poStdErrToStdOut, ## merge stdout and stderr to the stdout stream
poParentStreams ## use the parent's streams poParentStreams ## use the parent's streams
{.deprecated: [TProcess: ProcessObj, PProcess: Process,
TProcessOption: ProcessOption].}
const poUseShell* {.deprecated.} = poUsePath const poUseShell* {.deprecated.} = poUsePath
## Deprecated alias for poUsePath. ## Deprecated alias for poUsePath.
@ -125,7 +128,7 @@ proc startProcess*(command: string,
args: openArray[string] = [], args: openArray[string] = [],
env: PStringTable = nil, env: PStringTable = nil,
options: set[TProcessOption] = {poStdErrToStdOut}): options: set[TProcessOption] = {poStdErrToStdOut}):
PProcess {.rtl, extern: "nosp$1", tags: [ExecIOEffect, FReadEnv].} PProcess {.rtl, extern: "nosp$1", tags: [ExecIOEffect, ReadEnvEffect].}
## Starts a process. `Command` is the executable file, `workingDir` is the ## Starts a process. `Command` is the executable file, `workingDir` is the
## process's working directory. If ``workingDir == ""`` the current directory ## process's working directory. If ``workingDir == ""`` the current directory
## 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
@ -150,7 +153,7 @@ proc startProcess*(command: string,
proc startCmd*(command: string, options: set[TProcessOption] = { proc startCmd*(command: string, options: set[TProcessOption] = {
poStdErrToStdOut, poUsePath}): PProcess {. poStdErrToStdOut, poUsePath}): PProcess {.
tags: [ExecIOEffect, FReadEnv], deprecated.} = tags: [ExecIOEffect, ReadEnvEffect], deprecated.} =
## Deprecated - use `startProcess` directly. ## Deprecated - use `startProcess` directly.
result = startProcess(command=command, options=options + {poEvalCommand}) result = startProcess(command=command, options=options + {poEvalCommand})
@ -233,7 +236,7 @@ proc countProcessors*(): int {.rtl, extern: "nosp$1".} =
proc execProcesses*(cmds: openArray[string], proc execProcesses*(cmds: openArray[string],
options = {poStdErrToStdOut, poParentStreams}, options = {poStdErrToStdOut, poParentStreams},
n = countProcessors()): int {.rtl, extern: "nosp$1", n = countProcessors()): int {.rtl, extern: "nosp$1",
tags: [ExecIOEffect, TimeEffect, FReadEnv]} = tags: [ExecIOEffect, TimeEffect, ReadEnvEffect]} =
## executes the commands `cmds` in parallel. Creates `n` processes ## executes the commands `cmds` in parallel. Creates `n` processes
## that execute in parallel. The highest return value of all processes ## that execute in parallel. The highest return value of all processes
## is returned. ## is returned.
@ -584,12 +587,12 @@ elif not defined(useNimRtl):
when not defined(useFork): when not defined(useFork):
proc startProcessAuxSpawn(data: TStartProcessData): TPid {. proc startProcessAuxSpawn(data: TStartProcessData): TPid {.
tags: [ExecIOEffect, FReadEnv], gcsafe.} tags: [ExecIOEffect, ReadEnvEffect], gcsafe.}
proc startProcessAuxFork(data: TStartProcessData): TPid {. proc startProcessAuxFork(data: TStartProcessData): TPid {.
tags: [ExecIOEffect, FReadEnv], gcsafe.} tags: [ExecIOEffect, ReadEnvEffect], gcsafe.}
{.push stacktrace: off, profiler: off.} {.push stacktrace: off, profiler: off.}
proc startProcessAfterFork(data: ptr TStartProcessData) {. proc startProcessAfterFork(data: ptr TStartProcessData) {.
tags: [ExecIOEffect, FReadEnv], cdecl, gcsafe.} tags: [ExecIOEffect, ReadEnvEffect], cdecl, gcsafe.}
{.pop.} {.pop.}
proc startProcess(command: string, proc startProcess(command: string,

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2010 Andreas Rumpf # (c) Copyright 2010 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -20,24 +20,24 @@
## The file ``examples/parsecfgex.nim`` demonstrates how to use the ## The file ``examples/parsecfgex.nim`` demonstrates how to use the
## configuration file parser: ## configuration file parser:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## :file: examples/parsecfgex.nim ## :file: examples/parsecfgex.nim
import import
hashes, strutils, lexbase, streams hashes, strutils, lexbase, streams
include "system/inclrtl" include "system/inclrtl"
type type
TCfgEventKind* = enum ## enumeration of all events that may occur when parsing CfgEventKind* = enum ## enumeration of all events that may occur when parsing
cfgEof, ## end of file reached cfgEof, ## end of file reached
cfgSectionStart, ## a ``[section]`` has been parsed cfgSectionStart, ## a ``[section]`` has been parsed
cfgKeyValuePair, ## a ``key=value`` pair has been detected cfgKeyValuePair, ## a ``key=value`` pair has been detected
cfgOption, ## a ``--key=value`` command line option cfgOption, ## a ``--key=value`` command line option
cfgError ## an error ocurred during parsing cfgError ## an error ocurred during parsing
TCfgEvent* = object of TObject ## describes a parsing event CfgEvent* = object of RootObj ## describes a parsing event
case kind*: TCfgEventKind ## the kind of the event case kind*: TCfgEventKind ## the kind of the event
of cfgEof: nil of cfgEof: nil
of cfgSectionStart: of cfgSectionStart:
@ -53,28 +53,30 @@ type
msg*: string ## contains the error message. No exceptions msg*: string ## contains the error message. No exceptions
## are thrown if a parse error occurs. ## are thrown if a parse error occurs.
TTokKind = enum TokKind = enum
tkInvalid, tkEof, tkInvalid, tkEof,
tkSymbol, tkEquals, tkColon, tkBracketLe, tkBracketRi, tkDashDash tkSymbol, tkEquals, tkColon, tkBracketLe, tkBracketRi, tkDashDash
TToken {.final.} = object # a token Token = object # a token
kind: TTokKind # the type of the token kind: TokKind # the type of the token
literal: string # the parsed (string) literal literal: string # the parsed (string) literal
TCfgParser* = object of TBaseLexer ## the parser object. CfgParser* = object of BaseLexer ## the parser object.
tok: TToken tok: Token
filename: string filename: string
{.deprecated: [TCfgEventKind: CfgEventKind, TCfgEvent: CfgEvent,
TTokKind: TokKind, TToken: Token, TCfgParser: CfgParser].}
# implementation # implementation
const const
SymChars: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF', '.', SymChars: CharSet = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF', '.',
'/', '\\'} '/', '\\'}
proc rawGetTok(c: var TCfgParser, tok: var TToken) {.gcsafe.} proc rawGetTok(c: var CfgParser, tok: var Token) {.gcsafe.}
proc open*(c: var TCfgParser, input: PStream, filename: string, proc open*(c: var CfgParser, input: PStream, filename: string,
lineOffset = 0) {. lineOffset = 0) {.rtl, extern: "npc$1".} =
rtl, extern: "npc$1".} =
## initializes the parser with an input stream. `Filename` is only used ## initializes the parser with an input stream. `Filename` is only used
## for nice error messages. `lineOffset` can be used to influence the line ## for nice error messages. `lineOffset` can be used to influence the line
## number information in the generated error messages. ## number information in the generated error messages.
@ -85,23 +87,23 @@ proc open*(c: var TCfgParser, input: PStream, filename: string,
inc(c.lineNumber, lineOffset) inc(c.lineNumber, lineOffset)
rawGetTok(c, c.tok) rawGetTok(c, c.tok)
proc close*(c: var TCfgParser) {.rtl, extern: "npc$1".} = proc close*(c: var CfgParser) {.rtl, extern: "npc$1".} =
## closes the parser `c` and its associated input stream. ## closes the parser `c` and its associated input stream.
lexbase.close(c) lexbase.close(c)
proc getColumn*(c: TCfgParser): int {.rtl, extern: "npc$1".} = proc getColumn*(c: CfgParser): int {.rtl, extern: "npc$1".} =
## get the current column the parser has arrived at. ## get the current column the parser has arrived at.
result = getColNumber(c, c.bufpos) result = getColNumber(c, c.bufpos)
proc getLine*(c: TCfgParser): int {.rtl, extern: "npc$1".} = proc getLine*(c: CfgParser): int {.rtl, extern: "npc$1".} =
## get the current line the parser has arrived at. ## get the current line the parser has arrived at.
result = c.lineNumber result = c.lineNumber
proc getFilename*(c: TCfgParser): string {.rtl, extern: "npc$1".} = proc getFilename*(c: CfgParser): string {.rtl, extern: "npc$1".} =
## get the filename of the file that the parser processes. ## get the filename of the file that the parser processes.
result = c.filename result = c.filename
proc handleHexChar(c: var TCfgParser, xi: var int) = proc handleHexChar(c: var CfgParser, xi: var int) =
case c.buf[c.bufpos] case c.buf[c.bufpos]
of '0'..'9': of '0'..'9':
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0')) xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0'))
@ -115,12 +117,12 @@ proc handleHexChar(c: var TCfgParser, xi: var int) =
else: else:
discard discard
proc handleDecChars(c: var TCfgParser, xi: var int) = proc handleDecChars(c: var CfgParser, xi: var int) =
while c.buf[c.bufpos] in {'0'..'9'}: while c.buf[c.bufpos] in {'0'..'9'}:
xi = (xi * 10) + (ord(c.buf[c.bufpos]) - ord('0')) xi = (xi * 10) + (ord(c.buf[c.bufpos]) - ord('0'))
inc(c.bufpos) inc(c.bufpos)
proc getEscapedChar(c: var TCfgParser, tok: var TToken) = proc getEscapedChar(c: var CfgParser, 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':
@ -169,13 +171,13 @@ proc getEscapedChar(c: var TCfgParser, tok: var TToken) =
else: tok.kind = tkInvalid else: tok.kind = tkInvalid
else: tok.kind = tkInvalid else: tok.kind = tkInvalid
proc handleCRLF(c: var TCfgParser, pos: int): int = proc handleCRLF(c: var CfgParser, pos: int): int =
case c.buf[pos] case c.buf[pos]
of '\c': result = lexbase.handleCR(c, pos) of '\c': result = lexbase.handleCR(c, pos)
of '\L': result = lexbase.handleLF(c, pos) of '\L': result = lexbase.handleLF(c, pos)
else: result = pos else: result = pos
proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) = proc getString(c: var CfgParser, tok: var Token, rawMode: bool) =
var pos = c.bufpos + 1 # skip " var pos = c.bufpos + 1 # skip "
var buf = c.buf # put `buf` in a register var buf = c.buf # put `buf` in a register
tok.kind = tkSymbol tok.kind = tkSymbol
@ -221,7 +223,7 @@ proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
inc(pos) inc(pos)
c.bufpos = pos c.bufpos = pos
proc getSymbol(c: var TCfgParser, tok: var TToken) = proc getSymbol(c: var CfgParser, tok: var Token) =
var pos = c.bufpos var pos = c.bufpos
var buf = c.buf var buf = c.buf
while true: while true:
@ -231,7 +233,7 @@ proc getSymbol(c: var TCfgParser, tok: var TToken) =
c.bufpos = pos c.bufpos = pos
tok.kind = tkSymbol tok.kind = tkSymbol
proc skip(c: var TCfgParser) = proc skip(c: var CfgParser) =
var pos = c.bufpos var pos = c.bufpos
var buf = c.buf var buf = c.buf
while true: while true:
@ -247,7 +249,7 @@ proc skip(c: var TCfgParser) =
break # EndOfFile also leaves the loop break # EndOfFile also leaves the loop
c.bufpos = pos c.bufpos = pos
proc rawGetTok(c: var TCfgParser, tok: var TToken) = proc rawGetTok(c: var CfgParser, tok: var Token) =
tok.kind = tkInvalid tok.kind = tkInvalid
setLen(tok.literal, 0) setLen(tok.literal, 0)
skip(c) skip(c)
@ -286,19 +288,19 @@ proc rawGetTok(c: var TCfgParser, tok: var TToken) =
tok.literal = "[EOF]" tok.literal = "[EOF]"
else: getSymbol(c, tok) else: getSymbol(c, tok)
proc errorStr*(c: TCfgParser, msg: string): string {.rtl, extern: "npc$1".} = proc errorStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} =
## returns a properly formated error message containing current line and ## returns a properly formated error message containing current line and
## column information. ## column information.
result = `%`("$1($2, $3) Error: $4", result = `%`("$1($2, $3) Error: $4",
[c.filename, $getLine(c), $getColumn(c), msg]) [c.filename, $getLine(c), $getColumn(c), msg])
proc warningStr*(c: TCfgParser, msg: string): string {.rtl, extern: "npc$1".} = proc warningStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} =
## returns a properly formated warning message containing current line and ## returns a properly formated warning message containing current line and
## column information. ## column information.
result = `%`("$1($2, $3) Warning: $4", result = `%`("$1($2, $3) Warning: $4",
[c.filename, $getLine(c), $getColumn(c), msg]) [c.filename, $getLine(c), $getColumn(c), msg])
proc ignoreMsg*(c: TCfgParser, e: TCfgEvent): string {.rtl, extern: "npc$1".} = proc ignoreMsg*(c: CfgParser, e: CfgEvent): string {.rtl, extern: "npc$1".} =
## returns a properly formated warning message containing that ## returns a properly formated warning message containing that
## an entry is ignored. ## an entry is ignored.
case e.kind case e.kind
@ -309,7 +311,7 @@ proc ignoreMsg*(c: TCfgParser, e: TCfgEvent): string {.rtl, extern: "npc$1".} =
of cfgError: result = e.msg of cfgError: result = e.msg
of cfgEof: result = "" of cfgEof: result = ""
proc getKeyValPair(c: var TCfgParser, kind: TCfgEventKind): TCfgEvent = proc getKeyValPair(c: var CfgParser, kind: CfgEventKind): CfgEvent =
if c.tok.kind == tkSymbol: if c.tok.kind == tkSymbol:
result.kind = kind result.kind = kind
result.key = c.tok.literal result.key = c.tok.literal
@ -329,7 +331,7 @@ proc getKeyValPair(c: var TCfgParser, kind: TCfgEventKind): TCfgEvent =
result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal) result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
rawGetTok(c, c.tok) rawGetTok(c, c.tok)
proc next*(c: var TCfgParser): TCfgEvent {.rtl, extern: "npc$1".} = proc next*(c: var CfgParser): CfgEvent {.rtl, extern: "npc$1".} =
## retrieves the first/next event. This controls the parser. ## retrieves the first/next event. This controls the parser.
case c.tok.kind case c.tok.kind
of tkEof: of tkEof:

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim'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
@ -13,16 +13,16 @@
## Example: How to use the parser ## Example: How to use the parser
## ============================== ## ==============================
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## import os, parsecsv, streams ## import os, parsecsv, streams
## var s = newFileStream(ParamStr(1), fmRead) ## var s = newFileStream(paramStr(1), fmRead)
## if s == nil: quit("cannot open the file" & ParamStr(1)) ## if s == nil: quit("cannot open the file" & paramStr(1))
## var x: TCsvParser ## var x: CsvParser
## open(x, s, ParamStr(1)) ## open(x, s, paramStr(1))
## while readRow(x): ## while readRow(x):
## Echo "new row: " ## echo "new row: "
## for val in items(x.row): ## for val in items(x.row):
## Echo "##", val, "##" ## echo "##", val, "##"
## close(x) ## close(x)
## ##
@ -30,28 +30,30 @@ import
lexbase, streams lexbase, streams
type type
TCsvRow* = seq[string] ## a row in a CSV file CsvRow* = seq[string] ## a row in a CSV file
TCsvParser* = object of TBaseLexer ## the parser object. CsvParser* = object of BaseLexer ## the parser object.
row*: TCsvRow ## the current row row*: CsvRow ## the current row
filename: string filename: string
sep, quote, esc: char sep, quote, esc: char
skipWhite: bool skipWhite: bool
currRow: int currRow: int
EInvalidCsv* = object of EIO ## exception that is raised if CsvError* = object of IOError ## exception that is raised if
## a parsing error occurs ## a parsing error occurs
{.deprecated: [TCsvRow: CsvRow, TCsvParser: CsvParser, EInvalidCsv: CsvError].}
proc raiseEInvalidCsv(filename: string, line, col: int, proc raiseEInvalidCsv(filename: string, line, col: int,
msg: string) {.noreturn.} = msg: string) {.noreturn.} =
var e: ref EInvalidCsv var e: ref CsvError
new(e) new(e)
e.msg = filename & "(" & $line & ", " & $col & ") Error: " & msg e.msg = filename & "(" & $line & ", " & $col & ") Error: " & msg
raise e raise e
proc error(my: TCsvParser, pos: int, msg: string) = proc error(my: CsvParser, pos: int, msg: string) =
raiseEInvalidCsv(my.filename, my.LineNumber, getColNumber(my, pos), msg) raiseEInvalidCsv(my.filename, my.LineNumber, getColNumber(my, pos), msg)
proc open*(my: var TCsvParser, input: PStream, filename: string, proc open*(my: var CsvParser, input: Stream, filename: string,
separator = ',', quote = '"', escape = '\0', separator = ',', quote = '"', escape = '\0',
skipInitialSpace = false) = skipInitialSpace = false) =
## initializes the parser with an input stream. `Filename` is only used ## initializes the parser with an input stream. `Filename` is only used
@ -75,7 +77,7 @@ proc open*(my: var TCsvParser, input: PStream, filename: string,
my.row = @[] my.row = @[]
my.currRow = 0 my.currRow = 0
proc parseField(my: var TCsvParser, a: var string) = proc parseField(my: var CsvParser, a: var string) =
var pos = my.bufpos var pos = my.bufpos
var buf = my.buf var buf = my.buf
if my.skipWhite: if my.skipWhite:
@ -83,7 +85,7 @@ proc parseField(my: var TCsvParser, a: var string) =
setLen(a, 0) # reuse memory setLen(a, 0) # reuse memory
if buf[pos] == my.quote and my.quote != '\0': if buf[pos] == my.quote and my.quote != '\0':
inc(pos) inc(pos)
while true: while true:
var c = buf[pos] var c = buf[pos]
if c == '\0': if c == '\0':
my.bufpos = pos # can continue after exception? my.bufpos = pos # can continue after exception?
@ -121,11 +123,11 @@ proc parseField(my: var TCsvParser, a: var string) =
inc(pos) inc(pos)
my.bufpos = pos my.bufpos = pos
proc processedRows*(my: var TCsvParser): int = proc processedRows*(my: var CsvParser): int =
## returns number of the processed rows ## returns number of the processed rows
return my.currRow return my.currRow
proc readRow*(my: var TCsvParser, columns = 0): bool = proc readRow*(my: var CsvParser, columns = 0): bool =
## reads the next row; if `columns` > 0, it expects the row to have ## reads the next row; if `columns` > 0, it expects the row to have
## exactly this many columns. Returns false if the end of the file ## exactly this many columns. Returns false if the end of the file
## has been encountered else true. ## has been encountered else true.
@ -160,19 +162,19 @@ proc readRow*(my: var TCsvParser, columns = 0): bool =
$col & " columns") $col & " columns")
inc(my.currRow) inc(my.currRow)
proc close*(my: var TCsvParser) {.inline.} = proc close*(my: var CsvParser) {.inline.} =
## closes the parser `my` and its associated input stream. ## closes the parser `my` and its associated input stream.
lexbase.close(my) lexbase.close(my)
when isMainModule: when isMainModule:
import os import os
var s = newFileStream(ParamStr(1), fmRead) var s = newFileStream(paramStr(1), fmRead)
if s == nil: quit("cannot open the file" & ParamStr(1)) if s == nil: quit("cannot open the file" & paramStr(1))
var x: TCsvParser var x: CsvParser
open(x, s, ParamStr(1)) open(x, s, paramStr(1))
while readRow(x): while readRow(x):
Echo "new row: " echo "new row: "
for val in items(x.row): for val in items(x.row):
Echo "##", val, "##" echo "##", val, "##"
close(x) close(x)

View file

@ -1,13 +1,13 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2014 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 provides the standard Nimrod command line parser. ## This module provides the standard Nim command line parser.
## It supports one convenience iterator over all command line options and some ## It supports one convenience iterator over all command line options and some
## lower-level features. ## lower-level features.
## ##
@ -22,26 +22,28 @@ import
os, strutils os, strutils
type type
TCmdLineKind* = enum ## the detected command line token CmdLineKind* = enum ## the detected command line token
cmdEnd, ## end of command line reached cmdEnd, ## end of command line reached
cmdArgument, ## argument detected cmdArgument, ## argument detected
cmdLongoption, ## a long option ``--option`` detected cmdLongoption, ## a long option ``--option`` detected
cmdShortOption ## a short option ``-c`` detected cmdShortOption ## a short option ``-c`` detected
TOptParser* = OptParser* =
object of RootObj ## this object implements the command line parser object of RootObj ## this object implements the command line parser
cmd: string cmd: string
pos: int pos: int
inShortState: bool inShortState: bool
kind*: TCmdLineKind ## the dected command line token kind*: CmdLineKind ## the dected command line token
key*, val*: TaintedString ## key and value pair; ``key`` is the option key*, val*: TaintedString ## key and value pair; ``key`` is the option
## 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
{.deprecated: [TCmdLineKind: CmdLineKind, TOptParser: OptParser].}
when declared(os.paramCount): when declared(os.paramCount):
# we cannot provide this for NimRtl creation on Posix, because we can't # we cannot provide this for NimRtl creation on Posix, because we can't
# access the command line arguments then! # access the command line arguments then!
proc initOptParser*(cmdline = ""): TOptParser = proc initOptParser*(cmdline = ""): OptParser =
## 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 result.pos = 0
@ -57,7 +59,7 @@ when declared(os.paramCount):
result.val = TaintedString"" result.val = TaintedString""
proc parseWord(s: string, i: int, w: var string, proc parseWord(s: string, i: int, w: var string,
delim: TCharSet = {'\x09', ' ', '\0'}): int = delim: CharSet = {'\x09', ' ', '\0'}): int =
result = i result = i
if s[result] == '\"': if s[result] == '\"':
inc(result) inc(result)
@ -70,7 +72,7 @@ proc parseWord(s: string, i: int, w: var string,
add(w, s[result]) add(w, s[result])
inc(result) inc(result)
proc handleShortOption(p: var TOptParser) = proc handleShortOption(p: var OptParser) =
var i = p.pos var i = p.pos
p.kind = cmdShortOption p.kind = cmdShortOption
add(p.key.string, p.cmd[i]) add(p.key.string, p.cmd[i])
@ -87,8 +89,7 @@ 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 OptParser) {.rtl, extern: "npo$1".} =
rtl, extern: "npo$1".} =
## parses the first or next option; ``p.kind`` describes what token has been ## parses the first or next option; ``p.kind`` describes what token has been
## parsed. ``p.key`` and ``p.val`` are set accordingly. ## parsed. ``p.key`` and ``p.val`` are set accordingly.
var i = p.pos var i = p.pos
@ -122,14 +123,12 @@ proc next*(p: var TOptParser) {.
p.kind = cmdArgument p.kind = cmdArgument
p.pos = parseWord(p.cmd, i, p.key.string) p.pos = parseWord(p.cmd, i, p.key.string)
proc cmdLineRest*(p: TOptParser): TaintedString {. proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo$1".} =
rtl, extern: "npo$1".} =
## retrieves the rest of the command line that has not been parsed yet. ## retrieves the rest of the command line that has not been parsed yet.
result = strip(substr(p.cmd, p.pos, len(p.cmd) - 1)).TaintedString result = strip(substr(p.cmd, p.pos, len(p.cmd) - 1)).TaintedString
when declared(initOptParser): when declared(initOptParser):
iterator getopt*(): tuple[kind: CmdLineKind, key, val: TaintedString] =
iterator getopt*(): tuple[kind: TCmdLineKind, key, val: TaintedString] =
## 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:
## ##

View file

@ -1,13 +1,13 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2014 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 provides the standard Nimrod command line parser. ## This module provides the standard Nim command line parser.
## It supports one convenience iterator over all command line options and some ## It supports one convenience iterator over all command line options and some
## lower-level features. ## lower-level features.
## ##
@ -25,22 +25,24 @@ import
os, strutils os, strutils
type type
TCmdLineKind* = enum ## the detected command line token CmdLineKind* = enum ## the detected command line token
cmdEnd, ## end of command line reached cmdEnd, ## end of command line reached
cmdArgument, ## argument detected cmdArgument, ## argument detected
cmdLongOption, ## a long option ``--option`` detected cmdLongOption, ## a long option ``--option`` detected
cmdShortOption ## a short option ``-c`` detected cmdShortOption ## a short option ``-c`` detected
TOptParser* = OptParser* =
object of TObject ## this object implements the command line parser object of TObject ## this object implements the command line parser
cmd: seq[string] cmd: seq[string]
pos: int pos: int
remainingShortOptions: string remainingShortOptions: string
kind*: TCmdLineKind ## the dected command line token kind*: CmdLineKind ## the dected command line token
key*, val*: TaintedString ## key and value pair; ``key`` is the option key*, val*: TaintedString ## key and value pair; ``key`` is the option
## 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 initOptParser*(cmdline: seq[string]): TOptParser {.rtl.} = {.deprecated: [TCmdLineKind: CmdLineKind, TOptParser: OptParser].}
proc initOptParser*(cmdline: seq[string]): OptParser {.rtl.} =
## Initalizes option parses with cmdline. cmdline should not contain ## Initalizes option parses with cmdline. cmdline should not contain
## argument 0 - program name. ## argument 0 - program name.
## If cmdline == nil default to current command line arguments. ## If cmdline == nil default to current command line arguments.
@ -54,7 +56,7 @@ proc initOptParser*(cmdline: seq[string]): TOptParser {.rtl.} =
result.cmd = @cmdline result.cmd = @cmdline
proc initOptParser*(cmdline: string): TOptParser {.rtl, deprecated.} = proc initOptParser*(cmdline: string): OptParser {.rtl, deprecated.} =
## Initalizes option parses with cmdline. Splits cmdline in on spaces ## Initalizes option parses with cmdline. Splits cmdline in on spaces
## and calls initOptParser(openarray[string]) ## and calls initOptParser(openarray[string])
## Do not use. ## Do not use.
@ -64,13 +66,13 @@ proc initOptParser*(cmdline: string): TOptParser {.rtl, deprecated.} =
return initOptParser(cmdline.split) return initOptParser(cmdline.split)
when not defined(createNimRtl): when not defined(createNimRtl):
proc initOptParser*(): TOptParser = proc initOptParser*(): OptParser =
## Initializes option parser from current command line arguments. ## Initializes option parser from current command line arguments.
return initOptParser(commandLineParams()) return initOptParser(commandLineParams())
proc next*(p: var TOptParser) {.rtl, extern: "npo$1".} proc next*(p: var OptParser) {.rtl, extern: "npo$1".}
proc nextOption(p: var TOptParser, token: string, allowEmpty: bool) = proc nextOption(p: var OptParser, token: string, allowEmpty: bool) =
for splitchar in [':', '=']: for splitchar in [':', '=']:
if splitchar in token: if splitchar in token:
let pos = token.find(splitchar) let pos = token.find(splitchar)
@ -85,7 +87,7 @@ proc nextOption(p: var TOptParser, token: string, allowEmpty: bool) =
p.remainingShortOptions = token[0..token.len-1] p.remainingShortOptions = token[0..token.len-1]
p.next() p.next()
proc next(p: var TOptParser) = proc next(p: var OptParser) =
if p.remainingShortOptions.len != 0: if p.remainingShortOptions.len != 0:
p.kind = cmdShortOption p.kind = cmdShortOption
p.key = TaintedString(p.remainingShortOptions[0..0]) p.key = TaintedString(p.remainingShortOptions[0..0])
@ -111,20 +113,22 @@ proc next(p: var TOptParser) =
p.key = token p.key = token
p.val = "" p.val = ""
proc cmdLineRest*(p: TOptParser): TaintedString {.rtl, extern: "npo$1", deprecated.} = proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo$1", deprecated.} =
## Returns part of command line string that has not been parsed yet. ## Returns part of command line string that has not been parsed yet.
## Do not use - does not correctly handle whitespace. ## Do not use - does not correctly handle whitespace.
return p.cmd[p.pos..p.cmd.len-1].join(" ") return p.cmd[p.pos..p.cmd.len-1].join(" ")
type type
TGetoptResult* = tuple[kind: TCmdLineKind, key, val: TaintedString] GetoptResult* = tuple[kind: CmdLineKind, key, val: TaintedString]
{.deprecated: [TGetoptResult: GetoptResult].}
when declared(paramCount): when declared(paramCount):
iterator getopt*(): TGetoptResult = iterator getopt*(): GetoptResult =
## 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 OptParser object. Example:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## var ## var
## filename = "" ## filename = ""
## for kind, key, val in getopt(): ## for kind, key, val in getopt():

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim'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
@ -10,13 +10,13 @@
## The ``parsesql`` module implements a high performance SQL file ## The ``parsesql`` module implements a high performance SQL file
## parser. It parses PostgreSQL syntax and the SQL ANSI standard. ## parser. It parses PostgreSQL syntax and the SQL ANSI standard.
import import
hashes, strutils, lexbase, streams hashes, strutils, lexbase, streams
# ------------------- scanner ------------------------------------------------- # ------------------- scanner -------------------------------------------------
type type
TTokKind = enum ## enumeration of all SQL tokens TokKind = enum ## enumeration of all SQL tokens
tkInvalid, ## invalid token tkInvalid, ## invalid token
tkEof, ## end of file reached tkEof, ## end of file reached
tkIdentifier, ## abc tkIdentifier, ## abc
@ -38,33 +38,35 @@ type
tkBracketRi, ## ']' tkBracketRi, ## ']'
tkDot ## '.' tkDot ## '.'
TToken {.final.} = object # a token Token = object # a token
kind: TTokKind # the type of the token kind: TokKind # the type of the token
literal: string # the parsed (string) literal literal: string # the parsed (string) literal
TSqlLexer* = object of TBaseLexer ## the parser object. SqlLexer* = object of BaseLexer ## the parser object.
filename: string filename: string
{.deprecated: [TToken: Token, TSqlLexer: SqlLexer].}
const const
tokKindToStr: array[TTokKind, string] = [ tokKindToStr: array[TokKind, string] = [
"invalid", "[EOF]", "identifier", "quoted identifier", "string constant", "invalid", "[EOF]", "identifier", "quoted identifier", "string constant",
"escape string constant", "dollar quoted constant", "bit string constant", "escape string constant", "dollar quoted constant", "bit string constant",
"hex string constant", "integer constant", "numeric constant", "operator", "hex string constant", "integer constant", "numeric constant", "operator",
";", ":", ",", "(", ")", "[", "]", "." ";", ":", ",", "(", ")", "[", "]", "."
] ]
proc open(L: var TSqlLexer, input: PStream, filename: string) = proc open(L: var SqlLexer, input: Stream, filename: string) =
lexbase.open(L, input) lexbase.open(L, input)
L.filename = filename L.filename = filename
proc close(L: var TSqlLexer) = proc close(L: var SqlLexer) =
lexbase.close(L) lexbase.close(L)
proc getColumn(L: TSqlLexer): int = proc getColumn(L: SqlLexer): int =
## get the current column the parser has arrived at. ## get the current column the parser has arrived at.
result = getColNumber(L, L.bufPos) result = getColNumber(L, L.bufPos)
proc getLine(L: TSqlLexer): int = proc getLine(L: SqlLexer): int =
result = L.linenumber result = L.linenumber
proc handleHexChar(c: var TSqlLexer, xi: var int) = proc handleHexChar(c: var TSqlLexer, xi: var int) =
@ -137,10 +139,10 @@ proc getEscapedChar(c: var TSqlLexer, tok: var TToken) =
else: tok.kind = tkInvalid else: tok.kind = tkInvalid
else: tok.kind = tkInvalid else: tok.kind = tkInvalid
proc HandleCRLF(c: var TSqlLexer, pos: int): int = proc handleCRLF(c: var TSqlLexer, pos: int): int =
case c.buf[pos] case c.buf[pos]
of '\c': result = lexbase.HandleCR(c, pos) of '\c': result = lexbase.handleCR(c, pos)
of '\L': result = lexbase.HandleLF(c, pos) of '\L': result = lexbase.handleLF(c, pos)
else: result = pos else: result = pos
proc skip(c: var TSqlLexer) = proc skip(c: var TSqlLexer) =
@ -163,7 +165,7 @@ proc skip(c: var TSqlLexer) =
case buf[pos] case buf[pos]
of '\0': break of '\0': break
of '\c', '\L': of '\c', '\L':
pos = HandleCRLF(c, pos) pos = handleCRLF(c, pos)
buf = c.buf buf = c.buf
of '*': of '*':
if buf[pos+1] == '/': if buf[pos+1] == '/':
@ -181,13 +183,13 @@ proc skip(c: var TSqlLexer) =
else: inc(pos) else: inc(pos)
else: break else: break
of '\c', '\L': of '\c', '\L':
pos = HandleCRLF(c, pos) pos = handleCRLF(c, pos)
buf = c.buf buf = c.buf
else: else:
break # EndOfFile also leaves the loop break # EndOfFile also leaves the loop
c.bufpos = pos c.bufpos = pos
proc getString(c: var TSqlLexer, tok: var TToken, kind: TTokKind) = proc getString(c: var TSqlLexer, tok: var TToken, kind: TokKind) =
var pos = c.bufPos + 1 var pos = c.bufPos + 1
var buf = c.buf var buf = c.buf
tok.kind = kind tok.kind = kind
@ -465,7 +467,7 @@ proc errorStr(L: TSqlLexer, msg: string): string =
# :: left PostgreSQL-style typecast # :: left PostgreSQL-style typecast
# [ ] left array element selection # [ ] left array element selection
# - right unary minus # - right unary minus
# ^ left exponentiation # ^ left exponentiation
# * / % left multiplication, division, modulo # * / % left multiplication, division, modulo
# + - left addition, subtraction # + - left addition, subtraction
# IS IS TRUE, IS FALSE, IS UNKNOWN, IS NULL # IS IS TRUE, IS FALSE, IS UNKNOWN, IS NULL
@ -483,7 +485,7 @@ proc errorStr(L: TSqlLexer, msg: string): string =
# OR left logical disjunction # OR left logical disjunction
type type
TSqlNodeKind* = enum ## kind of SQL abstract syntax tree SqlNodeKind* = enum ## kind of SQL abstract syntax tree
nkNone, nkNone,
nkIdent, nkIdent,
nkStringLit, nkStringLit,
@ -536,79 +538,82 @@ type
nkEnumDef nkEnumDef
type type
EInvalidSql* = object of EInvalidValue ## Invalid SQL encountered SqlParseError* = object of ValueError ## Invalid SQL encountered
PSqlNode* = ref TSqlNode ## an SQL abstract syntax tree node SqlNode* = ref SqlNodeObj ## an SQL abstract syntax tree node
TSqlNode* = object ## an SQL abstract syntax tree node SqlNodeObj* = object ## an SQL abstract syntax tree node
case kind*: TSqlNodeKind ## kind of syntax tree case kind*: SqlNodeKind ## kind of syntax tree
of nkIdent, nkStringLit, nkBitStringLit, nkHexStringLit, of nkIdent, nkStringLit, nkBitStringLit, nkHexStringLit,
nkIntegerLit, nkNumericLit: nkIntegerLit, nkNumericLit:
strVal*: string ## AST leaf: the identifier, numeric literal strVal*: string ## AST leaf: the identifier, numeric literal
## string literal, etc. ## string literal, etc.
else: else:
sons*: seq[PSqlNode] ## the node's children sons*: seq[SqlNode] ## the node's children
TSqlParser* = object of TSqlLexer ## SQL parser object SqlParser* = object of SqlLexer ## SQL parser object
tok: TToken tok: Token
proc newNode(k: TSqlNodeKind): PSqlNode = {.deprecated: [EInvalidSql: SqlParseError, PSqlNode: SqlNode,
TSqlNode: SqlNodeObj, TSqlParser: SqlParser, TSqlNodeKind: SqlNodeKind].}
proc newNode(k: SqlNodeKind): SqlNode =
new(result) new(result)
result.kind = k result.kind = k
proc newNode(k: TSqlNodeKind, s: string): PSqlNode = proc newNode(k: SqlNodeKind, s: string): SqlNode =
new(result) new(result)
result.kind = k result.kind = k
result.strVal = s result.strVal = s
proc len*(n: PSqlNode): int = proc len*(n: SqlNode): int =
if isNil(n.sons): result = 0 if isNil(n.sons): result = 0
else: result = n.sons.len else: result = n.sons.len
proc add*(father, n: PSqlNode) = proc add*(father, n: SqlNode) =
if isNil(father.sons): father.sons = @[] if isNil(father.sons): father.sons = @[]
add(father.sons, n) add(father.sons, n)
proc getTok(p: var TSqlParser) = proc getTok(p: var SqlParser) =
getTok(p, p.tok) getTok(p, p.tok)
proc sqlError(p: TSqlParser, msg: string) = proc sqlError(p: SqlParser, msg: string) =
var e: ref EInvalidSql var e: ref SqlParseError
new(e) new(e)
e.msg = errorStr(p, msg) e.msg = errorStr(p, msg)
raise e raise e
proc isKeyw(p: TSqlParser, keyw: string): bool = proc isKeyw(p: SqlParser, keyw: string): bool =
result = p.tok.kind == tkIdentifier and result = p.tok.kind == tkIdentifier and
cmpIgnoreCase(p.tok.literal, keyw) == 0 cmpIgnoreCase(p.tok.literal, keyw) == 0
proc isOpr(p: TSqlParser, opr: string): bool = proc isOpr(p: SqlParser, opr: string): bool =
result = p.tok.kind == tkOperator and result = p.tok.kind == tkOperator and
cmpIgnoreCase(p.tok.literal, opr) == 0 cmpIgnoreCase(p.tok.literal, opr) == 0
proc optKeyw(p: var TSqlParser, keyw: string) = proc optKeyw(p: var SqlParser, keyw: string) =
if p.tok.kind == tkIdentifier and cmpIgnoreCase(p.tok.literal, keyw) == 0: if p.tok.kind == tkIdentifier and cmpIgnoreCase(p.tok.literal, keyw) == 0:
getTok(p) getTok(p)
proc expectIdent(p: TSqlParser) = proc expectIdent(p: SqlParser) =
if p.tok.kind != tkIdentifier and p.tok.kind != tkQuotedIdentifier: if p.tok.kind != tkIdentifier and p.tok.kind != tkQuotedIdentifier:
sqlError(p, "identifier expected") sqlError(p, "identifier expected")
proc expect(p: TSqlParser, kind: TTokKind) = proc expect(p: SqlParser, kind: TokKind) =
if p.tok.kind != kind: if p.tok.kind != kind:
sqlError(p, tokKindToStr[kind] & " expected") sqlError(p, tokKindToStr[kind] & " expected")
proc eat(p: var TSqlParser, kind: TTokKind) = proc eat(p: var SqlParser, kind: TokKind) =
if p.tok.kind == kind: if p.tok.kind == kind:
getTok(p) getTok(p)
else: else:
sqlError(p, tokKindToStr[kind] & " expected") sqlError(p, tokKindToStr[kind] & " expected")
proc eat(p: var TSqlParser, keyw: string) = proc eat(p: var SqlParser, keyw: string) =
if isKeyw(p, keyw): if isKeyw(p, keyw):
getTok(p) getTok(p)
else: else:
sqlError(p, keyw.toUpper() & " expected") sqlError(p, keyw.toUpper() & " expected")
proc parseDataType(p: var TSqlParser): PSqlNode = proc parseDataType(p: var SqlParser): SqlNode =
if isKeyw(p, "enum"): if isKeyw(p, "enum"):
result = newNode(nkEnumDef) result = newNode(nkEnumDef)
getTok(p) getTok(p)
@ -636,7 +641,7 @@ proc parseDataType(p: var TSqlParser): PSqlNode =
getTok(p) getTok(p)
eat(p, tkParRi) eat(p, tkParRi)
proc getPrecedence(p: TSqlParser): int = proc getPrecedence(p: SqlParser): int =
if isOpr(p, "*") or isOpr(p, "/") or isOpr(p, "%"): if isOpr(p, "*") or isOpr(p, "/") or isOpr(p, "%"):
result = 6 result = 6
elif isOpr(p, "+") or isOpr(p, "-"): elif isOpr(p, "+") or isOpr(p, "-"):
@ -655,9 +660,9 @@ proc getPrecedence(p: TSqlParser): int =
else: else:
result = - 1 result = - 1
proc parseExpr(p: var TSqlParser): PSqlNode proc parseExpr(p: var SqlParser): SqlNode
proc identOrLiteral(p: var TSqlParser): PSqlNode = proc identOrLiteral(p: var SqlParser): SqlNode =
case p.tok.kind case p.tok.kind
of tkIdentifier, tkQuotedIdentifier: of tkIdentifier, tkQuotedIdentifier:
result = newNode(nkIdent, p.tok.literal) result = newNode(nkIdent, p.tok.literal)
@ -685,7 +690,7 @@ proc identOrLiteral(p: var TSqlParser): PSqlNode =
sqlError(p, "expression expected") sqlError(p, "expression expected")
getTok(p) # we must consume a token here to prevend endless loops! getTok(p) # we must consume a token here to prevend endless loops!
proc primary(p: var TSqlParser): PSqlNode = proc primary(p: var SqlParser): SqlNode =
if p.tok.kind == tkOperator or isKeyw(p, "not"): if p.tok.kind == tkOperator or isKeyw(p, "not"):
result = newNode(nkPrefix) result = newNode(nkPrefix)
result.add(newNode(nkIdent, p.tok.literal)) result.add(newNode(nkIdent, p.tok.literal))
@ -723,9 +728,9 @@ proc primary(p: var TSqlParser): PSqlNode =
getTok(p) getTok(p)
else: break else: break
proc lowestExprAux(p: var TSqlParser, v: var PSqlNode, limit: int): int = proc lowestExprAux(p: var SqlParser, v: var SqlNode, limit: int): int =
var var
v2, node, opNode: PSqlNode v2, node, opNode: SqlNode
v = primary(p) # expand while operators have priorities higher than 'limit' v = primary(p) # expand while operators have priorities higher than 'limit'
var opPred = getPrecedence(p) var opPred = getPrecedence(p)
result = opPred result = opPred
@ -740,14 +745,14 @@ proc lowestExprAux(p: var TSqlParser, v: var PSqlNode, limit: int): int =
v = node v = node
opPred = getPrecedence(p) opPred = getPrecedence(p)
proc parseExpr(p: var TSqlParser): PSqlNode = proc parseExpr(p: var SqlParser): SqlNode =
discard lowestExprAux(p, result, - 1) discard lowestExprAux(p, result, - 1)
proc parseTableName(p: var TSqlParser): PSqlNode = proc parseTableName(p: var SqlParser): SqlNode =
expectIdent(p) expectIdent(p)
result = primary(p) result = primary(p)
proc parseColumnReference(p: var TSqlParser): PSqlNode = proc parseColumnReference(p: var SqlParser): SqlNode =
result = parseTableName(p) result = parseTableName(p)
if p.tok.kind == tkParLe: if p.tok.kind == tkParLe:
getTok(p) getTok(p)
@ -760,12 +765,12 @@ proc parseColumnReference(p: var TSqlParser): PSqlNode =
result.add(parseTableName(p)) result.add(parseTableName(p))
eat(p, tkParRi) eat(p, tkParRi)
proc parseCheck(p: var TSqlParser): PSqlNode = proc parseCheck(p: var SqlParser): SqlNode =
getTok(p) getTok(p)
result = newNode(nkCheck) result = newNode(nkCheck)
result.add(parseExpr(p)) result.add(parseExpr(p))
proc parseConstraint(p: var TSqlParser): PSqlNode = proc parseConstraint(p: var SqlParser): SqlNode =
getTok(p) getTok(p)
result = newNode(nkConstraint) result = newNode(nkConstraint)
expectIdent(p) expectIdent(p)
@ -774,7 +779,7 @@ proc parseConstraint(p: var TSqlParser): PSqlNode =
eat(p, "check") eat(p, "check")
result.add(parseExpr(p)) result.add(parseExpr(p))
proc parseColumnConstraints(p: var TSqlParser, result: PSqlNode) = proc parseColumnConstraints(p: var SqlParser, result: SqlNode) =
while true: while true:
if isKeyw(p, "default"): if isKeyw(p, "default"):
getTok(p) getTok(p)
@ -806,7 +811,7 @@ proc parseColumnConstraints(p: var TSqlParser, result: PSqlNode) =
else: else:
break break
proc parseColumnDef(p: var TSqlParser): PSqlNode = proc parseColumnDef(p: var SqlParser): SqlNode =
expectIdent(p) expectIdent(p)
result = newNode(nkColumnDef) result = newNode(nkColumnDef)
result.add(newNode(nkIdent, p.tok.literal)) result.add(newNode(nkIdent, p.tok.literal))
@ -814,7 +819,7 @@ proc parseColumnDef(p: var TSqlParser): PSqlNode =
result.add(parseDataType(p)) result.add(parseDataType(p))
parseColumnConstraints(p, result) parseColumnConstraints(p, result)
proc parseIfNotExists(p: var TSqlParser, k: TSqlNodeKind): PSqlNode = proc parseIfNotExists(p: var SqlParser, k: TSqlNodeKind): SqlNode =
getTok(p) getTok(p)
if isKeyw(p, "if"): if isKeyw(p, "if"):
getTok(p) getTok(p)
@ -824,7 +829,7 @@ proc parseIfNotExists(p: var TSqlParser, k: TSqlNodeKind): PSqlNode =
else: else:
result = newNode(k) result = newNode(k)
proc parseParIdentList(p: var TSqlParser, father: PSqlNode) = proc parseParIdentList(p: var SqlParser, father: SqlNode) =
eat(p, tkParLe) eat(p, tkParLe)
while true: while true:
expectIdent(p) expectIdent(p)
@ -834,7 +839,7 @@ proc parseParIdentList(p: var TSqlParser, father: PSqlNode) =
getTok(p) getTok(p)
eat(p, tkParRi) eat(p, tkParRi)
proc parseTableConstraint(p: var TSqlParser): PSqlNode = proc parseTableConstraint(p: var SqlParser): SqlNode =
if isKeyw(p, "primary"): if isKeyw(p, "primary"):
getTok(p) getTok(p)
eat(p, "key") eat(p, "key")
@ -861,7 +866,7 @@ proc parseTableConstraint(p: var TSqlParser): PSqlNode =
else: else:
sqlError(p, "column definition expected") sqlError(p, "column definition expected")
proc parseTableDef(p: var TSqlParser): PSqlNode = proc parseTableDef(p: var SqlParser): SqlNode =
result = parseIfNotExists(p, nkCreateTable) result = parseIfNotExists(p, nkCreateTable)
expectIdent(p) expectIdent(p)
result.add(newNode(nkIdent, p.tok.literal)) result.add(newNode(nkIdent, p.tok.literal))
@ -876,7 +881,7 @@ proc parseTableDef(p: var TSqlParser): PSqlNode =
if p.tok.kind != tkComma: break if p.tok.kind != tkComma: break
eat(p, tkParRi) eat(p, tkParRi)
proc parseTypeDef(p: var TSqlParser): PSqlNode = proc parseTypeDef(p: var SqlParser): SqlNode =
result = parseIfNotExists(p, nkCreateType) result = parseIfNotExists(p, nkCreateType)
expectIdent(p) expectIdent(p)
result.add(newNode(nkIdent, p.tok.literal)) result.add(newNode(nkIdent, p.tok.literal))
@ -884,12 +889,12 @@ proc parseTypeDef(p: var TSqlParser): PSqlNode =
eat(p, "as") eat(p, "as")
result.add(parseDataType(p)) result.add(parseDataType(p))
proc parseWhere(p: var TSqlParser): PSqlNode = proc parseWhere(p: var SqlParser): SqlNode =
getTok(p) getTok(p)
result = newNode(nkWhere) result = newNode(nkWhere)
result.add(parseExpr(p)) result.add(parseExpr(p))
proc parseIndexDef(p: var TSqlParser): PSqlNode = proc parseIndexDef(p: var SqlParser): SqlNode =
result = parseIfNotExists(p, nkCreateIndex) result = parseIfNotExists(p, nkCreateIndex)
if isKeyw(p, "primary"): if isKeyw(p, "primary"):
getTok(p) getTok(p)
@ -914,7 +919,7 @@ proc parseIndexDef(p: var TSqlParser): PSqlNode =
getTok(p) getTok(p)
eat(p, tkParRi) eat(p, tkParRi)
proc parseInsert(p: var TSqlParser): PSqlNode = proc parseInsert(p: var SqlParser): SqlNode =
getTok(p) getTok(p)
eat(p, "into") eat(p, "into")
expectIdent(p) expectIdent(p)
@ -941,7 +946,7 @@ proc parseInsert(p: var TSqlParser): PSqlNode =
result.add(n) result.add(n)
eat(p, tkParRi) eat(p, tkParRi)
proc parseUpdate(p: var TSqlParser): PSqlNode = proc parseUpdate(p: var SqlParser): SqlNode =
getTok(p) getTok(p)
result = newNode(nkUpdate) result = newNode(nkUpdate)
result.add(primary(p)) result.add(primary(p))
@ -962,7 +967,7 @@ proc parseUpdate(p: var TSqlParser): PSqlNode =
else: else:
result.add(nil) result.add(nil)
proc parseDelete(p: var TSqlParser): PSqlNode = proc parseDelete(p: var SqlParser): SqlNode =
getTok(p) getTok(p)
result = newNode(nkDelete) result = newNode(nkDelete)
eat(p, "from") eat(p, "from")
@ -972,7 +977,7 @@ proc parseDelete(p: var TSqlParser): PSqlNode =
else: else:
result.add(nil) result.add(nil)
proc parseSelect(p: var TSqlParser): PSqlNode = proc parseSelect(p: var SqlParser): SqlNode =
getTok(p) getTok(p)
if isKeyw(p, "distinct"): if isKeyw(p, "distinct"):
getTok(p) getTok(p)
@ -1041,7 +1046,7 @@ proc parseSelect(p: var TSqlParser): PSqlNode =
getTok(p) getTok(p)
result.add(n) result.add(n)
proc parseStmt(p: var TSqlParser): PSqlNode = proc parseStmt(p: var SqlParser): SqlNode =
if isKeyw(p, "create"): if isKeyw(p, "create"):
getTok(p) getTok(p)
optKeyw(p, "cached") optKeyw(p, "cached")
@ -1071,7 +1076,7 @@ proc parseStmt(p: var TSqlParser): PSqlNode =
else: else:
sqlError(p, "CREATE expected") sqlError(p, "CREATE expected")
proc open(p: var TSqlParser, input: PStream, filename: string) = proc open(p: var SqlParser, input: PStream, filename: string) =
## opens the parser `p` and assigns the input stream `input` to it. ## opens the parser `p` and assigns the input stream `input` to it.
## `filename` is only used for error messages. ## `filename` is only used for error messages.
open(TSqlLexer(p), input, filename) open(TSqlLexer(p), input, filename)
@ -1079,7 +1084,7 @@ proc open(p: var TSqlParser, input: PStream, filename: string) =
p.tok.literal = "" p.tok.literal = ""
getTok(p) getTok(p)
proc parse(p: var TSqlParser): PSqlNode = proc parse(p: var SqlParser): SqlNode =
## parses the content of `p`'s input stream and returns the SQL AST. ## parses the content of `p`'s input stream and returns the SQL AST.
## Syntax errors raise an `EInvalidSql` exception. ## Syntax errors raise an `EInvalidSql` exception.
result = newNode(nkStmtList) result = newNode(nkStmtList)
@ -1090,24 +1095,24 @@ proc parse(p: var TSqlParser): PSqlNode =
if result.len == 1: if result.len == 1:
result = result.sons[0] result = result.sons[0]
proc close(p: var TSqlParser) = proc close(p: var SqlParser) =
## closes the parser `p`. The associated input stream is closed too. ## 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): SqlNode =
## parses the SQL from `input` into an AST and returns the AST. ## parses the SQL from `input` into an AST and returns the AST.
## `filename` is only used for error messages. ## `filename` is only used for error messages.
## Syntax errors raise an `EInvalidSql` exception. ## Syntax errors raise an `EInvalidSql` exception.
var p: TSqlParser var p: SqlParser
open(p, input, filename) open(p, input, filename)
try: try:
result = parse(p) result = parse(p)
finally: finally:
close(p) close(p)
proc ra(n: PSqlNode, s: var string, indent: int) proc ra(n: SqlNode, s: var string, indent: int)
proc rs(n: PSqlNode, s: var string, indent: int, proc rs(n: SqlNode, s: var string, indent: int,
prefix = "(", suffix = ")", prefix = "(", suffix = ")",
sep = ", ") = sep = ", ") =
if n.len > 0: if n.len > 0:
@ -1117,7 +1122,7 @@ proc rs(n: PSqlNode, s: var string, indent: int,
ra(n.sons[i], s, indent) ra(n.sons[i], s, indent)
s.add(suffix) s.add(suffix)
proc ra(n: PSqlNode, s: var string, indent: int) = proc ra(n: SqlNode, s: var string, indent: int) =
if n == nil: return if n == nil: return
case n.kind case n.kind
of nkNone: discard of nkNone: discard
@ -1320,7 +1325,7 @@ proc ra(n: PSqlNode, s: var string, indent: int) =
#for x, y, z in db.select(fromm = a, b where = a.name == b.name): #for x, y, z in db.select(fromm = a, b where = a.name == b.name):
# writeln x, y, z # writeln x, y, z
proc renderSQL*(n: PSqlNode): string = proc renderSQL*(n: SqlNode): string =
## Converts an SQL abstract syntax tree to its string representation. ## Converts an SQL abstract syntax tree to its string representation.
result = "" result = ""
ra(n, result, 0) ra(n, result, 0)

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2014 Dominik Picheta # (c) Copyright 2014 Dominik Picheta
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -15,12 +15,14 @@
import strutils import strutils
type type
TUrl* = tuple[ ## represents a *Uniform Resource Locator* (URL) Url* = tuple[ ## represents a *Uniform Resource Locator* (URL)
## any optional component is "" if it does not exist ## any optional component is "" if it does not exist
scheme, username, password, scheme, username, password,
hostname, port, path, query, anchor: string] hostname, port, path, query, anchor: string]
proc parseUrl*(url: string): TUrl {.deprecated.} = {.deprecated: [TUrl: Url].}
proc parseUrl*(url: string): Url {.deprecated.} =
var i = 0 var i = 0
var scheme, username, password: string = "" var scheme, username, password: string = ""
@ -86,7 +88,7 @@ proc parseUrl*(url: string): TUrl {.deprecated.} =
return (scheme, username, password, hostname, port, path, query, anchor) return (scheme, username, password, hostname, port, path, query, anchor)
proc `$`*(u: TUrl): string {.deprecated.} = proc `$`*(u: Url): string {.deprecated.} =
## turns the URL `u` into its string representation. ## turns the URL `u` into its string representation.
result = "" result = ""
if u.scheme.len > 0: if u.scheme.len > 0:

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -35,7 +35,7 @@ proc parseHex*(s: string, number: var int, start = 0): int {.
## can use this feature to *chain* calls, though the result int will quickly ## can use this feature to *chain* calls, though the result int will quickly
## overflow. Example: ## overflow. Example:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## var value = 0 ## var value = 0
## discard parseHex("0x38", value) ## discard parseHex("0x38", value)
## assert value == 56 ## assert value == 56
@ -332,7 +332,7 @@ proc parseFloat*(s: string, number: var float, start = 0): int {.
number = bf number = bf
type type
TInterpolatedKind* = enum ## describes for `interpolatedFragments` InterpolatedKind* = enum ## describes for `interpolatedFragments`
## which part of the interpolated string is ## which part of the interpolated string is
## yielded; for example in "str$$$var${expr}" ## yielded; for example in "str$$$var${expr}"
ikStr, ## ``str`` part of the interpolated string ikStr, ## ``str`` part of the interpolated string
@ -340,7 +340,9 @@ type
ikVar, ## ``var`` part of the interpolated string ikVar, ## ``var`` part of the interpolated string
ikExpr ## ``expr`` part of the interpolated string ikExpr ## ``expr`` part of the interpolated string
iterator interpolatedFragments*(s: string): tuple[kind: TInterpolatedKind, {.deprecated: [TInterpolatedKind: InterpolatedKind].}
iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
value: string] = value: string] =
## Tokenizes the string `s` into substrings for interpolation purposes. ## Tokenizes the string `s` into substrings for interpolation purposes.
## ##
@ -360,7 +362,7 @@ iterator interpolatedFragments*(s: string): tuple[kind: TInterpolatedKind,
## (ikString, " ") ## (ikString, " ")
## (ikDollar, "$") ## (ikDollar, "$")
var i = 0 var i = 0
var kind: TInterpolatedKind var kind: InterpolatedKind
while true: while true:
var j = i var j = i
if s[j] == '$': if s[j] == '$':

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2010 Andreas Rumpf # (c) Copyright 2010 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -56,7 +56,7 @@ import
# xmlElementCloseEnd, ## ``/>`` # xmlElementCloseEnd, ## ``/>``
type type
TXmlEventKind* = enum ## enumation of all events that may occur when parsing XmlEventKind* = enum ## enumation of all events that may occur when parsing
xmlError, ## an error ocurred during parsing xmlError, ## an error ocurred during parsing
xmlEof, ## end of file reached xmlEof, ## end of file reached
xmlCharData, ## character data xmlCharData, ## character data
@ -72,7 +72,7 @@ type
xmlEntity, ## &entity; xmlEntity, ## &entity;
xmlSpecial ## ``<! ... data ... >`` xmlSpecial ## ``<! ... data ... >``
TXmlError* = enum ## enumeration that lists all errors that can occur XmlErrorKind* = enum ## enumeration that lists all errors that can occur
errNone, ## no error errNone, ## no error
errEndOfCDataExpected, ## ``]]>`` expected errEndOfCDataExpected, ## ``]]>`` expected
errNameExpected, ## name expected errNameExpected, ## name expected
@ -83,23 +83,26 @@ type
errQuoteExpected, ## ``"`` or ``'`` expected errQuoteExpected, ## ``"`` or ``'`` expected
errEndOfCommentExpected ## ``-->`` expected errEndOfCommentExpected ## ``-->`` expected
TParserState = enum ParserState = enum
stateStart, stateNormal, stateAttr, stateEmptyElementTag, stateError stateStart, stateNormal, stateAttr, stateEmptyElementTag, stateError
TXmlParseOption* = enum ## options for the XML parser XmlParseOption* = enum ## options for the XML parser
reportWhitespace, ## report whitespace reportWhitespace, ## report whitespace
reportComments ## report comments reportComments ## report comments
TXmlParser* = object of TBaseLexer ## the parser object. XmlParser* = object of TBaseLexer ## the parser object.
a, b, c: string a, b, c: string
kind: TXmlEventKind kind: XmlEventKind
err: TXmlError err: XmlErrorKind
state: TParserState state: ParserState
filename: string filename: string
options: set[TXmlParseOption] options: set[XmlParseOption]
{.deprecated: [TXmlParser: XmlParser, TXmlParseOptions: XmlParseOptions,
TXmlError: XmlErrorKind, TXmlEventKind: XmlEventKind].}
const const
errorMessages: array [TXmlError, string] = [ errorMessages: array[XmlError, string] = [
"no error", "no error",
"']]>' expected", "']]>' expected",
"name expected", "name expected",
@ -111,8 +114,8 @@ const
"'-->' expected" "'-->' expected"
] ]
proc open*(my: var TXmlParser, input: PStream, filename: string, proc open*(my: var XmlParser, input: Stream, filename: string,
options: set[TXmlParseOption] = {}) = options: set[XmlParseOption] = {}) =
## initializes the parser with an input stream. `Filename` is only used ## initializes the parser with an input stream. `Filename` is only used
## for nice error messages. The parser's behaviour can be controlled by ## for nice error messages. The parser's behaviour can be controlled by
## the `options` parameter: If `options` contains ``reportWhitespace`` ## the `options` parameter: If `options` contains ``reportWhitespace``
@ -127,7 +130,7 @@ proc open*(my: var TXmlParser, input: PStream, filename: string,
my.b = "" my.b = ""
my.options = options my.options = options
proc close*(my: var TXmlParser) {.inline.} = proc close*(my: var XmlParser) {.inline.} =
## closes the parser `my` and its associated input stream. ## closes the parser `my` and its associated input stream.
lexbase.close(my) lexbase.close(my)

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -32,7 +32,7 @@ const
## can be captured. More subpatterns cannot be captured! ## can be captured. More subpatterns cannot be captured!
type type
TPegKind = enum PegKind = enum
pkEmpty, pkEmpty,
pkAny, ## any character (.) pkAny, ## any character (.)
pkAnyRune, ## any Unicode character (_) pkAnyRune, ## any Unicode character (_)
@ -67,16 +67,16 @@ type
pkRule, ## a <- b pkRule, ## a <- b
pkList, ## a, b pkList, ## a, b
pkStartAnchor ## ^ --> Internal DSL: startAnchor() pkStartAnchor ## ^ --> Internal DSL: startAnchor()
TNonTerminalFlag = enum NonTerminalFlag = enum
ntDeclared, ntUsed ntDeclared, ntUsed
TNonTerminal {.final.} = object ## represents a non terminal symbol NonTerminalObj = object ## represents a non terminal symbol
name: string ## the name of the symbol name: string ## the name of the symbol
line: int ## line the symbol has been declared/used in line: int ## line the symbol has been declared/used in
col: int ## column the symbol has been declared/used in col: int ## column the symbol has been declared/used in
flags: set[TNonTerminalFlag] ## the nonterminal's flags flags: set[NonTerminalFlag] ## the nonterminal's flags
rule: TNode ## the rule that the symbol refers to rule: TNode ## the rule that the symbol refers to
TNode {.final, shallow.} = object TNode {.shallow.} = object
case kind: TPegKind case kind: PegKind
of pkEmpty..pkWhitespace: nil of pkEmpty..pkWhitespace: nil
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string
of pkChar, pkGreedyRepChar: ch: char of pkChar, pkGreedyRepChar: ch: char
@ -84,11 +84,13 @@ type
of pkNonTerminal: nt: PNonTerminal of pkNonTerminal: nt: PNonTerminal
of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns] of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns]
else: sons: seq[TNode] else: sons: seq[TNode]
PNonTerminal* = ref TNonTerminal NonTerminal* = ref NonTerminalObj
TPeg* = TNode ## type that represents a PEG Peg* = TNode ## type that represents a PEG
proc term*(t: string): TPeg {.nosideEffect, rtl, extern: "npegs$1Str".} = {.deprecated: [TPeg: Peg].}
proc term*(t: string): Peg {.nosideEffect, rtl, extern: "npegs$1Str".} =
## constructs a PEG from a terminal string ## constructs a PEG from a terminal string
if t.len != 1: if t.len != 1:
result.kind = pkTerminal result.kind = pkTerminal
@ -97,35 +99,35 @@ proc term*(t: string): TPeg {.nosideEffect, rtl, extern: "npegs$1Str".} =
result.kind = pkChar result.kind = pkChar
result.ch = t[0] result.ch = t[0]
proc termIgnoreCase*(t: string): TPeg {. proc termIgnoreCase*(t: string): Peg {.
nosideEffect, rtl, extern: "npegs$1".} = nosideEffect, rtl, extern: "npegs$1".} =
## constructs a PEG from a terminal string; ignore case for matching ## constructs a PEG from a terminal string; ignore case for matching
result.kind = pkTerminalIgnoreCase result.kind = pkTerminalIgnoreCase
result.term = t result.term = t
proc termIgnoreStyle*(t: string): TPeg {. proc termIgnoreStyle*(t: string): Peg {.
nosideEffect, rtl, extern: "npegs$1".} = nosideEffect, rtl, extern: "npegs$1".} =
## constructs a PEG from a terminal string; ignore style for matching ## constructs a PEG from a terminal string; ignore style for matching
result.kind = pkTerminalIgnoreStyle result.kind = pkTerminalIgnoreStyle
result.term = t result.term = t
proc term*(t: char): TPeg {.nosideEffect, rtl, extern: "npegs$1Char".} = proc term*(t: char): Peg {.nosideEffect, rtl, extern: "npegs$1Char".} =
## constructs a PEG from a terminal char ## constructs a PEG from a terminal char
assert t != '\0' assert t != '\0'
result.kind = pkChar result.kind = pkChar
result.ch = t result.ch = t
proc charSet*(s: set[char]): TPeg {.nosideEffect, rtl, extern: "npegs$1".} = proc charSet*(s: set[char]): Peg {.nosideEffect, rtl, extern: "npegs$1".} =
## constructs a PEG from a character set `s` ## constructs a PEG from a character set `s`
assert '\0' notin s assert '\0' notin s
result.kind = pkCharChoice result.kind = pkCharChoice
new(result.charChoice) new(result.charChoice)
result.charChoice[] = s result.charChoice[] = s
proc len(a: TPeg): int {.inline.} = return a.sons.len proc len(a: Peg): int {.inline.} = return a.sons.len
proc add(d: var TPeg, s: TPeg) {.inline.} = add(d.sons, s) proc add(d: var Peg, s: Peg) {.inline.} = add(d.sons, s)
proc addChoice(dest: var TPeg, elem: TPeg) = proc addChoice(dest: var Peg, elem: Peg) =
var L = dest.len-1 var L = dest.len-1
if L >= 0 and dest.sons[L].kind == pkCharChoice: if L >= 0 and dest.sons[L].kind == pkCharChoice:
# caution! Do not introduce false aliasing here! # caution! Do not introduce false aliasing here!
@ -137,7 +139,7 @@ proc addChoice(dest: var TPeg, elem: TPeg) =
else: add(dest, elem) else: add(dest, elem)
else: add(dest, elem) else: add(dest, elem)
template multipleOp(k: TPegKind, localOpt: expr) = template multipleOp(k: PegKind, localOpt: expr) =
result.kind = k result.kind = k
result.sons = @[] result.sons = @[]
for x in items(a): for x in items(a):
@ -149,12 +151,12 @@ template multipleOp(k: TPegKind, localOpt: expr) =
if result.len == 1: if result.len == 1:
result = result.sons[0] result = result.sons[0]
proc `/`*(a: varargs[TPeg]): TPeg {. proc `/`*(a: varargs[Peg]): Peg {.
nosideEffect, rtl, extern: "npegsOrderedChoice".} = nosideEffect, rtl, extern: "npegsOrderedChoice".} =
## constructs an ordered choice with the PEGs in `a` ## constructs an ordered choice with the PEGs in `a`
multipleOp(pkOrderedChoice, addChoice) multipleOp(pkOrderedChoice, addChoice)
proc addSequence(dest: var TPeg, elem: TPeg) = proc addSequence(dest: var Peg, elem: Peg) =
var L = dest.len-1 var L = dest.len-1
if L >= 0 and dest.sons[L].kind == pkTerminal: if L >= 0 and dest.sons[L].kind == pkTerminal:
# caution! Do not introduce false aliasing here! # caution! Do not introduce false aliasing here!
@ -166,12 +168,12 @@ proc addSequence(dest: var TPeg, elem: TPeg) =
else: add(dest, elem) else: add(dest, elem)
else: add(dest, elem) else: add(dest, elem)
proc sequence*(a: varargs[TPeg]): TPeg {. proc sequence*(a: varargs[Peg]): Peg {.
nosideEffect, rtl, extern: "npegs$1".} = nosideEffect, rtl, extern: "npegs$1".} =
## constructs a sequence with all the PEGs from `a` ## constructs a sequence with all the PEGs from `a`
multipleOp(pkSequence, addSequence) multipleOp(pkSequence, addSequence)
proc `?`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsOptional".} = proc `?`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsOptional".} =
## constructs an optional for the PEG `a` ## constructs an optional for the PEG `a`
if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar, if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar,
pkGreedyRepSet}: pkGreedyRepSet}:
@ -182,7 +184,7 @@ proc `?`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsOptional".} =
result.kind = pkOption result.kind = pkOption
result.sons = @[a] result.sons = @[a]
proc `*`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsGreedyRep".} = proc `*`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsGreedyRep".} =
## constructs a "greedy repetition" for the PEG `a` ## constructs a "greedy repetition" for the PEG `a`
case a.kind case a.kind
of pkGreedyRep, pkGreedyRepChar, pkGreedyRepSet, pkGreedyAny, pkOption: of pkGreedyRep, pkGreedyRepChar, pkGreedyRepSet, pkGreedyAny, pkOption:
@ -200,111 +202,99 @@ proc `*`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsGreedyRep".} =
result.kind = pkGreedyRep result.kind = pkGreedyRep
result.sons = @[a] result.sons = @[a]
proc `!*`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsSearch".} = proc `!*`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsSearch".} =
## constructs a "search" for the PEG `a` ## constructs a "search" for the PEG `a`
result.kind = pkSearch result.kind = pkSearch
result.sons = @[a] result.sons = @[a]
proc `!*\`*(a: TPeg): TPeg {.noSideEffect, rtl, proc `!*\`*(a: Peg): Peg {.noSideEffect, rtl,
extern: "npgegsCapturedSearch".} = extern: "npgegsCapturedSearch".} =
## constructs a "captured search" for the PEG `a` ## constructs a "captured search" for the PEG `a`
result.kind = pkCapturedSearch result.kind = pkCapturedSearch
result.sons = @[a] result.sons = @[a]
when false:
proc contains(a: TPeg, k: TPegKind): bool =
if a.kind == k: return true
case a.kind
of pkEmpty, pkAny, pkAnyRune, pkGreedyAny, pkNewLine, pkTerminal,
pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, pkGreedyRepChar,
pkCharChoice, pkGreedyRepSet: nil
of pkNonTerminal: return true
else:
for i in 0..a.sons.len-1:
if contains(a.sons[i], k): return true
proc `+`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsGreedyPosRep".} = proc `+`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsGreedyPosRep".} =
## constructs a "greedy positive repetition" with the PEG `a` ## constructs a "greedy positive repetition" with the PEG `a`
return sequence(a, *a) return sequence(a, *a)
proc `&`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsAndPredicate".} = proc `&`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsAndPredicate".} =
## constructs an "and predicate" with the PEG `a` ## constructs an "and predicate" with the PEG `a`
result.kind = pkAndPredicate result.kind = pkAndPredicate
result.sons = @[a] result.sons = @[a]
proc `!`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsNotPredicate".} = proc `!`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsNotPredicate".} =
## constructs a "not predicate" with the PEG `a` ## constructs a "not predicate" with the PEG `a`
result.kind = pkNotPredicate result.kind = pkNotPredicate
result.sons = @[a] result.sons = @[a]
proc any*: TPeg {.inline.} = proc any*: Peg {.inline.} =
## constructs the PEG `any character`:idx: (``.``) ## constructs the PEG `any character`:idx: (``.``)
result.kind = pkAny result.kind = pkAny
proc anyRune*: TPeg {.inline.} = proc anyRune*: Peg {.inline.} =
## constructs the PEG `any rune`:idx: (``_``) ## constructs the PEG `any rune`:idx: (``_``)
result.kind = pkAnyRune result.kind = pkAnyRune
proc newLine*: TPeg {.inline.} = proc newLine*: Peg {.inline.} =
## constructs the PEG `newline`:idx: (``\n``) ## constructs the PEG `newline`:idx: (``\n``)
result.kind = pkNewline result.kind = pkNewline
proc unicodeLetter*: TPeg {.inline.} = proc unicodeLetter*: Peg {.inline.} =
## constructs the PEG ``\letter`` which matches any Unicode letter. ## constructs the PEG ``\letter`` which matches any Unicode letter.
result.kind = pkLetter result.kind = pkLetter
proc unicodeLower*: TPeg {.inline.} = proc unicodeLower*: Peg {.inline.} =
## constructs the PEG ``\lower`` which matches any Unicode lowercase letter. ## constructs the PEG ``\lower`` which matches any Unicode lowercase letter.
result.kind = pkLower result.kind = pkLower
proc unicodeUpper*: TPeg {.inline.} = proc unicodeUpper*: Peg {.inline.} =
## constructs the PEG ``\upper`` which matches any Unicode uppercase letter. ## constructs the PEG ``\upper`` which matches any Unicode uppercase letter.
result.kind = pkUpper result.kind = pkUpper
proc unicodeTitle*: TPeg {.inline.} = proc unicodeTitle*: Peg {.inline.} =
## constructs the PEG ``\title`` which matches any Unicode title letter. ## constructs the PEG ``\title`` which matches any Unicode title letter.
result.kind = pkTitle result.kind = pkTitle
proc unicodeWhitespace*: TPeg {.inline.} = proc unicodeWhitespace*: Peg {.inline.} =
## constructs the PEG ``\white`` which matches any Unicode ## constructs the PEG ``\white`` which matches any Unicode
## whitespace character. ## whitespace character.
result.kind = pkWhitespace result.kind = pkWhitespace
proc startAnchor*: TPeg {.inline.} = proc startAnchor*: Peg {.inline.} =
## constructs the PEG ``^`` which matches the start of the input. ## constructs the PEG ``^`` which matches the start of the input.
result.kind = pkStartAnchor result.kind = pkStartAnchor
proc endAnchor*: TPeg {.inline.} = proc endAnchor*: Peg {.inline.} =
## constructs the PEG ``$`` which matches the end of the input. ## constructs the PEG ``$`` which matches the end of the input.
result = !any() result = !any()
proc capture*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsCapture".} = proc capture*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsCapture".} =
## constructs a capture with the PEG `a` ## constructs a capture with the PEG `a`
result.kind = pkCapture result.kind = pkCapture
result.sons = @[a] result.sons = @[a]
proc backref*(index: range[1..MaxSubPatterns]): TPeg {. proc backref*(index: range[1..MaxSubPatterns]): Peg {.
nosideEffect, rtl, extern: "npegs$1".} = nosideEffect, rtl, extern: "npegs$1".} =
## constructs a back reference of the given `index`. `index` starts counting ## constructs a back reference of the given `index`. `index` starts counting
## from 1. ## from 1.
result.kind = pkBackRef result.kind = pkBackRef
result.index = index-1 result.index = index-1
proc backrefIgnoreCase*(index: range[1..MaxSubPatterns]): TPeg {. proc backrefIgnoreCase*(index: range[1..MaxSubPatterns]): Peg {.
nosideEffect, rtl, extern: "npegs$1".} = nosideEffect, rtl, extern: "npegs$1".} =
## constructs a back reference of the given `index`. `index` starts counting ## constructs a back reference of the given `index`. `index` starts counting
## from 1. Ignores case for matching. ## from 1. Ignores case for matching.
result.kind = pkBackRefIgnoreCase result.kind = pkBackRefIgnoreCase
result.index = index-1 result.index = index-1
proc backrefIgnoreStyle*(index: range[1..MaxSubPatterns]): TPeg {. proc backrefIgnoreStyle*(index: range[1..MaxSubPatterns]): Peg {.
nosideEffect, rtl, extern: "npegs$1".}= nosideEffect, rtl, extern: "npegs$1".}=
## constructs a back reference of the given `index`. `index` starts counting ## constructs a back reference of the given `index`. `index` starts counting
## from 1. Ignores style for matching. ## from 1. Ignores style for matching.
result.kind = pkBackRefIgnoreStyle result.kind = pkBackRefIgnoreStyle
result.index = index-1 result.index = index-1
proc spaceCost(n: TPeg): int = proc spaceCost(n: Peg): int =
case n.kind case n.kind
of pkEmpty: discard of pkEmpty: discard
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar,
@ -319,7 +309,7 @@ proc spaceCost(n: TPeg): int =
inc(result, spaceCost(n.sons[i])) inc(result, spaceCost(n.sons[i]))
if result >= InlineThreshold: break if result >= InlineThreshold: break
proc nonterminal*(n: PNonTerminal): TPeg {. proc nonterminal*(n: PNonTerminal): Peg {.
nosideEffect, rtl, extern: "npegs$1".} = nosideEffect, rtl, extern: "npegs$1".} =
## constructs a PEG that consists of the nonterminal symbol ## constructs a PEG that consists of the nonterminal symbol
assert n != nil assert n != nil
@ -415,7 +405,7 @@ proc charSetEsc(cc: set[char]): string =
else: else:
result = '[' & charSetEscAux(cc) & ']' result = '[' & charSetEscAux(cc) & ']'
proc toStrAux(r: TPeg, res: var string) = proc toStrAux(r: Peg, res: var string) =
case r.kind case r.kind
of pkEmpty: add(res, "()") of pkEmpty: add(res, "()")
of pkAny: add(res, '.') of pkAny: add(res, '.')
@ -501,7 +491,7 @@ proc toStrAux(r: TPeg, res: var string) =
of pkStartAnchor: of pkStartAnchor:
add(res, '^') add(res, '^')
proc `$` *(r: TPeg): string {.nosideEffect, rtl, extern: "npegsToString".} = proc `$` *(r: Peg): string {.nosideEffect, rtl, extern: "npegsToString".} =
## converts a PEG to its string representation ## converts a PEG to its string representation
result = "" result = ""
toStrAux(r, result) toStrAux(r, result)
@ -509,12 +499,14 @@ proc `$` *(r: TPeg): string {.nosideEffect, rtl, extern: "npegsToString".} =
# --------------------- core engine ------------------------------------------- # --------------------- core engine -------------------------------------------
type type
TCaptures* {.final.} = object ## contains the captured substrings. Captures* = object ## contains the captured substrings.
matches: array[0..maxSubpatterns-1, tuple[first, last: int]] matches: array[0..maxSubpatterns-1, tuple[first, last: int]]
ml: int ml: int
origStart: int origStart: int
proc bounds*(c: TCaptures, {.deprecated: [TCaptures: Captures].}
proc bounds*(c: Captures,
i: range[0..maxSubpatterns-1]): tuple[first, last: int] = i: range[0..maxSubpatterns-1]): tuple[first, last: int] =
## returns the bounds ``[first..last]`` of the `i`'th capture. ## returns the bounds ``[first..last]`` of the `i`'th capture.
result = c.matches[i] result = c.matches[i]
@ -533,7 +525,7 @@ when not useUnicode:
proc isTitle(a: char): bool {.inline.} = return false proc isTitle(a: char): bool {.inline.} = return false
proc isWhiteSpace(a: char): bool {.inline.} = return a in {' ', '\9'..'\13'} proc isWhiteSpace(a: char): bool {.inline.} = return a in {' ', '\9'..'\13'}
proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {. proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
nosideEffect, rtl, extern: "npegs$1".} = nosideEffect, rtl, extern: "npegs$1".} =
## low-level matching proc that implements the PEG interpreter. Use this ## low-level matching proc that implements the PEG interpreter. Use this
## for maximum efficiency (every other PEG operation ends up calling this ## for maximum efficiency (every other PEG operation ends up calling this
@ -734,7 +726,7 @@ proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {.
of pkBackRef..pkBackRefIgnoreStyle: of pkBackRef..pkBackRefIgnoreStyle:
if p.index >= c.ml: return -1 if p.index >= c.ml: return -1
var (a, b) = c.matches[p.index] var (a, b) = c.matches[p.index]
var n: TPeg var n: Peg
n.kind = succ(pkTerminal, ord(p.kind)-ord(pkBackRef)) n.kind = succ(pkTerminal, ord(p.kind)-ord(pkBackRef))
n.term = s.substr(a, b) n.term = s.substr(a, b)
result = rawMatch(s, n, start, c) result = rawMatch(s, n, start, c)
@ -747,51 +739,51 @@ template fillMatches(s, caps, c: expr) =
for k in 0..c.ml-1: for k in 0..c.ml-1:
caps[k] = substr(s, c.matches[k][0], c.matches[k][1]) caps[k] = substr(s, c.matches[k][0], c.matches[k][1])
proc match*(s: string, pattern: TPeg, matches: var openArray[string], proc match*(s: string, pattern: Peg, matches: var openArray[string],
start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} = start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} =
## returns ``true`` if ``s[start..]`` matches the ``pattern`` and ## returns ``true`` if ``s[start..]`` matches the ``pattern`` and
## the captured substrings in the array ``matches``. If it does not ## the captured substrings in the array ``matches``. If it does not
## match, nothing is written into ``matches`` and ``false`` is ## match, nothing is written into ``matches`` and ``false`` is
## returned. ## returned.
var c: TCaptures var c: Captures
c.origStart = start c.origStart = start
result = rawMatch(s, pattern, start, c) == len(s) - start result = rawMatch(s, pattern, start, c) == len(s) - start
if result: fillMatches(s, matches, c) if result: fillMatches(s, matches, c)
proc match*(s: string, pattern: TPeg, proc match*(s: string, pattern: Peg,
start = 0): bool {.nosideEffect, rtl, extern: "npegs$1".} = start = 0): bool {.nosideEffect, rtl, extern: "npegs$1".} =
## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``. ## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``.
var c: TCaptures var c: Captures
c.origStart = start c.origStart = start
result = rawMatch(s, pattern, start, c) == len(s)-start result = rawMatch(s, pattern, start, c) == len(s)-start
proc matchLen*(s: string, pattern: TPeg, matches: var openArray[string], proc matchLen*(s: string, pattern: Peg, matches: var openArray[string],
start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} = start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
## the same as ``match``, but it returns the length of the match, ## the same as ``match``, but it returns the length of the match,
## if there is no match, -1 is returned. Note that a match length ## if there is no match, -1 is returned. Note that a match length
## of zero can happen. It's possible that a suffix of `s` remains ## of zero can happen. It's possible that a suffix of `s` remains
## that does not belong to the match. ## that does not belong to the match.
var c: TCaptures var c: Captures
c.origStart = start c.origStart = start
result = rawMatch(s, pattern, start, c) result = rawMatch(s, pattern, start, c)
if result >= 0: fillMatches(s, matches, c) if result >= 0: fillMatches(s, matches, c)
proc matchLen*(s: string, pattern: TPeg, proc matchLen*(s: string, pattern: Peg,
start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} = start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} =
## the same as ``match``, but it returns the length of the match, ## the same as ``match``, but it returns the length of the match,
## if there is no match, -1 is returned. Note that a match length ## if there is no match, -1 is returned. Note that a match length
## of zero can happen. It's possible that a suffix of `s` remains ## of zero can happen. It's possible that a suffix of `s` remains
## that does not belong to the match. ## that does not belong to the match.
var c: TCaptures var c: Captures
c.origStart = start c.origStart = start
result = rawMatch(s, pattern, start, c) result = rawMatch(s, pattern, start, c)
proc find*(s: string, pattern: TPeg, matches: var openArray[string], proc find*(s: string, pattern: Peg, matches: var openArray[string],
start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} = start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
## returns the starting position of ``pattern`` in ``s`` and the captured ## returns the starting position of ``pattern`` in ``s`` and the captured
## substrings in the array ``matches``. If it does not match, nothing ## substrings in the array ``matches``. If it does not match, nothing
## is written into ``matches`` and -1 is returned. ## is written into ``matches`` and -1 is returned.
var c: TCaptures var c: Captures
c.origStart = start c.origStart = start
for i in start .. s.len-1: for i in start .. s.len-1:
c.ml = 0 c.ml = 0
@ -801,14 +793,14 @@ proc find*(s: string, pattern: TPeg, matches: var openArray[string],
return -1 return -1
# could also use the pattern here: (!P .)* P # could also use the pattern here: (!P .)* P
proc findBounds*(s: string, pattern: TPeg, matches: var openArray[string], proc findBounds*(s: string, pattern: Peg, matches: var openArray[string],
start = 0): tuple[first, last: int] {. start = 0): tuple[first, last: int] {.
nosideEffect, rtl, extern: "npegs$1Capture".} = nosideEffect, rtl, extern: "npegs$1Capture".} =
## returns the starting position and end position of ``pattern`` in ``s`` ## returns the starting position and end position of ``pattern`` in ``s``
## and the captured ## and the captured
## substrings in the array ``matches``. If it does not match, nothing ## substrings in the array ``matches``. If it does not match, nothing
## is written into ``matches`` and (-1,0) is returned. ## is written into ``matches`` and (-1,0) is returned.
var c: TCaptures var c: Captures
c.origStart = start c.origStart = start
for i in start .. s.len-1: for i in start .. s.len-1:
c.ml = 0 c.ml = 0
@ -818,19 +810,19 @@ proc findBounds*(s: string, pattern: TPeg, matches: var openArray[string],
return (i, i+L-1) return (i, i+L-1)
return (-1, 0) return (-1, 0)
proc find*(s: string, pattern: TPeg, proc find*(s: string, pattern: Peg,
start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} = start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} =
## returns the starting position of ``pattern`` in ``s``. If it does not ## returns the starting position of ``pattern`` in ``s``. If it does not
## match, -1 is returned. ## match, -1 is returned.
var c: TCaptures var c: Captures
c.origStart = start c.origStart = start
for i in start .. s.len-1: for i in start .. s.len-1:
if rawMatch(s, pattern, i, c) >= 0: return i if rawMatch(s, pattern, i, c) >= 0: return i
return -1 return -1
iterator findAll*(s: string, pattern: TPeg, start = 0): string = iterator findAll*(s: string, pattern: Peg, start = 0): string =
## yields all matching *substrings* of `s` that match `pattern`. ## yields all matching *substrings* of `s` that match `pattern`.
var c: TCaptures var c: Captures
c.origStart = start c.origStart = start
var i = start var i = start
while i < s.len: while i < s.len:
@ -842,7 +834,7 @@ iterator findAll*(s: string, pattern: TPeg, start = 0): string =
yield substr(s, i, i+L-1) yield substr(s, i, i+L-1)
inc(i, L) inc(i, L)
proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {. proc findAll*(s: string, pattern: Peg, start = 0): seq[string] {.
nosideEffect, rtl, extern: "npegs$1".} = nosideEffect, rtl, extern: "npegs$1".} =
## returns all matching *substrings* of `s` that match `pattern`. ## returns all matching *substrings* of `s` that match `pattern`.
## If it does not match, @[] is returned. ## If it does not match, @[] is returned.
@ -851,11 +843,11 @@ proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {.
when not defined(nimhygiene): when not defined(nimhygiene):
{.pragma: inject.} {.pragma: inject.}
template `=~`*(s: string, pattern: TPeg): bool = template `=~`*(s: string, pattern: Peg): bool =
## This calls ``match`` with an implicit declared ``matches`` array that ## This calls ``match`` with an implicit declared ``matches`` array that
## can be used in the scope of the ``=~`` call: ## can be used in the scope of the ``=~`` call:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## ##
## if line =~ peg"\s* {\w+} \s* '=' \s* {\w+}": ## if line =~ peg"\s* {\w+} \s* '=' \s* {\w+}":
## # matches a key=value pair: ## # matches a key=value pair:
@ -876,46 +868,46 @@ template `=~`*(s: string, pattern: TPeg): bool =
# ------------------------- more string handling ------------------------------ # ------------------------- more string handling ------------------------------
proc contains*(s: string, pattern: TPeg, start = 0): bool {. proc contains*(s: string, pattern: Peg, start = 0): bool {.
nosideEffect, rtl, extern: "npegs$1".} = nosideEffect, rtl, extern: "npegs$1".} =
## same as ``find(s, pattern, start) >= 0`` ## same as ``find(s, pattern, start) >= 0``
return find(s, pattern, start) >= 0 return find(s, pattern, start) >= 0
proc contains*(s: string, pattern: TPeg, matches: var openArray[string], proc contains*(s: string, pattern: Peg, matches: var openArray[string],
start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} = start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} =
## same as ``find(s, pattern, matches, start) >= 0`` ## same as ``find(s, pattern, matches, start) >= 0``
return find(s, pattern, matches, start) >= 0 return find(s, pattern, matches, start) >= 0
proc startsWith*(s: string, prefix: TPeg, start = 0): bool {. proc startsWith*(s: string, prefix: Peg, start = 0): bool {.
nosideEffect, rtl, extern: "npegs$1".} = nosideEffect, rtl, extern: "npegs$1".} =
## returns true if `s` starts with the pattern `prefix` ## returns true if `s` starts with the pattern `prefix`
result = matchLen(s, prefix, start) >= 0 result = matchLen(s, prefix, start) >= 0
proc endsWith*(s: string, suffix: TPeg, start = 0): bool {. proc endsWith*(s: string, suffix: Peg, start = 0): bool {.
nosideEffect, rtl, extern: "npegs$1".} = nosideEffect, rtl, extern: "npegs$1".} =
## returns true if `s` ends with the pattern `prefix` ## returns true if `s` ends with the pattern `prefix`
var c: TCaptures var c: Captures
c.origStart = start c.origStart = start
for i in start .. s.len-1: for i in start .. s.len-1:
if rawMatch(s, suffix, i, c) == s.len - i: return true if rawMatch(s, suffix, i, c) == s.len - i: return true
proc replacef*(s: string, sub: TPeg, by: string): string {. proc replacef*(s: string, sub: Peg, by: string): string {.
nosideEffect, rtl, extern: "npegs$1".} = nosideEffect, rtl, extern: "npegs$1".} =
## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by` ## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by`
## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples: ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## "var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2") ## "var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2")
## ##
## Results in: ## Results in:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## ##
## "var1<-keykey; val2<-key2key2" ## "var1<-keykey; val2<-key2key2"
result = "" result = ""
var i = 0 var i = 0
var caps: array[0..maxSubpatterns-1, string] var caps: array[0..maxSubpatterns-1, string]
var c: TCaptures var c: Captures
while i < s.len: while i < s.len:
c.ml = 0 c.ml = 0
var x = rawMatch(s, sub, i, c) var x = rawMatch(s, sub, i, c)
@ -928,13 +920,13 @@ proc replacef*(s: string, sub: TPeg, by: string): string {.
inc(i, x) inc(i, x)
add(result, substr(s, i)) add(result, substr(s, i))
proc replace*(s: string, sub: TPeg, by = ""): string {. proc replace*(s: string, sub: Peg, by = ""): string {.
nosideEffect, rtl, extern: "npegs$1".} = nosideEffect, rtl, extern: "npegs$1".} =
## Replaces `sub` in `s` by the string `by`. Captures cannot be accessed ## Replaces `sub` in `s` by the string `by`. Captures cannot be accessed
## in `by`. ## in `by`.
result = "" result = ""
var i = 0 var i = 0
var c: TCaptures var c: Captures
while i < s.len: while i < s.len:
var x = rawMatch(s, sub, i, c) var x = rawMatch(s, sub, i, c)
if x <= 0: if x <= 0:
@ -946,13 +938,13 @@ proc replace*(s: string, sub: TPeg, by = ""): string {.
add(result, substr(s, i)) add(result, substr(s, i))
proc parallelReplace*(s: string, subs: varargs[ proc parallelReplace*(s: string, subs: varargs[
tuple[pattern: TPeg, repl: string]]): string {. tuple[pattern: Peg, repl: string]]): string {.
nosideEffect, rtl, extern: "npegs$1".} = nosideEffect, rtl, extern: "npegs$1".} =
## Returns a modified copy of `s` with the substitutions in `subs` ## Returns a modified copy of `s` with the substitutions in `subs`
## applied in parallel. ## applied in parallel.
result = "" result = ""
var i = 0 var i = 0
var c: TCaptures var c: Captures
var caps: array[0..maxSubpatterns-1, string] var caps: array[0..maxSubpatterns-1, string]
while i < s.len: while i < s.len:
block searchSubs: block searchSubs:
@ -970,7 +962,7 @@ proc parallelReplace*(s: string, subs: varargs[
add(result, substr(s, i)) add(result, substr(s, i))
proc transformFile*(infile, outfile: string, proc transformFile*(infile, outfile: string,
subs: varargs[tuple[pattern: TPeg, repl: string]]) {. subs: varargs[tuple[pattern: Peg, repl: string]]) {.
rtl, extern: "npegs$1".} = rtl, extern: "npegs$1".} =
## reads in the file `infile`, performs a parallel replacement (calls ## reads in the file `infile`, performs a parallel replacement (calls
## `parallelReplace`) and writes back to `outfile`. Raises ``EIO`` if an ## `parallelReplace`) and writes back to `outfile`. Raises ``EIO`` if an
@ -978,25 +970,25 @@ proc transformFile*(infile, outfile: string,
var x = readFile(infile).string var x = readFile(infile).string
writeFile(outfile, x.parallelReplace(subs)) writeFile(outfile, x.parallelReplace(subs))
iterator split*(s: string, sep: TPeg): string = iterator split*(s: string, sep: Peg): string =
## Splits the string `s` into substrings. ## Splits the string `s` into substrings.
## ##
## Substrings are separated by the PEG `sep`. ## Substrings are separated by the PEG `sep`.
## Examples: ## Examples:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## for word in split("00232this02939is39an22example111", peg"\d+"): ## for word in split("00232this02939is39an22example111", peg"\d+"):
## writeln(stdout, word) ## writeln(stdout, word)
## ##
## Results in: ## Results in:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## "this" ## "this"
## "is" ## "is"
## "an" ## "an"
## "example" ## "example"
## ##
var c: TCaptures var c: Captures
var var
first = 0 first = 0
last = 0 last = 0
@ -1013,7 +1005,7 @@ iterator split*(s: string, sep: TPeg): string =
if first < last: if first < last:
yield substr(s, first, last-1) yield substr(s, first, last-1)
proc split*(s: string, sep: TPeg): seq[string] {. proc split*(s: string, sep: Peg): seq[string] {.
nosideEffect, rtl, extern: "npegs$1".} = nosideEffect, rtl, extern: "npegs$1".} =
## Splits the string `s` into substrings. ## Splits the string `s` into substrings.
accumulateResult(split(s, sep)) accumulateResult(split(s, sep))
@ -1060,7 +1052,7 @@ type
charset: set[char] ## if kind == tkCharSet charset: set[char] ## if kind == tkCharSet
index: int ## if kind == tkBackref index: int ## if kind == tkBackref
TPegLexer {.inheritable.} = object ## the lexer object. PegLexer {.inheritable.} = object ## the lexer object.
bufpos: int ## the current position within the buffer bufpos: int ## the current position within the buffer
buf: cstring ## the buffer itself buf: cstring ## the buffer itself
LineNumber: int ## the current line number LineNumber: int ## the current line number
@ -1076,20 +1068,20 @@ const
"@", "built-in", "escaped", "$", "$", "^" "@", "built-in", "escaped", "$", "$", "^"
] ]
proc handleCR(L: var TPegLexer, pos: int): int = proc handleCR(L: var PegLexer, pos: int): int =
assert(L.buf[pos] == '\c') assert(L.buf[pos] == '\c')
inc(L.linenumber) inc(L.linenumber)
result = pos+1 result = pos+1
if L.buf[result] == '\L': inc(result) if L.buf[result] == '\L': inc(result)
L.lineStart = result L.lineStart = result
proc handleLF(L: var TPegLexer, pos: int): int = proc handleLF(L: var PegLexer, pos: int): int =
assert(L.buf[pos] == '\L') assert(L.buf[pos] == '\L')
inc(L.linenumber) inc(L.linenumber)
result = pos+1 result = pos+1
L.lineStart = result L.lineStart = result
proc init(L: var TPegLexer, input, filename: string, line = 1, col = 0) = proc init(L: var PegLexer, input, filename: string, line = 1, col = 0) =
L.buf = input L.buf = input
L.bufpos = 0 L.bufpos = 0
L.lineNumber = line L.lineNumber = line
@ -1097,18 +1089,18 @@ proc init(L: var TPegLexer, input, filename: string, line = 1, col = 0) =
L.lineStart = 0 L.lineStart = 0
L.filename = filename L.filename = filename
proc getColumn(L: TPegLexer): int {.inline.} = proc getColumn(L: PegLexer): int {.inline.} =
result = abs(L.bufpos - L.lineStart) + L.colOffset result = abs(L.bufpos - L.lineStart) + L.colOffset
proc getLine(L: TPegLexer): int {.inline.} = proc getLine(L: PegLexer): int {.inline.} =
result = L.linenumber result = L.linenumber
proc errorStr(L: TPegLexer, msg: string, line = -1, col = -1): string = proc errorStr(L: PegLexer, msg: string, line = -1, col = -1): string =
var line = if line < 0: getLine(L) else: line var line = if line < 0: getLine(L) else: line
var col = if col < 0: getColumn(L) else: col var col = if col < 0: getColumn(L) else: col
result = "$1($2, $3) Error: $4" % [L.filename, $line, $col, msg] result = "$1($2, $3) Error: $4" % [L.filename, $line, $col, msg]
proc handleHexChar(c: var TPegLexer, xi: var int) = proc handleHexChar(c: var PegLexer, xi: var int) =
case c.buf[c.bufpos] case c.buf[c.bufpos]
of '0'..'9': of '0'..'9':
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0')) xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0'))
@ -1121,7 +1113,7 @@ proc handleHexChar(c: var TPegLexer, xi: var int) =
inc(c.bufpos) inc(c.bufpos)
else: discard else: discard
proc getEscapedChar(c: var TPegLexer, tok: var TToken) = proc getEscapedChar(c: var PegLexer, tok: var TToken) =
inc(c.bufpos) inc(c.bufpos)
case c.buf[c.bufpos] case c.buf[c.bufpos]
of 'r', 'R', 'c', 'C': of 'r', 'R', 'c', 'C':
@ -1173,7 +1165,7 @@ proc getEscapedChar(c: var TPegLexer, tok: var TToken) =
add(tok.literal, c.buf[c.bufpos]) add(tok.literal, c.buf[c.bufpos])
inc(c.bufpos) inc(c.bufpos)
proc skip(c: var TPegLexer) = proc skip(c: var PegLexer) =
var pos = c.bufpos var pos = c.bufpos
var buf = c.buf var buf = c.buf
while true: while true:
@ -1192,7 +1184,7 @@ proc skip(c: var TPegLexer) =
break # EndOfFile also leaves the loop break # EndOfFile also leaves the loop
c.bufpos = pos c.bufpos = pos
proc getString(c: var TPegLexer, tok: var TToken) = proc getString(c: var PegLexer, tok: var TToken) =
tok.kind = tkStringLit tok.kind = tkStringLit
var pos = c.bufPos + 1 var pos = c.bufPos + 1
var buf = c.buf var buf = c.buf
@ -1214,7 +1206,7 @@ proc getString(c: var TPegLexer, tok: var TToken) =
inc(pos) inc(pos)
c.bufpos = pos c.bufpos = pos
proc getDollar(c: var TPegLexer, tok: var TToken) = proc getDollar(c: var PegLexer, tok: var TToken) =
var pos = c.bufPos + 1 var pos = c.bufPos + 1
var buf = c.buf var buf = c.buf
if buf[pos] in {'0'..'9'}: if buf[pos] in {'0'..'9'}:
@ -1227,7 +1219,7 @@ proc getDollar(c: var TPegLexer, tok: var TToken) =
tok.kind = tkDollar tok.kind = tkDollar
c.bufpos = pos c.bufpos = pos
proc getCharSet(c: var TPegLexer, tok: var TToken) = proc getCharSet(c: var PegLexer, tok: var TToken) =
tok.kind = tkCharSet tok.kind = tkCharSet
tok.charset = {} tok.charset = {}
var pos = c.bufPos + 1 var pos = c.bufPos + 1
@ -1278,7 +1270,7 @@ proc getCharSet(c: var TPegLexer, tok: var TToken) =
c.bufpos = pos c.bufpos = pos
if caret: tok.charset = {'\1'..'\xFF'} - tok.charset if caret: tok.charset = {'\1'..'\xFF'} - tok.charset
proc getSymbol(c: var TPegLexer, tok: var TToken) = proc getSymbol(c: var PegLexer, tok: var TToken) =
var pos = c.bufpos var pos = c.bufpos
var buf = c.buf var buf = c.buf
while true: while true:
@ -1288,7 +1280,7 @@ proc getSymbol(c: var TPegLexer, tok: var TToken) =
c.bufpos = pos c.bufpos = pos
tok.kind = tkIdentifier tok.kind = tkIdentifier
proc getBuiltin(c: var TPegLexer, tok: var TToken) = proc getBuiltin(c: var PegLexer, tok: var TToken) =
if c.buf[c.bufpos+1] in strutils.Letters: if c.buf[c.bufpos+1] in strutils.Letters:
inc(c.bufpos) inc(c.bufpos)
getSymbol(c, tok) getSymbol(c, tok)
@ -1297,7 +1289,7 @@ proc getBuiltin(c: var TPegLexer, tok: var TToken) =
tok.kind = tkEscaped tok.kind = tkEscaped
getEscapedChar(c, tok) # may set tok.kind to tkInvalid getEscapedChar(c, tok) # may set tok.kind to tkInvalid
proc getTok(c: var TPegLexer, tok: var TToken) = proc getTok(c: var PegLexer, tok: var TToken) =
tok.kind = tkInvalid tok.kind = tkInvalid
tok.modifier = modNone tok.modifier = modNone
setLen(tok.literal, 0) setLen(tok.literal, 0)
@ -1403,7 +1395,7 @@ proc getTok(c: var TPegLexer, tok: var TToken) =
add(tok.literal, c.buf[c.bufpos]) add(tok.literal, c.buf[c.bufpos])
inc(c.bufpos) inc(c.bufpos)
proc arrowIsNextTok(c: TPegLexer): bool = proc arrowIsNextTok(c: PegLexer): bool =
# the only look ahead we need # the only look ahead we need
var pos = c.bufpos var pos = c.bufpos
while c.buf[pos] in {'\t', ' '}: inc(pos) while c.buf[pos] in {'\t', ' '}: inc(pos)
@ -1414,31 +1406,31 @@ proc arrowIsNextTok(c: TPegLexer): bool =
type type
EInvalidPeg* = object of EInvalidValue ## raised if an invalid EInvalidPeg* = object of EInvalidValue ## raised if an invalid
## PEG has been detected ## PEG has been detected
TPegParser = object of TPegLexer ## the PEG parser object PegParser = object of PegLexer ## the PEG parser object
tok: TToken tok: TToken
nonterms: seq[PNonTerminal] nonterms: seq[PNonTerminal]
modifier: TModifier modifier: TModifier
captures: int captures: int
identIsVerbatim: bool identIsVerbatim: bool
skip: TPeg skip: Peg
proc pegError(p: TPegParser, msg: string, line = -1, col = -1) = proc pegError(p: PegParser, msg: string, line = -1, col = -1) =
var e: ref EInvalidPeg var e: ref EInvalidPeg
new(e) new(e)
e.msg = errorStr(p, msg, line, col) e.msg = errorStr(p, msg, line, col)
raise e raise e
proc getTok(p: var TPegParser) = proc getTok(p: var PegParser) =
getTok(p, p.tok) getTok(p, p.tok)
if p.tok.kind == tkInvalid: pegError(p, "invalid token") if p.tok.kind == tkInvalid: pegError(p, "invalid token")
proc eat(p: var TPegParser, kind: TTokKind) = proc eat(p: var PegParser, kind: TTokKind) =
if p.tok.kind == kind: getTok(p) if p.tok.kind == kind: getTok(p)
else: pegError(p, tokKindToStr[kind] & " expected") else: pegError(p, tokKindToStr[kind] & " expected")
proc parseExpr(p: var TPegParser): TPeg proc parseExpr(p: var PegParser): Peg
proc getNonTerminal(p: var TPegParser, name: string): PNonTerminal = proc getNonTerminal(p: var PegParser, name: string): PNonTerminal =
for i in 0..high(p.nonterms): for i in 0..high(p.nonterms):
result = p.nonterms[i] result = p.nonterms[i]
if cmpIgnoreStyle(result.name, name) == 0: return if cmpIgnoreStyle(result.name, name) == 0: return
@ -1446,19 +1438,19 @@ proc getNonTerminal(p: var TPegParser, name: string): PNonTerminal =
result = newNonTerminal(name, getLine(p), getColumn(p)) result = newNonTerminal(name, getLine(p), getColumn(p))
add(p.nonterms, result) add(p.nonterms, result)
proc modifiedTerm(s: string, m: TModifier): TPeg = proc modifiedTerm(s: string, m: TModifier): Peg =
case m case m
of modNone, modVerbatim: result = term(s) of modNone, modVerbatim: result = term(s)
of modIgnoreCase: result = termIgnoreCase(s) of modIgnoreCase: result = termIgnoreCase(s)
of modIgnoreStyle: result = termIgnoreStyle(s) of modIgnoreStyle: result = termIgnoreStyle(s)
proc modifiedBackref(s: int, m: TModifier): TPeg = proc modifiedBackref(s: int, m: TModifier): Peg =
case m case m
of modNone, modVerbatim: result = backref(s) of modNone, modVerbatim: result = backref(s)
of modIgnoreCase: result = backrefIgnoreCase(s) of modIgnoreCase: result = backrefIgnoreCase(s)
of modIgnoreStyle: result = backrefIgnoreStyle(s) of modIgnoreStyle: result = backrefIgnoreStyle(s)
proc builtin(p: var TPegParser): TPeg = proc builtin(p: var PegParser): Peg =
# do not use "y", "skip" or "i" as these would be ambiguous # do not use "y", "skip" or "i" as these would be ambiguous
case p.tok.literal case p.tok.literal
of "n": result = newLine() of "n": result = newLine()
@ -1478,11 +1470,11 @@ proc builtin(p: var TPegParser): TPeg =
of "white": result = unicodeWhitespace() of "white": result = unicodeWhitespace()
else: pegError(p, "unknown built-in: " & p.tok.literal) else: pegError(p, "unknown built-in: " & p.tok.literal)
proc token(terminal: TPeg, p: TPegParser): TPeg = proc token(terminal: Peg, p: PegParser): Peg =
if p.skip.kind == pkEmpty: result = terminal if p.skip.kind == pkEmpty: result = terminal
else: result = sequence(p.skip, terminal) else: result = sequence(p.skip, terminal)
proc primary(p: var TPegParser): TPeg = proc primary(p: var PegParser): Peg =
case p.tok.kind case p.tok.kind
of tkAmp: of tkAmp:
getTok(p) getTok(p)
@ -1571,7 +1563,7 @@ proc primary(p: var TPegParser): TPeg =
getTok(p) getTok(p)
else: break else: break
proc seqExpr(p: var TPegParser): TPeg = proc seqExpr(p: var PegParser): Peg =
result = primary(p) result = primary(p)
while true: while true:
case p.tok.kind case p.tok.kind
@ -1585,13 +1577,13 @@ proc seqExpr(p: var TPegParser): TPeg =
else: break else: break
else: break else: break
proc parseExpr(p: var TPegParser): TPeg = proc parseExpr(p: var PegParser): Peg =
result = seqExpr(p) result = seqExpr(p)
while p.tok.kind == tkBar: while p.tok.kind == tkBar:
getTok(p) getTok(p)
result = result / seqExpr(p) result = result / seqExpr(p)
proc parseRule(p: var TPegParser): PNonTerminal = proc parseRule(p: var PegParser): PNonTerminal =
if p.tok.kind == tkIdentifier and arrowIsNextTok(p): if p.tok.kind == tkIdentifier and arrowIsNextTok(p):
result = getNonTerminal(p, p.tok.literal) result = getNonTerminal(p, p.tok.literal)
if ntDeclared in result.flags: if ntDeclared in result.flags:
@ -1605,7 +1597,7 @@ proc parseRule(p: var TPegParser): PNonTerminal =
else: else:
pegError(p, "rule expected, but found: " & p.tok.literal) pegError(p, "rule expected, but found: " & p.tok.literal)
proc rawParse(p: var TPegParser): TPeg = proc rawParse(p: var PegParser): Peg =
## parses a rule or a PEG expression ## parses a rule or a PEG expression
while p.tok.kind == tkBuiltin: while p.tok.kind == tkBuiltin:
case p.tok.literal case p.tok.literal
@ -1635,12 +1627,12 @@ proc rawParse(p: var TPegParser): TPeg =
elif ntUsed notin nt.flags and i > 0: elif ntUsed notin nt.flags and i > 0:
pegError(p, "unused rule: " & nt.name, nt.line, nt.col) pegError(p, "unused rule: " & nt.name, nt.line, nt.col)
proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): TPeg = proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): Peg =
## constructs a TPeg object from `pattern`. `filename`, `line`, `col` are ## constructs a Peg object from `pattern`. `filename`, `line`, `col` are
## used for error messages, but they only provide start offsets. `parsePeg` ## used for error messages, but they only provide start offsets. `parsePeg`
## keeps track of line and column numbers within `pattern`. ## keeps track of line and column numbers within `pattern`.
var p: TPegParser var p: PegParser
init(TPegLexer(p), pattern, filename, line, col) init(PegLexer(p), pattern, filename, line, col)
p.tok.kind = tkInvalid p.tok.kind = tkInvalid
p.tok.modifier = modNone p.tok.modifier = modNone
p.tok.literal = "" p.tok.literal = ""
@ -1650,8 +1642,8 @@ proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): TPeg =
getTok(p) getTok(p)
result = rawParse(p) result = rawParse(p)
proc peg*(pattern: string): TPeg = proc peg*(pattern: string): Peg =
## constructs a TPeg object from the `pattern`. The short name has been ## constructs a Peg object from the `pattern`. The short name has been
## chosen to encourage its use as a raw string modifier:: ## chosen to encourage its use as a raw string modifier::
## ##
## peg"{\ident} \s* '=' \s* {.*}" ## peg"{\ident} \s* '=' \s* {.*}"
@ -1704,7 +1696,7 @@ when isMainModule:
expr.rule = sequence(capture(ident), *sequence( expr.rule = sequence(capture(ident), *sequence(
nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr))) nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr)))
var c: TCaptures var c: Captures
var s = "a+b + c +d+e+f" var s = "a+b + c +d+e+f"
assert rawMatch(s, expr.rule, 0, c) == len(s) assert rawMatch(s, expr.rule, 0, c) == len(s)
var a = "" var a = ""

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2013 Robert Persson # (c) Copyright 2013 Robert Persson
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -12,10 +12,11 @@ import strutils
import numeric import numeric
type type
TPoly* = object Poly* = object
cofs:seq[float] cofs:seq[float]
{.deprecated: [TPoly: Poly].}
proc degree*(p:TPoly):int= proc degree*(p:TPoly):int=
## Returns the degree of the polynomial, ## Returns the degree of the polynomial,
## that is the number of coefficients-1 ## that is the number of coefficients-1
@ -104,7 +105,7 @@ proc `$` *(p:TPoly):string =
result="0" result="0"
proc derivative*(p:TPoly):TPoly= proc derivative*(p: TPoly): TPoly=
## Returns a new polynomial, which is the derivative of `p` ## Returns a new polynomial, which is the derivative of `p`
newSeq[float](result.cofs,p.degree) newSeq[float](result.cofs,p.degree)
for idx in 0..high(result.cofs): for idx in 0..high(result.cofs):
@ -310,10 +311,10 @@ proc getRangeForRoots(p:TPoly):tuple[xmin,xmax:float]=
var bound1,bound2:float var bound1,bound2:float
for i in countup(0,deg): for i in countup(0,deg):
var c=abs(p.cofs[i]/d) var c=abs(p.cofs[i]/d)
bound1=max(bound1,c+1.0) bound1=max(bound1,c+1.0)
bound2=bound2+c bound2=bound2+c
bound2=max(1.0,bound2) bound2=max(1.0,bound2)
result.xmax=min(bound1,bound2) result.xmax=min(bound1,bound2)
result.xmin= -result.xmax result.xmin= -result.xmax

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2014 Dominik Picheta # (c) Copyright 2014 Dominik Picheta
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -28,7 +28,7 @@ else:
export fcntl, F_GETFL, O_NONBLOCK, F_SETFL, EAGAIN, EWOULDBLOCK, MSG_NOSIGNAL, export fcntl, F_GETFL, O_NONBLOCK, F_SETFL, EAGAIN, EWOULDBLOCK, MSG_NOSIGNAL,
EINTR, EINPROGRESS, ECONNRESET, EPIPE, ENETRESET EINTR, EINPROGRESS, ECONNRESET, EPIPE, ENETRESET
export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen, export SocketHandle, Sockaddr_in, Addrinfo, INADDR_ANY, SockAddr, SockLen,
inet_ntoa, recv, `==`, connect, send, accept, recvfrom, sendto inet_ntoa, recv, `==`, connect, send, accept, recvfrom, sendto
export export
@ -40,22 +40,22 @@ export
MSG_PEEK MSG_PEEK
type type
TPort* = distinct uint16 ## port type Port* = distinct uint16 ## port type
TDomain* = enum ## domain, which specifies the protocol family of the Domain* = enum ## domain, which specifies the protocol family of the
## created socket. Other domains than those that are listed ## created socket. Other domains than those that are listed
## here are unsupported. ## here are unsupported.
AF_UNIX, ## for local socket (using a file). Unsupported on Windows. AF_UNIX, ## for local socket (using a file). Unsupported on Windows.
AF_INET = 2, ## for network protocol IPv4 or AF_INET = 2, ## for network protocol IPv4 or
AF_INET6 = 23 ## for network protocol IPv6. AF_INET6 = 23 ## for network protocol IPv6.
TType* = enum ## second argument to `socket` proc SockType* = enum ## second argument to `socket` proc
SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets
SOCK_DGRAM = 2, ## datagram service or Datagram Sockets SOCK_DGRAM = 2, ## datagram service or Datagram Sockets
SOCK_RAW = 3, ## raw protocols atop the network layer. SOCK_RAW = 3, ## raw protocols atop the network layer.
SOCK_SEQPACKET = 5 ## reliable sequenced packet service SOCK_SEQPACKET = 5 ## reliable sequenced packet service
TProtocol* = enum ## third argument to `socket` proc Protocol* = enum ## third argument to `socket` proc
IPPROTO_TCP = 6, ## Transmission control protocol. IPPROTO_TCP = 6, ## Transmission control protocol.
IPPROTO_UDP = 17, ## User datagram protocol. IPPROTO_UDP = 17, ## User datagram protocol.
IPPROTO_IP, ## Internet protocol. Unsupported on Windows. IPPROTO_IP, ## Internet protocol. Unsupported on Windows.
@ -63,19 +63,22 @@ type
IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows. IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows.
IPPROTO_ICMP ## Control message protocol. Unsupported on Windows. IPPROTO_ICMP ## Control message protocol. Unsupported on Windows.
TServent* {.pure, final.} = object ## information about a service Servent* = object ## information about a service
name*: string name*: string
aliases*: seq[string] aliases*: seq[string]
port*: TPort port*: Port
proto*: string proto*: string
Thostent* {.pure, final.} = object ## information about a given host Hostent* = object ## information about a given host
name*: string name*: string
aliases*: seq[string] aliases*: seq[string]
addrtype*: TDomain addrtype*: Domain
length*: int length*: int
addrList*: seq[string] addrList*: seq[string]
{.deprecated: [TPort: Port, TDomain: Domain, TType: SockType,
TProtocol: Protocol, TServent: Servent, THostent: Hostent].}
when useWinVersion: when useWinVersion:
let let
osInvalidSocket* = winlean.INVALID_SOCKET osInvalidSocket* = winlean.INVALID_SOCKET
@ -86,37 +89,37 @@ when useWinVersion:
FIONBIO* = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or FIONBIO* = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or
(102 shl 8) or 126 (102 shl 8) or 126
proc ioctlsocket*(s: TSocketHandle, cmd: clong, proc ioctlsocket*(s: SocketHandle, cmd: clong,
argptr: ptr clong): cint {. argptr: ptr clong): cint {.
stdcall, importc: "ioctlsocket", dynlib: "ws2_32.dll".} stdcall, importc: "ioctlsocket", dynlib: "ws2_32.dll".}
else: else:
let let
osInvalidSocket* = posix.INVALID_SOCKET osInvalidSocket* = posix.INVALID_SOCKET
proc `==`*(a, b: TPort): bool {.borrow.} proc `==`*(a, b: Port): bool {.borrow.}
## ``==`` for ports. ## ``==`` for ports.
proc `$`*(p: TPort): string {.borrow.} proc `$`*(p: Port): string {.borrow.}
## returns the port number as a string ## returns the port number as a string
proc toInt*(domain: TDomain): cint proc toInt*(domain: Domain): cint
## Converts the TDomain enum to a platform-dependent ``cint``. ## Converts the TDomain enum to a platform-dependent ``cint``.
proc toInt*(typ: TType): cint proc toInt*(typ: SockType): cint
## Converts the TType enum to a platform-dependent ``cint``. ## Converts the TType enum to a platform-dependent ``cint``.
proc toInt*(p: TProtocol): cint proc toInt*(p: Protocol): cint
## Converts the TProtocol enum to a platform-dependent ``cint``. ## Converts the TProtocol enum to a platform-dependent ``cint``.
when not useWinVersion: when not useWinVersion:
proc toInt(domain: TDomain): cint = proc toInt(domain: Domain): cint =
case domain case domain
of AF_UNIX: result = posix.AF_UNIX of AF_UNIX: result = posix.AF_UNIX
of AF_INET: result = posix.AF_INET of AF_INET: result = posix.AF_INET
of AF_INET6: result = posix.AF_INET6 of AF_INET6: result = posix.AF_INET6
else: discard else: discard
proc toInt(typ: TType): cint = proc toInt(typ: SockType): cint =
case typ case typ
of SOCK_STREAM: result = posix.SOCK_STREAM of SOCK_STREAM: result = posix.SOCK_STREAM
of SOCK_DGRAM: result = posix.SOCK_DGRAM of SOCK_DGRAM: result = posix.SOCK_DGRAM
@ -124,7 +127,7 @@ when not useWinVersion:
of SOCK_RAW: result = posix.SOCK_RAW of SOCK_RAW: result = posix.SOCK_RAW
else: discard else: discard
proc toInt(p: TProtocol): cint = proc toInt(p: Protocol): cint =
case p case p
of IPPROTO_TCP: result = posix.IPPROTO_TCP of IPPROTO_TCP: result = posix.IPPROTO_TCP
of IPPROTO_UDP: result = posix.IPPROTO_UDP of IPPROTO_UDP: result = posix.IPPROTO_UDP
@ -135,22 +138,22 @@ when not useWinVersion:
else: discard else: discard
else: else:
proc toInt(domain: TDomain): cint = proc toInt(domain: Domain): cint =
result = toU16(ord(domain)) result = toU16(ord(domain))
proc toInt(typ: TType): cint = proc toInt(typ: SockType): cint =
result = cint(ord(typ)) result = cint(ord(typ))
proc toInt(p: TProtocol): cint = proc toInt(p: Protocol): cint =
result = cint(ord(p)) result = cint(ord(p))
proc newRawSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, proc newRawSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
protocol: TProtocol = IPPROTO_TCP): TSocketHandle = protocol: Protocol = IPPROTO_TCP): SocketHandle =
## Creates a new socket; returns `InvalidSocket` if an error occurs. ## Creates a new socket; returns `InvalidSocket` if an error occurs.
socket(toInt(domain), toInt(typ), toInt(protocol)) socket(toInt(domain), toInt(typ), toInt(protocol))
proc close*(socket: TSocketHandle) = proc close*(socket: SocketHandle) =
## closes a socket. ## closes a socket.
when useWinVersion: when useWinVersion:
discard winlean.closeSocket(socket) discard winlean.closeSocket(socket)
@ -159,10 +162,10 @@ proc close*(socket: TSocketHandle) =
# TODO: These values should not be discarded. An EOS should be raised. # TODO: These values should not be discarded. An EOS should be raised.
# http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times # http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times
proc bindAddr*(socket: TSocketHandle, name: ptr TSockAddr, namelen: TSockLen): cint = proc bindAddr*(socket: SocketHandle, name: ptr SockAddr, namelen: SockLen): cint =
result = bindSocket(socket, name, namelen) result = bindSocket(socket, name, namelen)
proc listen*(socket: TSocketHandle, backlog = SOMAXCONN): cint {.tags: [FReadIO].} = proc listen*(socket: SocketHandle, backlog = SOMAXCONN): cint {.tags: [FReadIO].} =
## Marks ``socket`` as accepting connections. ## Marks ``socket`` as accepting connections.
## ``Backlog`` specifies the maximum length of the ## ``Backlog`` specifies the maximum length of the
## queue of pending connections. ## queue of pending connections.
@ -171,12 +174,12 @@ proc listen*(socket: TSocketHandle, backlog = SOMAXCONN): cint {.tags: [FReadIO]
else: else:
result = posix.listen(socket, cint(backlog)) result = posix.listen(socket, cint(backlog))
proc getAddrInfo*(address: string, port: TPort, af: TDomain = AF_INET, typ: TType = SOCK_STREAM, proc getAddrInfo*(address: string, port: Port, af: Domain = AF_INET, typ: SockType = SOCK_STREAM,
prot: TProtocol = IPPROTO_TCP): ptr TAddrInfo = prot: Protocol = IPPROTO_TCP): ptr AddrInfo =
## ##
## ##
## **Warning**: The resulting ``ptr TAddrInfo`` must be freed using ``dealloc``! ## **Warning**: The resulting ``ptr TAddrInfo`` must be freed using ``dealloc``!
var hints: TAddrInfo var hints: AddrInfo
result = nil result = nil
hints.ai_family = toInt(af) hints.ai_family = toInt(af)
hints.ai_socktype = toInt(typ) hints.ai_socktype = toInt(typ)
@ -184,11 +187,11 @@ proc getAddrInfo*(address: string, port: TPort, af: TDomain = AF_INET, typ: TTyp
var gaiResult = getAddrInfo(address, $port, addr(hints), result) var gaiResult = getAddrInfo(address, $port, addr(hints), result)
if gaiResult != 0'i32: if gaiResult != 0'i32:
when useWinVersion: when useWinVersion:
osError(osLastError()) raiseOSError(osLastError())
else: else:
raise newException(EOS, $gai_strerror(gaiResult)) raise newException(EOS, $gai_strerror(gaiResult))
proc dealloc*(ai: ptr TAddrInfo) = proc dealloc*(ai: ptr AddrInfo) =
freeaddrinfo(ai) freeaddrinfo(ai)
proc ntohl*(x: int32): int32 = proc ntohl*(x: int32): int32 =
@ -220,7 +223,7 @@ proc htons*(x: int16): int16 =
## order, this is a no-op; otherwise, it performs a 2-byte swap operation. ## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
result = rawsockets.ntohs(x) result = rawsockets.ntohs(x)
proc getServByName*(name, proto: string): TServent {.tags: [FReadIO].} = proc getServByName*(name, proto: string): Servent {.tags: [FReadIO].} =
## Searches the database from the beginning and finds the first entry for ## Searches the database from the beginning and finds the first entry for
## which the service name specified by ``name`` matches the s_name member ## which the service name specified by ``name`` matches the s_name member
## and the protocol name specified by ``proto`` matches the s_proto member. ## and the protocol name specified by ``proto`` matches the s_proto member.
@ -233,10 +236,10 @@ proc getServByName*(name, proto: string): TServent {.tags: [FReadIO].} =
if s == nil: raise newException(EOS, "Service not found.") if s == nil: raise newException(EOS, "Service not found.")
result.name = $s.s_name result.name = $s.s_name
result.aliases = cstringArrayToSeq(s.s_aliases) result.aliases = cstringArrayToSeq(s.s_aliases)
result.port = TPort(s.s_port) result.port = Port(s.s_port)
result.proto = $s.s_proto result.proto = $s.s_proto
proc getServByPort*(port: TPort, proto: string): TServent {.tags: [FReadIO].} = proc getServByPort*(port: Port, proto: string): Servent {.tags: [FReadIO].} =
## Searches the database from the beginning and finds the first entry for ## Searches the database from the beginning and finds the first entry for
## which the port specified by ``port`` matches the s_port member and the ## which the port specified by ``port`` matches the s_port member and the
## protocol name specified by ``proto`` matches the s_proto member. ## protocol name specified by ``proto`` matches the s_proto member.
@ -249,28 +252,28 @@ proc getServByPort*(port: TPort, proto: string): TServent {.tags: [FReadIO].} =
if s == nil: raise newException(EOS, "Service not found.") if s == nil: raise newException(EOS, "Service not found.")
result.name = $s.s_name result.name = $s.s_name
result.aliases = cstringArrayToSeq(s.s_aliases) result.aliases = cstringArrayToSeq(s.s_aliases)
result.port = TPort(s.s_port) result.port = Port(s.s_port)
result.proto = $s.s_proto result.proto = $s.s_proto
proc getHostByAddr*(ip: string): Thostent {.tags: [FReadIO].} = proc getHostByAddr*(ip: string): Hostent {.tags: [FReadIO].} =
## This function will lookup the hostname of an IP Address. ## This function will lookup the hostname of an IP Address.
var myaddr: TInAddr var myaddr: InAddr
myaddr.s_addr = inet_addr(ip) myaddr.s_addr = inet_addr(ip)
when useWinVersion: when useWinVersion:
var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint, var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
cint(rawsockets.AF_INET)) cint(rawsockets.AF_INET))
if s == nil: osError(osLastError()) if s == nil: raiseOSError(osLastError())
else: else:
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).TSocklen, var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).Socklen,
cint(posix.AF_INET)) cint(posix.AF_INET))
if s == nil: if s == nil:
raise newException(EOS, $hstrerror(h_errno)) raise newException(EOS, $hstrerror(h_errno))
result.name = $s.h_name result.name = $s.h_name
result.aliases = cstringArrayToSeq(s.h_aliases) result.aliases = cstringArrayToSeq(s.h_aliases)
when useWinVersion: when useWinVersion:
result.addrtype = TDomain(s.h_addrtype) result.addrtype = Domain(s.h_addrtype)
else: else:
if s.h_addrtype == posix.AF_INET: if s.h_addrtype == posix.AF_INET:
result.addrtype = AF_INET result.addrtype = AF_INET
@ -281,17 +284,17 @@ proc getHostByAddr*(ip: string): Thostent {.tags: [FReadIO].} =
result.addrList = cstringArrayToSeq(s.h_addr_list) result.addrList = cstringArrayToSeq(s.h_addr_list)
result.length = int(s.h_length) result.length = int(s.h_length)
proc getHostByName*(name: string): Thostent {.tags: [FReadIO].} = proc getHostByName*(name: string): Hostent {.tags: [FReadIO].} =
## This function will lookup the IP address of a hostname. ## This function will lookup the IP address of a hostname.
when useWinVersion: when useWinVersion:
var s = winlean.gethostbyname(name) var s = winlean.gethostbyname(name)
else: else:
var s = posix.gethostbyname(name) var s = posix.gethostbyname(name)
if s == nil: osError(osLastError()) if s == nil: raiseOSError(osLastError())
result.name = $s.h_name result.name = $s.h_name
result.aliases = cstringArrayToSeq(s.h_aliases) result.aliases = cstringArrayToSeq(s.h_aliases)
when useWinVersion: when useWinVersion:
result.addrtype = TDomain(s.h_addrtype) result.addrtype = Domain(s.h_addrtype)
else: else:
if s.h_addrtype == posix.AF_INET: if s.h_addrtype == posix.AF_INET:
result.addrtype = AF_INET result.addrtype = AF_INET
@ -302,69 +305,69 @@ proc getHostByName*(name: string): Thostent {.tags: [FReadIO].} =
result.addrList = cstringArrayToSeq(s.h_addr_list) result.addrList = cstringArrayToSeq(s.h_addr_list)
result.length = int(s.h_length) result.length = int(s.h_length)
proc getSockName*(socket: TSocketHandle): TPort = proc getSockName*(socket: SocketHandle): Port =
## returns the socket's associated port number. ## returns the socket's associated port number.
var name: Tsockaddr_in var name: Sockaddr_in
when useWinVersion: when useWinVersion:
name.sin_family = int16(ord(AF_INET)) name.sin_family = int16(ord(AF_INET))
else: else:
name.sin_family = posix.AF_INET name.sin_family = posix.AF_INET
#name.sin_port = htons(cint16(port)) #name.sin_port = htons(cint16(port))
#name.sin_addr.s_addr = htonl(INADDR_ANY) #name.sin_addr.s_addr = htonl(INADDR_ANY)
var namelen = sizeof(name).TSocklen var namelen = sizeof(name).Socklen
if getsockname(socket, cast[ptr TSockAddr](addr(name)), if getsockname(socket, cast[ptr SockAddr](addr(name)),
addr(namelen)) == -1'i32: addr(namelen)) == -1'i32:
osError(osLastError()) raiseOSError(osLastError())
result = TPort(rawsockets.ntohs(name.sin_port)) result = Port(rawsockets.ntohs(name.sin_port))
proc getSockOptInt*(socket: TSocketHandle, level, optname: int): int {. proc getSockOptInt*(socket: SocketHandle, level, optname: int): int {.
tags: [FReadIO].} = tags: [FReadIO].} =
## getsockopt for integer options. ## getsockopt for integer options.
var res: cint var res: cint
var size = sizeof(res).TSocklen var size = sizeof(res).Socklen
if getsockopt(socket, cint(level), cint(optname), if getsockopt(socket, cint(level), cint(optname),
addr(res), addr(size)) < 0'i32: addr(res), addr(size)) < 0'i32:
osError(osLastError()) raiseOSError(osLastError())
result = int(res) result = int(res)
proc setSockOptInt*(socket: TSocketHandle, level, optname, optval: int) {. proc setSockOptInt*(socket: SocketHandle, level, optname, optval: int) {.
tags: [FWriteIO].} = tags: [FWriteIO].} =
## setsockopt for integer options. ## setsockopt for integer options.
var value = cint(optval) var value = cint(optval)
if setsockopt(socket, cint(level), cint(optname), addr(value), if setsockopt(socket, cint(level), cint(optname), addr(value),
sizeof(value).TSocklen) < 0'i32: sizeof(value).Socklen) < 0'i32:
osError(osLastError()) raiseOSError(osLastError())
proc setBlocking*(s: TSocketHandle, blocking: bool) = proc setBlocking*(s: SocketHandle, blocking: bool) =
## Sets blocking mode on socket. ## Sets blocking mode on socket.
## ##
## Raises EOS on error. ## Raises EOS on error.
when useWinVersion: when useWinVersion:
var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
if ioctlsocket(s, FIONBIO, addr(mode)) == -1: if ioctlsocket(s, FIONBIO, addr(mode)) == -1:
osError(osLastError()) raiseOSError(osLastError())
else: # BSD sockets else: # BSD sockets
var x: int = fcntl(s, F_GETFL, 0) var x: int = fcntl(s, F_GETFL, 0)
if x == -1: if x == -1:
osError(osLastError()) raiseOSError(osLastError())
else: else:
var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
if fcntl(s, F_SETFL, mode) == -1: if fcntl(s, F_SETFL, mode) == -1:
osError(osLastError()) raiseOSError(osLastError())
proc timeValFromMilliseconds(timeout = 500): Ttimeval = proc timeValFromMilliseconds(timeout = 500): Timeval =
if timeout != -1: if timeout != -1:
var seconds = timeout div 1000 var seconds = timeout div 1000
result.tv_sec = seconds.int32 result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
proc createFdSet(fd: var TFdSet, s: seq[TSocketHandle], m: var int) = proc createFdSet(fd: var FdSet, s: seq[SocketHandle], m: var int) =
FD_ZERO(fd) FD_ZERO(fd)
for i in items(s): for i in items(s):
m = max(m, int(i)) m = max(m, int(i))
FD_SET(i, fd) FD_SET(i, fd)
proc pruneSocketSet(s: var seq[TSocketHandle], fd: var TFdSet) = proc pruneSocketSet(s: var seq[SocketHandle], fd: var FdSet) =
var i = 0 var i = 0
var L = s.len var L = s.len
while i < L: while i < L:
@ -375,7 +378,7 @@ proc pruneSocketSet(s: var seq[TSocketHandle], fd: var TFdSet) =
inc(i) inc(i)
setLen(s, L) setLen(s, L)
proc select*(readfds: var seq[TSocketHandle], timeout = 500): int = proc select*(readfds: var seq[SocketHandle], timeout = 500): int =
## Traditional select function. This function will return the number of ## Traditional select function. This function will return the number of
## sockets that are ready to be read from, written to, or which have errors. ## sockets that are ready to be read from, written to, or which have errors.
## If there are none; 0 is returned. ## If there are none; 0 is returned.
@ -396,7 +399,7 @@ proc select*(readfds: var seq[TSocketHandle], timeout = 500): int =
pruneSocketSet(readfds, (rd)) pruneSocketSet(readfds, (rd))
proc selectWrite*(writefds: var seq[TSocketHandle], proc selectWrite*(writefds: var seq[SocketHandle],
timeout = 500): int {.tags: [FReadIO].} = timeout = 500): int {.tags: [FReadIO].} =
## When a socket in ``writefds`` is ready to be written to then a non-zero ## When a socket in ``writefds`` is ready to be written to then a non-zero
## value will be returned specifying the count of the sockets which can be ## value will be returned specifying the count of the sockets which can be
@ -405,7 +408,7 @@ proc selectWrite*(writefds: var seq[TSocketHandle],
## ##
## ``timeout`` is specified in miliseconds and ``-1`` can be specified for ## ``timeout`` is specified in miliseconds and ``-1`` can be specified for
## an unlimited time. ## an unlimited time.
var tv {.noInit.}: Ttimeval = timeValFromMilliseconds(timeout) var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
var wr: TFdSet var wr: TFdSet
var m = 0 var m = 0
@ -419,5 +422,5 @@ proc selectWrite*(writefds: var seq[TSocketHandle],
pruneSocketSet(writefds, (wr)) pruneSocketSet(writefds, (wr))
when defined(Windows): when defined(Windows):
var wsa: TWSADATA var wsa: WSADATA
if WSAStartup(0x0101'i16, addr wsa) != 0: osError(osLastError()) if WSAStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError())

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Dominik Picheta # (c) Copyright 2012 Dominik Picheta
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -20,36 +20,40 @@ const
redisNil* = "\0\0" redisNil* = "\0\0"
type type
PPipeline = ref object Pipeline = ref object
enabled: bool enabled: bool
buffer: string buffer: string
expected: int ## number of replies expected if pipelined expected: int ## number of replies expected if pipelined
type type
TSendMode = enum SendMode = enum
normal, pipelined, multiple normal, pipelined, multiple
type type
TRedis* {.pure, final.} = object Redis* = object
socket: TSocket socket: TSocket
connected: bool connected: bool
pipeline: PPipeline pipeline: Pipeline
TRedisStatus* = string RedisStatus* = string
TRedisInteger* = biggestInt RedisInteger* = BiggestInt
TRedisString* = string ## Bulk reply RedisString* = string ## Bulk reply
TRedisList* = seq[TRedisString] ## Multi-bulk reply RedisList* = seq[RedisString] ## Multi-bulk reply
EInvalidReply* = object of ESynch ## Invalid reply from redis ReplyError* = object of IOError ## Invalid reply from redis
ERedis* = object of ESynch ## Error in redis RedisError* = object of IOError ## Error in redis
proc newPipeline(): PPipeline = {.deprecated: [TSendMode: SendMode, TRedis: Redis, TRedisStatus: RedisStatus,
TRedisInteger: RedisInteger, TRedisString: RedisString,
TRedisList: RedistList, EInvalidReply: ReplyError, ERedis: RedisError].}
proc newPipeline(): Pipeline =
new(result) new(result)
result.buffer = "" result.buffer = ""
result.enabled = false result.enabled = false
result.expected = 0 result.expected = 0
proc open*(host = "localhost", port = 6379.TPort): TRedis = proc open*(host = "localhost", port = 6379.Port): Redis =
## Opens a connection to the redis server. ## Opens a connection to the redis server.
result.socket = socket(buffered = false) result.socket = socket(buffered = false)
if result.socket == InvalidSocket: if result.socket == InvalidSocket:
@ -58,24 +62,24 @@ proc open*(host = "localhost", port = 6379.TPort): TRedis =
result.pipeline = newPipeline() result.pipeline = newPipeline()
proc raiseInvalidReply(expected, got: char) = proc raiseInvalidReply(expected, got: char) =
raise newException(EInvalidReply, raise newException(ReplyError,
"Expected '$1' at the beginning of a status reply got '$2'" % "Expected '$1' at the beginning of a status reply got '$2'" %
[$expected, $got]) [$expected, $got])
proc raiseNoOK(status: string, pipelineEnabled:bool) = proc raiseNoOK(status: string, pipelineEnabled: bool) =
if pipelineEnabled and not (status == "QUEUED" or status == "PIPELINED"): if pipelineEnabled and not (status == "QUEUED" or status == "PIPELINED"):
raise newException(EInvalidReply, "Expected \"QUEUED\" or \"PIPELINED\" got \"$1\"" % status) raise newException(EInvalidReply, "Expected \"QUEUED\" or \"PIPELINED\" got \"$1\"" % status)
elif not pipelineEnabled and status != "OK": elif not pipelineEnabled and status != "OK":
raise newException(EInvalidReply, "Expected \"OK\" got \"$1\"" % status) raise newException(EInvalidReply, "Expected \"OK\" got \"$1\"" % status)
template readSocket(r: TRedis, dummyVal:expr): stmt = template readSocket(r: Redis, dummyVal:expr): stmt =
var line {.inject.} :TaintedString = "" var line {.inject.}: TaintedString = ""
if r.pipeline.enabled: if r.pipeline.enabled:
return dummyVal return dummyVal
else: else:
readLine(r.socket, line) readLine(r.socket, line)
proc parseStatus(r: TRedis, line: string = ""): TRedisStatus = proc parseStatus(r: Redis, line: string = ""): RedisStatus =
if r.pipeline.enabled: if r.pipeline.enabled:
return "PIPELINED" return "PIPELINED"

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2011 Philippe Lhoste # (c) Copyright 2011 Philippe Lhoste
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2010 Andreas Rumpf # (c) Copyright 2010 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -30,13 +30,15 @@ var
cacheEnabled = false cacheEnabled = false
type type
PRope* = ref TRope ## empty rope is represented by nil Rope* = ref RopeObj ## empty rope is represented by nil
TRope {.acyclic, final, pure.} = object RopeObj {.acyclic.} = object
left, right: PRope left, right: Rope
length: int length: int
data: string # != nil if a leaf data: string # != nil if a leaf
proc isConc(r: PRope): bool {.inline.} = return isNil(r.data) {.deprecated: [PRope: Rope].}
proc isConc(r: Rope): bool {.inline.} = return isNil(r.data)
# Note that the left and right pointers are not needed for leafs. # Note that the left and right pointers are not needed for leafs.
# Leaves have relatively high memory overhead (~30 bytes on a 32 # Leaves have relatively high memory overhead (~30 bytes on a 32
@ -46,25 +48,25 @@ proc isConc(r: PRope): bool {.inline.} = return isNil(r.data)
# performance. But for the caching tree we use the leaf's left and right # performance. But for the caching tree we use the leaf's left and right
# pointers. # pointers.
proc len*(a: PRope): int {.rtl, extern: "nro$1".} = proc len*(a: Rope): int {.rtl, extern: "nro$1".} =
## the rope's length ## the rope's length
if a == nil: result = 0 if a == nil: result = 0
else: result = a.length else: result = a.length
proc newRope(): PRope = new(result) proc newRope(): Rope = new(result)
proc newRope(data: string): PRope = proc newRope(data: string): Rope =
new(result) new(result)
result.length = len(data) result.length = len(data)
result.data = data result.data = data
var var
cache {.threadvar.}: PRope # the root of the cache tree cache {.threadvar.}: Rope # the root of the cache tree
N {.threadvar.}: PRope # dummy rope needed for splay algorithm N {.threadvar.}: Rope # dummy rope needed for splay algorithm
when countCacheMisses: when countCacheMisses:
var misses, hits: int var misses, hits: int
proc splay(s: string, tree: PRope, cmpres: var int): PRope = proc splay(s: string, tree: Rope, cmpres: var int): Rope =
var c: int var c: int
var t = tree var t = tree
N.left = nil N.left = nil
@ -102,7 +104,7 @@ proc splay(s: string, tree: PRope, cmpres: var int): PRope =
t.right = N.left t.right = N.left
result = t result = t
proc insertInCache(s: string, tree: PRope): PRope = proc insertInCache(s: string, tree: Rope): Rope =
var t = tree var t = tree
if t == nil: if t == nil:
result = newRope(s) result = newRope(s)
@ -128,7 +130,7 @@ proc insertInCache(s: string, tree: PRope): PRope =
result.left = t result.left = t
t.right = nil t.right = nil
proc rope*(s: string): PRope {.rtl, extern: "nro$1Str".} = proc rope*(s: string): Rope {.rtl, extern: "nro$1Str".} =
## Converts a string to a rope. ## Converts a string to a rope.
if s.len == 0: if s.len == 0:
result = nil result = nil
@ -138,11 +140,11 @@ proc rope*(s: string): PRope {.rtl, extern: "nro$1Str".} =
else: else:
result = newRope(s) result = newRope(s)
proc rope*(i: BiggestInt): PRope {.rtl, extern: "nro$1BiggestInt".} = proc rope*(i: BiggestInt): Rope {.rtl, extern: "nro$1BiggestInt".} =
## Converts an int to a rope. ## Converts an int to a rope.
result = rope($i) result = rope($i)
proc rope*(f: BiggestFloat): PRope {.rtl, extern: "nro$1BiggestFloat".} = proc rope*(f: BiggestFloat): Rope {.rtl, extern: "nro$1BiggestFloat".} =
## Converts a float to a rope. ## Converts a float to a rope.
result = rope($f) result = rope($f)
@ -156,7 +158,7 @@ proc disableCache*() {.rtl, extern: "nro$1".} =
cache = nil cache = nil
cacheEnabled = false cacheEnabled = false
proc `&`*(a, b: PRope): PRope {.rtl, extern: "nroConcRopeRope".} = proc `&`*(a, b: Rope): Rope {.rtl, extern: "nroConcRopeRope".} =
## the concatenation operator for ropes. ## the concatenation operator for ropes.
if a == nil: if a == nil:
result = b result = b
@ -176,27 +178,27 @@ proc `&`*(a, b: PRope): PRope {.rtl, extern: "nroConcRopeRope".} =
result.left = a result.left = a
result.right = b result.right = b
proc `&`*(a: PRope, b: string): PRope {.rtl, extern: "nroConcRopeStr".} = proc `&`*(a: Rope, b: string): Rope {.rtl, extern: "nroConcRopeStr".} =
## the concatenation operator for ropes. ## the concatenation operator for ropes.
result = a & rope(b) result = a & rope(b)
proc `&`*(a: string, b: PRope): PRope {.rtl, extern: "nroConcStrRope".} = proc `&`*(a: string, b: Rope): Rope {.rtl, extern: "nroConcStrRope".} =
## the concatenation operator for ropes. ## the concatenation operator for ropes.
result = rope(a) & b result = rope(a) & b
proc `&`*(a: openarray[PRope]): PRope {.rtl, extern: "nroConcOpenArray".} = proc `&`*(a: openarray[Rope]): Rope {.rtl, extern: "nroConcOpenArray".} =
## the concatenation operator for an openarray of ropes. ## the concatenation operator for an openarray of ropes.
for i in countup(0, high(a)): result = result & a[i] for i in countup(0, high(a)): result = result & a[i]
proc add*(a: var PRope, b: PRope) {.rtl, extern: "nro$1Rope".} = proc add*(a: var Rope, b: Rope) {.rtl, extern: "nro$1Rope".} =
## adds `b` to the rope `a`. ## adds `b` to the rope `a`.
a = a & b a = a & b
proc add*(a: var PRope, b: string) {.rtl, extern: "nro$1Str".} = proc add*(a: var Rope, b: string) {.rtl, extern: "nro$1Str".} =
## adds `b` to the rope `a`. ## adds `b` to the rope `a`.
a = a & b a = a & b
proc `[]`*(r: PRope, i: int): char {.rtl, extern: "nroCharAt".} = proc `[]`*(r: Rope, i: int): char {.rtl, extern: "nroCharAt".} =
## returns the character at position `i` in the rope `r`. This is quite ## returns the character at position `i` in the rope `r`. This is quite
## expensive! Worst-case: O(n). If ``i >= r.len``, ``\0`` is returned. ## expensive! Worst-case: O(n). If ``i >= r.len``, ``\0`` is returned.
var x = r var x = r
@ -213,7 +215,7 @@ proc `[]`*(r: PRope, i: int): char {.rtl, extern: "nroCharAt".} =
x = x.right x = x.right
dec(j, x.len) dec(j, x.len)
iterator leaves*(r: PRope): string = iterator leaves*(r: Rope): string =
## iterates over any leaf string in the rope `r`. ## iterates over any leaf string in the rope `r`.
if r != nil: if r != nil:
var stack = @[r] var stack = @[r]
@ -226,16 +228,16 @@ iterator leaves*(r: PRope): string =
assert(it.data != nil) assert(it.data != nil)
yield it.data yield it.data
iterator items*(r: PRope): char = iterator items*(r: Rope): char =
## iterates over any character in the rope `r`. ## iterates over any character in the rope `r`.
for s in leaves(r): for s in leaves(r):
for c in items(s): yield c for c in items(s): yield c
proc write*(f: TFile, r: PRope) {.rtl, extern: "nro$1".} = proc write*(f: TFile, r: Rope) {.rtl, extern: "nro$1".} =
## writes a rope to a file. ## writes a rope to a file.
for s in leaves(r): write(f, s) for s in leaves(r): write(f, s)
proc `$`*(r: PRope): string {.rtl, extern: "nroToString".}= proc `$`*(r: Rope): string {.rtl, extern: "nroToString".}=
## converts a rope back to a string. ## converts a rope back to a string.
result = newString(r.len) result = newString(r.len)
setLen(result, 0) setLen(result, 0)
@ -245,11 +247,11 @@ when false:
# Format string caching seems reasonable: All leaves can be shared and format # Format string caching seems reasonable: All leaves can be shared and format
# string parsing has to be done only once. A compiled format string is stored # string parsing has to be done only once. A compiled format string is stored
# as a rope. A negative length is used for the index into the args array. # as a rope. A negative length is used for the index into the args array.
proc compiledArg(idx: int): PRope = proc compiledArg(idx: int): Rope =
new(result) new(result)
result.length = -idx result.length = -idx
proc compileFrmt(frmt: string): PRope = proc compileFrmt(frmt: string): Rope =
var i = 0 var i = 0
var length = len(frmt) var length = len(frmt)
result = nil result = nil
@ -289,7 +291,7 @@ when false:
if i - 1 >= start: if i - 1 >= start:
add(result, substr(frmt, start, i-1)) add(result, substr(frmt, start, i-1))
proc `%`*(frmt: string, args: openarray[PRope]): PRope {. proc `%`*(frmt: string, args: openarray[Rope]): Rope {.
rtl, extern: "nroFormat".} = rtl, extern: "nroFormat".} =
## `%` substitution operator for ropes. Does not support the ``$identifier`` ## `%` substitution operator for ropes. Does not support the ``$identifier``
## nor ``${identifier}`` notations. ## nor ``${identifier}`` notations.
@ -332,12 +334,12 @@ proc `%`*(frmt: string, args: openarray[PRope]): PRope {.
if i - 1 >= start: if i - 1 >= start:
add(result, substr(frmt, start, i - 1)) add(result, substr(frmt, start, i - 1))
proc addf*(c: var PRope, frmt: string, args: openarray[PRope]) {. proc addf*(c: var Rope, frmt: string, args: openarray[Rope]) {.
rtl, extern: "nro$1".} = rtl, extern: "nro$1".} =
## shortcut for ``add(c, frmt % args)``. ## shortcut for ``add(c, frmt % args)``.
add(c, frmt % args) add(c, frmt % args)
proc equalsFile*(r: PRope, f: TFile): bool {.rtl, extern: "nro$1File".} = proc equalsFile*(r: Rope, f: TFile): bool {.rtl, extern: "nro$1File".} =
## returns true if the contents of the file `f` equal `r`. ## returns true if the contents of the file `f` equal `r`.
var bufSize = 1024 # reasonable start value var bufSize = 1024 # reasonable start value
var buf = alloc(BufSize) var buf = alloc(BufSize)
@ -352,7 +354,7 @@ proc equalsFile*(r: PRope, f: TFile): bool {.rtl, extern: "nro$1File".} =
result = readBuffer(f, buf, 1) == 0 # really at the end of file? result = readBuffer(f, buf, 1) == 0 # really at the end of file?
dealloc(buf) dealloc(buf)
proc equalsFile*(r: PRope, f: string): bool {.rtl, extern: "nro$1Str".} = proc equalsFile*(r: Rope, f: string): bool {.rtl, extern: "nro$1Str".} =
## returns true if the contents of the file `f` equal `r`. If `f` does not ## returns true if the contents of the file `f` equal `r`. If `f` does not
## exist, false is returned. ## exist, false is returned.
var bin: TFile var bin: TFile

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2013 Andreas Rumpf, Dominik Picheta # (c) Copyright 2013 Andreas Rumpf, Dominik Picheta
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -9,13 +9,13 @@
## This module implements helper procs for SCGI applications. Example: ## This module implements helper procs for SCGI applications. Example:
## ##
## .. code-block:: Nimrod ## .. code-block:: Nim
## ##
## import strtabs, sockets, scgi ## import strtabs, sockets, scgi
## ##
## var counter = 0 ## var counter = 0
## proc handleRequest(client: TSocket, input: string, ## proc handleRequest(client: Socket, input: string,
## headers: PStringTable): bool {.procvar.} = ## headers: StringTableRef): bool {.procvar.} =
## inc(counter) ## inc(counter)
## client.writeStatusOkTextContent() ## client.writeStatusOkTextContent()
## client.send("Hello for the $#th time." % $counter & "\c\L") ## client.send("Hello for the $#th time." % $counter & "\c\L")
@ -31,11 +31,11 @@ include "system/inclrtl"
import sockets, strutils, os, strtabs, asyncio import sockets, strutils, os, strtabs, asyncio
type type
EScgi* = object of EIO ## the exception that is raised, if a SCGI error occurs ScgiError* = object of IOError ## the exception that is raised, if a SCGI error occurs
proc scgiError*(msg: string) {.noreturn.} = proc raiseScgiError*(msg: string) {.noreturn.} =
## raises an EScgi exception with message `msg`. ## raises an ScgiError exception with message `msg`.
var e: ref EScgi var e: ref ScgiError
new(e) new(e)
e.msg = msg e.msg = msg
raise e raise e
@ -45,7 +45,7 @@ proc parseWord(inp: string, outp: var string, start: int): int =
while inp[result] != '\0': inc(result) while inp[result] != '\0': inc(result)
outp = substr(inp, start, result-1) outp = substr(inp, start, result-1)
proc parseHeaders(s: string, L: int): PStringTable = proc parseHeaders(s: string, L: int): StringTableRef =
result = newStringTable() result = newStringTable()
var i = 0 var i = 0
while i < L: while i < L:
@ -54,73 +54,77 @@ proc parseHeaders(s: string, L: int): PStringTable =
i = parseWord(s, val, i)+1 i = parseWord(s, val, i)+1
result[key] = val result[key] = val
if s[i] == ',': inc(i) if s[i] == ',': inc(i)
else: scgiError("',' after netstring expected") else: raiseScgiError("',' after netstring expected")
proc recvChar(s: TSocket): char = proc recvChar(s: Socket): char =
var c: char var c: char
if recv(s, addr(c), sizeof(c)) == sizeof(c): if recv(s, addr(c), sizeof(c)) == sizeof(c):
result = c result = c
type type
TScgiState* = object of TObject ## SCGI state object ScgiState* = object of RootObj ## SCGI state object
server: TSocket server: Socket
bufLen: int bufLen: int
client*: TSocket ## the client socket to send data to client*: Socket ## the client socket to send data to
headers*: PStringTable ## the parsed headers headers*: StringTableRef ## the parsed headers
input*: string ## the input buffer input*: string ## the input buffer
# Async # Async
TClientMode = enum ClientMode = enum
ClientReadChar, ClientReadHeaders, ClientReadContent ClientReadChar, ClientReadHeaders, ClientReadContent
PAsyncClient = ref object AsyncClient = ref object
c: PAsyncSocket c: AsyncSocket
mode: TClientMode mode: ClientMode
dataLen: int dataLen: int
headers: PStringTable ## the parsed headers headers: PStringTable ## the parsed headers
input: string ## the input buffer input: string ## the input buffer
TAsyncScgiState = object AsyncScgiStateObj = object
handleRequest: proc (client: PAsyncSocket, handleRequest: proc (client: AsyncSocket,
input: string, headers: PStringTable) {.closure,gcsafe.} input: string,
asyncServer: PAsyncSocket headers: StringTableRef) {.closure, gcsafe.}
disp: PDispatcher asyncServer: AsyncSocket
PAsyncScgiState* = ref TAsyncScgiState disp: Dispatcher
AsyncScgiState* = ref AsyncScgiStateObj
proc recvBuffer(s: var TScgiState, L: int) =
{.deprected: [EScgi: ScgiError, TScgiState: ScgiState,
PAsyncScgiState: AsyncScgiState, scgiError: raiseScgiError].}
proc recvBuffer(s: var ScgiState, L: int) =
if L > s.bufLen: if L > s.bufLen:
s.bufLen = L s.bufLen = L
s.input = newString(L) s.input = newString(L)
if L > 0 and recv(s.client, cstring(s.input), L) != L: if L > 0 and recv(s.client, cstring(s.input), L) != L:
scgiError("could not read all data") raiseScgiError("could not read all data")
setLen(s.input, L) setLen(s.input, L)
proc open*(s: var TScgiState, port = TPort(4000), address = "127.0.0.1", proc open*(s: var ScgiState, port = Port(4000), address = "127.0.0.1",
reuseAddr = False) = reuseAddr = false) =
## opens a connection. ## opens a connection.
s.bufLen = 4000 s.bufLen = 4000
s.input = newString(s.buflen) # will be reused s.input = newString(s.buflen) # will be reused
s.server = socket() s.server = socket()
if s.server == InvalidSocket: osError(osLastError()) if s.server == InvalidSocket: raiseOSError(osLastError())
new(s.client) # Initialise s.client for `next` new(s.client) # Initialise s.client for `next`
if s.server == InvalidSocket: scgiError("could not open socket") if s.server == InvalidSocket: raiseScgiError("could not open socket")
#s.server.connect(connectionName, port) #s.server.connect(connectionName, port)
if reuseAddr: if reuseAddr:
s.server.setSockOpt(OptReuseAddr, True) s.server.setSockOpt(OptReuseAddr, True)
bindAddr(s.server, port, address) bindAddr(s.server, port, address)
listen(s.server) listen(s.server)
proc close*(s: var TScgiState) = proc close*(s: var ScgiState) =
## closes the connection. ## closes the connection.
s.server.close() s.server.close()
proc next*(s: var TScgistate, timeout: int = -1): bool = proc next*(s: var ScgiState, timeout: int = -1): bool =
## proceed to the first/next request. Waits ``timeout`` miliseconds for a ## proceed to the first/next request. Waits ``timeout`` miliseconds for a
## request, if ``timeout`` is `-1` then this function will never time out. ## request, if ``timeout`` is `-1` then this function will never time out.
## Returns `True` if a new request has been processed. ## Returns `true` if a new request has been processed.
var rsocks = @[s.server] var rsocks = @[s.server]
if select(rsocks, timeout) == 1 and rsocks.len == 1: if select(rsocks, timeout) == 1 and rsocks.len == 1:
new(s.client) new(s.client)
@ -132,17 +136,17 @@ proc next*(s: var TScgistate, timeout: int = -1): bool =
s.client.close() s.client.close()
return false return false
if d notin strutils.digits: if d notin strutils.digits:
if d != ':': scgiError("':' after length expected") if d != ':': raiseScgiError("':' after length expected")
break break
L = L * 10 + ord(d) - ord('0') L = L * 10 + ord(d) - ord('0')
recvBuffer(s, L+1) recvBuffer(s, L+1)
s.headers = parseHeaders(s.input, L) s.headers = parseHeaders(s.input, L)
if s.headers["SCGI"] != "1": scgiError("SCGI Version 1 expected") if s.headers["SCGI"] != "1": raiseScgiError("SCGI Version 1 expected")
L = parseInt(s.headers["CONTENT_LENGTH"]) L = parseInt(s.headers["CONTENT_LENGTH"])
recvBuffer(s, L) recvBuffer(s, L)
return True return True
proc writeStatusOkTextContent*(c: TSocket, contentType = "text/html") = proc writeStatusOkTextContent*(c: Socket, contentType = "text/html") =
## sends the following string to the socket `c`:: ## sends the following string to the socket `c`::
## ##
## Status: 200 OK\r\LContent-Type: text/html\r\L\r\L ## Status: 200 OK\r\LContent-Type: text/html\r\L\r\L
@ -151,11 +155,11 @@ proc writeStatusOkTextContent*(c: TSocket, contentType = "text/html") =
c.send("Status: 200 OK\r\L" & c.send("Status: 200 OK\r\L" &
"Content-Type: $1\r\L\r\L" % contentType) "Content-Type: $1\r\L\r\L" % contentType)
proc run*(handleRequest: proc (client: TSocket, input: string, proc run*(handleRequest: proc (client: Socket, input: string,
headers: PStringTable): bool {.nimcall,gcsafe.}, headers: StringTableRef): bool {.nimcall,gcsafe.},
port = TPort(4000)) = port = Port(4000)) =
## encapsulates the SCGI object and main loop. ## encapsulates the SCGI object and main loop.
var s: TScgiState var s: ScgiState
s.open(port) s.open(port)
var stop = false var stop = false
while not stop: while not stop:
@ -166,11 +170,11 @@ proc run*(handleRequest: proc (client: TSocket, input: string,
# -- AsyncIO start # -- AsyncIO start
proc recvBufferAsync(client: PAsyncClient, L: int): TReadLineResult = proc recvBufferAsync(client: AsyncClient, L: int): ReadLineResult =
result = ReadPartialLine result = ReadPartialLine
var data = "" var data = ""
if L < 1: if L < 1:
scgiError("Cannot read negative or zero length: " & $L) raiseScgiError("Cannot read negative or zero length: " & $L)
let ret = recvAsync(client.c, data, L) let ret = recvAsync(client.c, data, L)
if ret == 0 and data == "": if ret == 0 and data == "":
client.c.close() client.c.close()
@ -181,16 +185,16 @@ proc recvBufferAsync(client: PAsyncClient, L: int): TReadLineResult =
if ret == L: if ret == L:
return ReadFullLine return ReadFullLine
proc checkCloseSocket(client: PAsyncClient) = proc checkCloseSocket(client: AsyncClient) =
if not client.c.isClosed: if not client.c.isClosed:
if client.c.isSendDataBuffered: if client.c.isSendDataBuffered:
client.c.setHandleWrite do (s: PAsyncSocket): client.c.setHandleWrite do (s: AsyncSocket):
if not s.isClosed and not s.isSendDataBuffered: if not s.isClosed and not s.isSendDataBuffered:
s.close() s.close()
s.delHandleWrite() s.delHandleWrite()
else: client.c.close() else: client.c.close()
proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) = proc handleClientRead(client: AsyncClient, s: AsyncScgiState) =
case client.mode case client.mode
of ClientReadChar: of ClientReadChar:
while true: while true:
@ -203,7 +207,7 @@ proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) =
if ret == -1: if ret == -1:
return # No more data available return # No more data available
if d[0] notin strutils.digits: if d[0] notin strutils.digits:
if d[0] != ':': scgiError("':' after length expected") if d[0] != ':': raiseScgiError("':' after length expected")
break break
client.dataLen = client.dataLen * 10 + ord(d[0]) - ord('0') client.dataLen = client.dataLen * 10 + ord(d[0]) - ord('0')
client.mode = ClientReadHeaders client.mode = ClientReadHeaders
@ -213,7 +217,7 @@ proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) =
case ret case ret
of ReadFullLine: of ReadFullLine:
client.headers = parseHeaders(client.input, client.input.len-1) client.headers = parseHeaders(client.input, client.input.len-1)
if client.headers["SCGI"] != "1": scgiError("SCGI Version 1 expected") if client.headers["SCGI"] != "1": raiseScgiError("SCGI Version 1 expected")
client.input = "" # For next part client.input = "" # For next part
let contentLen = parseInt(client.headers["CONTENT_LENGTH"]) let contentLen = parseInt(client.headers["CONTENT_LENGTH"])
@ -236,50 +240,50 @@ proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) =
s.handleRequest(client.c, client.input, client.headers) s.handleRequest(client.c, client.input, client.headers)
checkCloseSocket(client) checkCloseSocket(client)
proc handleAccept(sock: PAsyncSocket, s: PAsyncScgiState) = proc handleAccept(sock: AsyncSocket, s: AsyncScgiState) =
var client: PAsyncSocket var client: AsyncSocket
new(client) new(client)
accept(s.asyncServer, client) accept(s.asyncServer, client)
var asyncClient = PAsyncClient(c: client, mode: ClientReadChar, dataLen: 0, var asyncClient = AsyncClient(c: client, mode: ClientReadChar, dataLen: 0,
headers: newStringTable(), input: "") headers: newStringTable(), input: "")
client.handleRead = client.handleRead =
proc (sock: PAsyncSocket) = proc (sock: AsyncSocket) =
handleClientRead(asyncClient, s) handleClientRead(asyncClient, s)
s.disp.register(client) s.disp.register(client)
proc open*(handleRequest: proc (client: PAsyncSocket, proc open*(handleRequest: proc (client: AsyncSocket,
input: string, headers: PStringTable) {. input: string, headers: StringTableRef) {.
closure, gcsafe.}, closure, gcsafe.},
port = TPort(4000), address = "127.0.0.1", port = Port(4000), address = "127.0.0.1",
reuseAddr = false): PAsyncScgiState = reuseAddr = false): AsyncScgiState =
## Creates an ``PAsyncScgiState`` object which serves as a SCGI server. ## Creates an ``AsyncScgiState`` object which serves as a SCGI server.
## ##
## After the execution of ``handleRequest`` the client socket will be closed ## After the execution of ``handleRequest`` the client socket will be closed
## automatically unless it has already been closed. ## automatically unless it has already been closed.
var cres: PAsyncScgiState var cres: AsyncScgiState
new(cres) new(cres)
cres.asyncServer = AsyncSocket() cres.asyncServer = AsyncSocket()
cres.asyncServer.handleAccept = proc (s: PAsyncSocket) = handleAccept(s, cres) cres.asyncServer.handleAccept = proc (s: AsyncSocket) = handleAccept(s, cres)
if reuseAddr: if reuseAddr:
cres.asyncServer.setSockOpt(OptReuseAddr, True) cres.asyncServer.setSockOpt(OptReuseAddr, true)
bindAddr(cres.asyncServer, port, address) bindAddr(cres.asyncServer, port, address)
listen(cres.asyncServer) listen(cres.asyncServer)
cres.handleRequest = handleRequest cres.handleRequest = handleRequest
result = cres result = cres
proc register*(d: PDispatcher, s: PAsyncScgiState): PDelegate {.discardable.} = proc register*(d: Dispatcher, s: AsyncScgiState): Delegate {.discardable.} =
## Registers ``s`` with dispatcher ``d``. ## Registers ``s`` with dispatcher ``d``.
result = d.register(s.asyncServer) result = d.register(s.asyncServer)
s.disp = d s.disp = d
proc close*(s: PAsyncScgiState) = proc close*(s: AsyncScgiState) =
## Closes the ``PAsyncScgiState``. ## Closes the ``AsyncScgiState``.
s.asyncServer.close() s.asyncServer.close()
when false: when false:
var counter = 0 var counter = 0
proc handleRequest(client: TSocket, input: string, proc handleRequest(client: Socket, input: string,
headers: PStringTable): bool {.procvar.} = headers: StringTableRef): bool {.procvar.} =
inc(counter) inc(counter)
client.writeStatusOkTextContent() client.writeStatusOkTextContent()
client.send("Hello for the $#th time." % $counter & "\c\L") client.send("Hello for the $#th time." % $counter & "\c\L")

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2014 Dominik Picheta # (c) Copyright 2014 Dominik Picheta
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -18,57 +18,57 @@ elif defined(windows):
else: else:
import posix import posix
proc hash*(x: TSocketHandle): THash {.borrow.} proc hash*(x: SocketHandle): THash {.borrow.}
proc `$`*(x: TSocketHandle): string {.borrow.} proc `$`*(x: SocketHandle): string {.borrow.}
type type
TEvent* = enum Event* = enum
EvRead, EvWrite EvRead, EvWrite
PSelectorKey* = ref object SelectorKey* = ref object
fd*: TSocketHandle fd*: SocketHandle
events*: set[TEvent] ## The events which ``fd`` listens for. events*: set[Event] ## The events which ``fd`` listens for.
data*: PObject ## User object. data*: RootRef ## User object.
TReadyInfo* = tuple[key: PSelectorKey, events: set[TEvent]] ReadyInfo* = tuple[key: SelectorKey, events: set[Event]]
when defined(nimdoc): when defined(nimdoc):
type type
PSelector* = ref object Selector* = ref object
## An object which holds file descripters to be checked for read/write ## An object which holds file descripters to be checked for read/write
## status. ## status.
fds: TTable[TSocketHandle, PSelectorKey] fds: Table[SocketHandle, SelectorKey]
proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent], proc register*(s: Selector, fd: SocketHandle, events: set[Event],
data: PObject): PSelectorKey {.discardable.} = data: RootRef): SelectorKey {.discardable.} =
## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent ## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent
## ``events``. ## ``events``.
proc update*(s: PSelector, fd: TSocketHandle, proc update*(s: Selector, fd: SocketHandle,
events: set[TEvent]): PSelectorKey {.discardable.} = events: set[Event]): SelectorKey {.discardable.} =
## Updates the events which ``fd`` wants notifications for. ## Updates the events which ``fd`` wants notifications for.
proc select*(s: PSelector, timeout: int): seq[TReadyInfo] = proc select*(s: Selector, timeout: int): seq[ReadyInfo] =
## The ``events`` field of the returned ``key`` contains the original events ## The ``events`` field of the returned ``key`` contains the original events
## for which the ``fd`` was bound. This is contrary to the ``events`` field ## for which the ``fd`` was bound. This is contrary to the ``events`` field
## of the ``TReadyInfo`` tuple which determines which events are ready ## of the ``TReadyInfo`` tuple which determines which events are ready
## on the ``fd``. ## on the ``fd``.
proc contains*(s: PSelector, fd: TSocketHandle): bool = proc contains*(s: Selector, fd: SocketHandle): bool =
## Determines whether selector contains a file descriptor. ## Determines whether selector contains a file descriptor.
proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey = proc `[]`*(s: Selector, fd: SocketHandle): SelectorKey =
## Retrieves the selector key for ``fd``. ## Retrieves the selector key for ``fd``.
elif defined(linux): elif defined(linux):
type type
PSelector* = ref object Selector* = ref object
epollFD: cint epollFD: cint
events: array[64, epoll_event] events: array[64, epoll_event]
fds: TTable[TSocketHandle, PSelectorKey] fds: Table[SocketHandle, SelectorKey]
proc createEventStruct(events: set[TEvent], fd: TSocketHandle): epoll_event = proc createEventStruct(events: set[Event], fd: SocketHandle): epoll_event =
if EvRead in events: if EvRead in events:
result.events = EPOLLIN result.events = EPOLLIN
if EvWrite in events: if EvWrite in events:
@ -76,22 +76,22 @@ elif defined(linux):
result.events = result.events or EPOLLRDHUP result.events = result.events or EPOLLRDHUP
result.data.fd = fd.cint result.data.fd = fd.cint
proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent], proc register*(s: Selector, fd: SocketHandle, events: set[Event],
data: PObject): PSelectorKey {.discardable.} = data: RootRef): SelectorKey {.discardable.} =
## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent ## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent
## ``events``. ## ``events``.
var event = createEventStruct(events, fd) var event = createEventStruct(events, fd)
if events != {}: if events != {}:
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0: if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
OSError(OSLastError()) raiseOSError(osLastError())
var key = PSelectorKey(fd: fd, events: events, data: data) var key = SelectorKey(fd: fd, events: events, data: data)
s.fds[fd] = key s.fds[fd] = key
result = key result = key
proc update*(s: PSelector, fd: TSocketHandle, proc update*(s: Selector, fd: SocketHandle,
events: set[TEvent]): PSelectorKey {.discardable.} = events: set[Event]): SelectorKey {.discardable.} =
## Updates the events which ``fd`` wants notifications for. ## Updates the events which ``fd`` wants notifications for.
if s.fds[fd].events != events: if s.fds[fd].events != events:
if events == {}: if events == {}:
@ -101,7 +101,7 @@ elif defined(linux):
# because its got fds which are waiting for no events and # because its got fds which are waiting for no events and
# are therefore constantly ready. (leading to 100% CPU usage). # are therefore constantly ready. (leading to 100% CPU usage).
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0: if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
OSError(OSLastError()) raiseOSError(osLastError())
s.fds[fd].events = events s.fds[fd].events = events
else: else:
var event = createEventStruct(events, fd) var event = createEventStruct(events, fd)
@ -109,36 +109,36 @@ elif defined(linux):
# This fd is idle. It's not a member of this epoll instance and must # This fd is idle. It's not a member of this epoll instance and must
# be re-registered. # be re-registered.
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0: if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
OSError(OSLastError()) raiseOSError(osLastError())
else: else:
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0: if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
OSError(OSLastError()) raiseOSError(osLastError())
s.fds[fd].events = events s.fds[fd].events = events
result = s.fds[fd] result = s.fds[fd]
proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} = proc unregister*(s: Selector, fd: SocketHandle): SelectorKey {.discardable.} =
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0: if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
let err = OSLastError() let err = osLastError()
if err.cint notin {ENOENT, EBADF}: # TODO: Why do we sometimes get an EBADF? Is this normal? if err.cint notin {ENOENT, EBADF}: # TODO: Why do we sometimes get an EBADF? Is this normal?
OSError(err) raiseOSError(err)
result = s.fds[fd] result = s.fds[fd]
s.fds.del(fd) s.fds.del(fd)
proc close*(s: PSelector) = proc close*(s: Selector) =
if s.epollFD.close() != 0: OSError(OSLastError()) if s.epollFD.close() != 0: raiseOSError(osLastError())
dealloc(addr s.events) # TODO: Test this dealloc(addr s.events) # TODO: Test this
proc epollHasFd(s: PSelector, fd: TSocketHandle): bool = proc epollHasFd(s: Selector, fd: SocketHandle): bool =
result = true result = true
var event = createEventStruct(s.fds[fd].events, fd) var event = createEventStruct(s.fds[fd].events, fd)
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0: if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
let err = osLastError() let err = osLastError()
if err.cint in {ENOENT, EBADF}: if err.cint in {ENOENT, EBADF}:
return false return false
OSError(OSLastError()) raiseOSError(osLastError())
proc select*(s: PSelector, timeout: int): seq[TReadyInfo] = proc select*(s: Selector, timeout: int): seq[ReadyInfo] =
## ##
## The ``events`` field of the returned ``key`` contains the original events ## The ``events`` field of the returned ``key`` contains the original events
## for which the ``fd`` was bound. This is contrary to the ``events`` field ## for which the ``fd`` was bound. This is contrary to the ``events`` field
@ -146,12 +146,12 @@ elif defined(linux):
## on the ``fd``. ## on the ``fd``.
result = @[] result = @[]
let evNum = epoll_wait(s.epollFD, addr s.events[0], 64.cint, timeout.cint) let evNum = epoll_wait(s.epollFD, addr s.events[0], 64.cint, timeout.cint)
if evNum < 0: OSError(OSLastError()) if evNum < 0: raiseOSError(osLastError())
if evNum == 0: return @[] if evNum == 0: return @[]
for i in 0 .. <evNum: for i in 0 .. <evNum:
let fd = s.events[i].data.fd.TSocketHandle let fd = s.events[i].data.fd.SocketHandle
var evSet: set[TEvent] = {} var evSet: set[Event] = {}
if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead} if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
if (s.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite} if (s.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
let selectorKey = s.fds[fd] let selectorKey = s.fds[fd]
@ -160,15 +160,15 @@ elif defined(linux):
#echo("Epoll: ", result[i].key.fd, " ", result[i].events, " ", result[i].key.events) #echo("Epoll: ", result[i].key.fd, " ", result[i].events, " ", result[i].key.events)
proc newSelector*(): PSelector = proc newSelector*(): Selector =
new result new result
result.epollFD = epoll_create(64) result.epollFD = epoll_create(64)
#result.events = cast[array[64, epoll_event]](alloc0(sizeof(epoll_event)*64)) #result.events = cast[array[64, epoll_event]](alloc0(sizeof(epoll_event)*64))
result.fds = initTable[TSocketHandle, PSelectorKey]() result.fds = initTable[SocketHandle, SelectorKey]()
if result.epollFD < 0: if result.epollFD < 0:
OSError(OSLastError()) raiseOSError(osLastError())
proc contains*(s: PSelector, fd: TSocketHandle): bool = proc contains*(s: Selector, fd: SocketHandle): bool =
## Determines whether selector contains a file descriptor. ## Determines whether selector contains a file descriptor.
if s.fds.hasKey(fd): if s.fds.hasKey(fd):
# Ensure the underlying epoll instance still contains this fd. # Ensure the underlying epoll instance still contains this fd.
@ -179,46 +179,46 @@ elif defined(linux):
else: else:
return false return false
proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey = proc `[]`*(s: Selector, fd: SocketHandle): SelectorKey =
## Retrieves the selector key for ``fd``. ## Retrieves the selector key for ``fd``.
return s.fds[fd] return s.fds[fd]
elif not defined(nimdoc): elif not defined(nimdoc):
# TODO: kqueue for bsd/mac os x. # TODO: kqueue for bsd/mac os x.
type type
PSelector* = ref object Selector* = ref object
fds: TTable[TSocketHandle, PSelectorKey] fds: Table[SocketHandle, SelectorKey]
proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent], proc register*(s: Selector, fd: SocketHandle, events: set[Event],
data: PObject): PSelectorKey {.discardable.} = data: RootRef): SelectorKey {.discardable.} =
if s.fds.hasKey(fd): if s.fds.hasKey(fd):
raise newException(EInvalidValue, "File descriptor already exists.") raise newException(ValueError, "File descriptor already exists.")
var sk = PSelectorKey(fd: fd, events: events, data: data) var sk = SelectorKey(fd: fd, events: events, data: data)
s.fds[fd] = sk s.fds[fd] = sk
result = sk result = sk
proc update*(s: PSelector, fd: TSocketHandle, proc update*(s: Selector, fd: SocketHandle,
events: set[TEvent]): PSelectorKey {.discardable.} = events: set[Event]): SelectorKey {.discardable.} =
## Updates the events which ``fd`` wants notifications for. ## Updates the events which ``fd`` wants notifications for.
if not s.fds.hasKey(fd): if not s.fds.hasKey(fd):
raise newException(EInvalidValue, "File descriptor not found.") raise newException(ValueError, "File descriptor not found.")
s.fds[fd].events = events s.fds[fd].events = events
result = s.fds[fd] result = s.fds[fd]
proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} = proc unregister*(s: Selector, fd: SocketHandle): SelectorKey {.discardable.} =
result = s.fds[fd] result = s.fds[fd]
s.fds.del(fd) s.fds.del(fd)
proc close*(s: PSelector) = nil proc close*(s: Selector) = discard
proc timeValFromMilliseconds(timeout: int): TTimeVal = proc timeValFromMilliseconds(timeout: int): TimeVal =
if timeout != -1: if timeout != -1:
var seconds = timeout div 1000 var seconds = timeout div 1000
result.tv_sec = seconds.int32 result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
proc createFdSet(rd, wr: var TFdSet, fds: TTable[TSocketHandle, PSelectorKey], proc createFdSet(rd, wr: var FdSet, fds: Table[SocketHandle, SelectorKey],
m: var int) = m: var int) =
FD_ZERO(rd); FD_ZERO(wr) FD_ZERO(rd); FD_ZERO(wr)
for k, v in pairs(fds): for k, v in pairs(fds):
@ -229,22 +229,22 @@ elif not defined(nimdoc):
m = max(m, int(k)) m = max(m, int(k))
FD_SET(k, wr) FD_SET(k, wr)
proc getReadyFDs(rd, wr: var TFdSet, fds: TTable[TSocketHandle, PSelectorKey]): proc getReadyFDs(rd, wr: var FdSet, fds: Table[SocketHandle, SelectorKey]):
seq[TReadyInfo] = seq[ReadyInfo] =
result = @[] result = @[]
for k, v in pairs(fds): for k, v in pairs(fds):
var events: set[TEvent] = {} var events: set[Event] = {}
if FD_ISSET(k, rd) != 0'i32: if FD_ISSET(k, rd) != 0'i32:
events = events + {EvRead} events = events + {EvRead}
if FD_ISSET(k, wr) != 0'i32: if FD_ISSET(k, wr) != 0'i32:
events = events + {EvWrite} events = events + {EvWrite}
result.add((v, events)) result.add((v, events))
proc select(fds: TTable[TSocketHandle, PSelectorKey], timeout = 500): proc select(fds: Table[SocketHandle, SelectorKey], timeout = 500):
seq[TReadyInfo] = seq[ReadyInfo] =
var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout) var tv {.noInit.}: TimeVal = timeValFromMilliseconds(timeout)
var rd, wr: TFdSet var rd, wr: FdSet
var m = 0 var m = 0
createFdSet(rd, wr, fds, m) createFdSet(rd, wr, fds, m)
@ -255,26 +255,26 @@ elif not defined(nimdoc):
retCode = int(select(cint(m+1), addr(rd), addr(wr), nil, nil)) retCode = int(select(cint(m+1), addr(rd), addr(wr), nil, nil))
if retCode < 0: if retCode < 0:
OSError(OSLastError()) raiseOSError(osLastError())
elif retCode == 0: elif retCode == 0:
return @[] return @[]
else: else:
return getReadyFDs(rd, wr, fds) return getReadyFDs(rd, wr, fds)
proc select*(s: PSelector, timeout: int): seq[TReadyInfo] = proc select*(s: Selector, timeout: int): seq[ReadyInfo] =
result = select(s.fds, timeout) result = select(s.fds, timeout)
proc newSelector*(): PSelector = proc newSelector*(): Selector =
new result new result
result.fds = initTable[TSocketHandle, PSelectorKey]() result.fds = initTable[SocketHandle, SelectorKey]()
proc contains*(s: PSelector, fd: TSocketHandle): bool = proc contains*(s: Selector, fd: SocketHandle): bool =
return s.fds.hasKey(fd) return s.fds.hasKey(fd)
proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey = proc `[]`*(s: Selector, fd: SocketHandle): SelectorKey =
return s.fds[fd] return s.fds[fd]
proc contains*(s: PSelector, key: PSelectorKey): bool = proc contains*(s: Selector, key: SelectorKey): bool =
## Determines whether selector contains this selector key. More accurate ## Determines whether selector contains this selector key. More accurate
## than checking if the file descriptor is in the selector because it ## than checking if the file descriptor is in the selector because it
## ensures that the keys are equal. File descriptors may not always be ## ensures that the keys are equal. File descriptors may not always be
@ -282,20 +282,24 @@ proc contains*(s: PSelector, key: PSelectorKey): bool =
## the new one may have the same value. ## the new one may have the same value.
return key.fd in s and s.fds[key.fd] == key return key.fd in s and s.fds[key.fd] == key
{.deprecated: [TEvent: Event, PSelectorKey: SelectorKey,
TReadyInfo: ReadyInfo, PSelector: Selector].}
when isMainModule and not defined(nimdoc): when isMainModule and not defined(nimdoc):
# Select() # Select()
import sockets import sockets
type type
PSockWrapper = ref object of PObject SockWrapper = ref object of RootObj
sock: TSocket sock: Socket
var sock = socket() var sock = socket()
if sock == sockets.InvalidSocket: osError(osLastError()) if sock == sockets.InvalidSocket: raiseOSError(osLastError())
#sock.setBlocking(false) #sock.setBlocking(false)
sock.connect("irc.freenode.net", TPort(6667)) sock.connect("irc.freenode.net", Port(6667))
var selector = newSelector() var selector = newSelector()
var data = PSockWrapper(sock: sock) var data = SockWrapper(sock: sock)
let key = selector.register(sock.getFD, {EvWrite}, data) let key = selector.register(sock.getFD, {EvWrite}, data)
var i = 0 var i = 0
while true: while true:

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Dominik Picheta # (c) Copyright 2012 Dominik Picheta
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -16,11 +16,11 @@
## Example gmail use: ## Example gmail use:
## ##
## ##
## .. code-block:: Nimrod ## .. code-block:: Nim
## var msg = createMessage("Hello from Nimrod's SMTP", ## var msg = createMessage("Hello from Nim's SMTP",
## "Hello!.\n Is this awesome or what?", ## "Hello!.\n Is this awesome or what?",
## @["foo@gmail.com"]) ## @["foo@gmail.com"])
## var smtp = connect("smtp.gmail.com", 465, True, True) ## var smtp = connect("smtp.gmail.com", 465, true, true)
## smtp.auth("username", "password") ## smtp.auth("username", "password")
## smtp.sendmail("username@gmail.com", @["foo@gmail.com"], $msg) ## smtp.sendmail("username@gmail.com", @["foo@gmail.com"], $msg)
## ##
@ -31,18 +31,20 @@
import sockets, strutils, strtabs, base64, os import sockets, strutils, strtabs, base64, os
type type
TSMTP* {.final.} = object Smtp* = object
sock: TSocket sock: Socket
debug: Bool debug: bool
TMessage* {.final.} = object Message* = object
msgTo: seq[string] msgTo: seq[string]
msgCc: seq[string] msgCc: seq[string]
msgSubject: string msgSubject: string
msgOtherHeaders: PStringTable msgOtherHeaders: StringTableRef
msgBody: string msgBody: string
EInvalidReply* = object of EIO ReplyError* = object of IOError
{.deprecated: [EInvalidReply: ReplyError, TMessage: Message, TSMTP: Smtp].}
proc debugSend(smtp: TSMTP, cmd: string) = proc debugSend(smtp: TSMTP, cmd: string) =
if smtp.debug: if smtp.debug:
@ -170,7 +172,7 @@ when isMainModule:
#smtp.sendmail("root@localhost", @["dominik@localhost"], $msg) #smtp.sendmail("root@localhost", @["dominik@localhost"], $msg)
#echo(decode("a17sm3701420wbe.12")) #echo(decode("a17sm3701420wbe.12"))
var msg = createMessage("Hello from Nimrod's SMTP!", var msg = createMessage("Hello from Nim's SMTP!",
"Hello!!!!.\n Is this awesome or what?", "Hello!!!!.\n Is this awesome or what?",
@["someone@yahoo.com", "someone@gmail.com"]) @["someone@yahoo.com", "someone@gmail.com"])
echo(msg) echo(msg)

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2013 Andreas Rumpf, Dominik Picheta # (c) Copyright 2013 Andreas Rumpf, Dominik Picheta
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -14,8 +14,8 @@
## the ``socket`` function to `false`. Be aware that some functions may not yet ## the ``socket`` function to `false`. Be aware that some functions may not yet
## support buffered sockets (mainly the recvFrom function). ## support buffered sockets (mainly the recvFrom function).
## ##
## Most procedures raise EOS on error, but some may return ``-1`` or a boolean ## Most procedures raise OSError on error, but some may return ``-1`` or a
## ``false``. ## boolean ``false``.
## ##
## SSL is supported through the OpenSSL library. This support can be activated ## SSL is supported through the OpenSSL library. This support can be activated
## by compiling with the ``-d:ssl`` switch. When an SSL socket is used it will ## by compiling with the ``-d:ssl`` switch. When an SSL socket is used it will
@ -47,25 +47,29 @@ else:
when defined(ssl): when defined(ssl):
type type
ESSL* = object of ESynch SSLError* = object of Exception
TSSLCVerifyMode* = enum SSLCVerifyMode* = enum
CVerifyNone, CVerifyPeer CVerifyNone, CVerifyPeer
TSSLProtVersion* = enum SSLProtVersion* = enum
protSSLv2, protSSLv3, protTLSv1, protSSLv23 protSSLv2, protSSLv3, protTLSv1, protSSLv23
PSSLContext* = distinct PSSLCTX SSLContext* = distinct PSSLCTX
TSSLAcceptResult* = enum SSLAcceptResult* = enum
AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess
{.deprecated: [ESSL: SSLError, TSSLCVerifyMode: SSLCVerifyMode,
TSSLProtVersion: SSLProtVersion, PSSLContext: SSLContext,
TSSLAcceptResult: SSLAcceptResult].}
const const
BufferSize*: int = 4000 ## size of a buffered socket's buffer BufferSize*: int = 4000 ## size of a buffered socket's buffer
type type
TSocketImpl = object ## socket type TSocketImpl = object ## socket type
fd: TSocketHandle fd: SocketHandle
case isBuffered: bool # determines whether this socket is buffered. case isBuffered: bool # determines whether this socket is buffered.
of true: of true:
buffer: array[0..BufferSize, char] buffer: array[0..BufferSize, char]
@ -76,31 +80,31 @@ type
case isSsl: bool case isSsl: bool
of true: of true:
sslHandle: PSSL sslHandle: PSSL
sslContext: PSSLContext sslContext: SSLContext
sslNoHandshake: bool # True if needs handshake. sslNoHandshake: bool # True if needs handshake.
sslHasPeekChar: bool sslHasPeekChar: bool
sslPeekChar: char sslPeekChar: char
of false: nil of false: nil
nonblocking: bool nonblocking: bool
TSocket* = ref TSocketImpl Socket* = ref TSocketImpl
TPort* = distinct uint16 ## port type Port* = distinct uint16 ## port type
TDomain* = enum ## domain, which specifies the protocol family of the Domain* = enum ## domain, which specifies the protocol family of the
## created socket. Other domains than those that are listed ## created socket. Other domains than those that are listed
## here are unsupported. ## here are unsupported.
AF_UNIX, ## for local socket (using a file). Unsupported on Windows. AF_UNIX, ## for local socket (using a file). Unsupported on Windows.
AF_INET = 2, ## for network protocol IPv4 or AF_INET = 2, ## for network protocol IPv4 or
AF_INET6 = 23 ## for network protocol IPv6. AF_INET6 = 23 ## for network protocol IPv6.
TType* = enum ## second argument to `socket` proc SockType* = enum ## second argument to `socket` proc
SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets
SOCK_DGRAM = 2, ## datagram service or Datagram Sockets SOCK_DGRAM = 2, ## datagram service or Datagram Sockets
SOCK_RAW = 3, ## raw protocols atop the network layer. SOCK_RAW = 3, ## raw protocols atop the network layer.
SOCK_SEQPACKET = 5 ## reliable sequenced packet service SOCK_SEQPACKET = 5 ## reliable sequenced packet service
TProtocol* = enum ## third argument to `socket` proc Protocol* = enum ## third argument to `socket` proc
IPPROTO_TCP = 6, ## Transmission control protocol. IPPROTO_TCP = 6, ## Transmission control protocol.
IPPROTO_UDP = 17, ## User datagram protocol. IPPROTO_UDP = 17, ## User datagram protocol.
IPPROTO_IP, ## Internet protocol. Unsupported on Windows. IPPROTO_IP, ## Internet protocol. Unsupported on Windows.
@ -108,33 +112,39 @@ type
IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows. IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows.
IPPROTO_ICMP ## Control message protocol. Unsupported on Windows. IPPROTO_ICMP ## Control message protocol. Unsupported on Windows.
TServent* {.pure, final.} = object ## information about a service Servent* = object ## information about a service
name*: string name*: string
aliases*: seq[string] aliases*: seq[string]
port*: TPort port*: Port
proto*: string proto*: string
Thostent* {.pure, final.} = object ## information about a given host Hostent* = object ## information about a given host
name*: string name*: string
aliases*: seq[string] aliases*: seq[string]
addrtype*: TDomain addrtype*: Domain
length*: int length*: int
addrList*: seq[string] addrList*: seq[string]
TSOBool* = enum ## Boolean socket options. SOBool* = enum ## Boolean socket options.
OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive, OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive,
OptOOBInline, OptReuseAddr OptOOBInline, OptReuseAddr
TRecvLineResult* = enum ## result for recvLineAsync RecvLineResult* = enum ## result for recvLineAsync
RecvFullLine, RecvPartialLine, RecvDisconnected, RecvFail RecvFullLine, RecvPartialLine, RecvDisconnected, RecvFail
TReadLineResult* = enum ## result for readLineAsync ReadLineResult* = enum ## result for readLineAsync
ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone
ETimeout* = object of Exception TimeoutError* = object of Exception
{.deprecated: [TSocket: Socket, TType: SockType, TPort: Port, TDomain: Domain,
TProtocol: Protocol, TServent: Servent, THostent: Hostent,
TSOBool: SOBool, TRecvLineResult: RecvLineResult,
TReadLineResult: ReadLineResult, ETimeout: TimeoutError].}
let let
invalidSocket*: TSocket = nil ## invalid socket invalidSocket*: Socket = nil ## invalid socket
when defined(windows): when defined(windows):
let let
@ -143,7 +153,7 @@ else:
let let
osInvalidSocket = posix.INVALID_SOCKET osInvalidSocket = posix.INVALID_SOCKET
proc newTSocket(fd: TSocketHandle, isBuff: bool): TSocket = proc newTSocket(fd: SocketHandle, isBuff: bool): Socket =
if fd == osInvalidSocket: if fd == osInvalidSocket:
return nil return nil
new(result) new(result)
@ -153,10 +163,10 @@ proc newTSocket(fd: TSocketHandle, isBuff: bool): TSocket =
result.currPos = 0 result.currPos = 0
result.nonblocking = false result.nonblocking = false
proc `==`*(a, b: TPort): bool {.borrow.} proc `==`*(a, b: Port): bool {.borrow.}
## ``==`` for ports. ## ``==`` for ports.
proc `$`*(p: TPort): string {.borrow.} proc `$`*(p: Port): string {.borrow.}
## returns the port number as a string ## returns the port number as a string
proc ntohl*(x: int32): int32 = proc ntohl*(x: int32): int32 =
@ -189,14 +199,14 @@ proc htons*(x: int16): int16 =
result = sockets.ntohs(x) result = sockets.ntohs(x)
when defined(Posix): when defined(Posix):
proc toInt(domain: TDomain): cint = proc toInt(domain: Domain): cint =
case domain case domain
of AF_UNIX: result = posix.AF_UNIX of AF_UNIX: result = posix.AF_UNIX
of AF_INET: result = posix.AF_INET of AF_INET: result = posix.AF_INET
of AF_INET6: result = posix.AF_INET6 of AF_INET6: result = posix.AF_INET6
else: discard else: discard
proc toInt(typ: TType): cint = proc toInt(typ: SockType): cint =
case typ case typ
of SOCK_STREAM: result = posix.SOCK_STREAM of SOCK_STREAM: result = posix.SOCK_STREAM
of SOCK_DGRAM: result = posix.SOCK_DGRAM of SOCK_DGRAM: result = posix.SOCK_DGRAM
@ -204,7 +214,7 @@ when defined(Posix):
of SOCK_RAW: result = posix.SOCK_RAW of SOCK_RAW: result = posix.SOCK_RAW
else: discard else: discard
proc toInt(p: TProtocol): cint = proc toInt(p: Protocol): cint =
case p case p
of IPPROTO_TCP: result = posix.IPPROTO_TCP of IPPROTO_TCP: result = posix.IPPROTO_TCP
of IPPROTO_UDP: result = posix.IPPROTO_UDP of IPPROTO_UDP: result = posix.IPPROTO_UDP
@ -215,13 +225,13 @@ when defined(Posix):
else: discard else: discard
else: else:
proc toInt(domain: TDomain): cint = proc toInt(domain: Domain): cint =
result = toU16(ord(domain)) result = toU16(ord(domain))
proc toInt(typ: TType): cint = proc toInt(typ: SockType): cint =
result = cint(ord(typ)) result = cint(ord(typ))
proc toInt(p: TProtocol): cint = proc toInt(p: Protocol): cint =
result = cint(ord(p)) result = cint(ord(p))
proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
@ -336,7 +346,7 @@ when defined(ssl):
if SSLSetFd(socket.sslHandle, socket.fd) != 1: if SSLSetFd(socket.sslHandle, socket.fd) != 1:
SSLError() SSLError()
proc socketError*(socket: TSocket, err: int = -1, async = false) = proc raiseSocketError*(socket: TSocket, err: int = -1, async = false) =
## Raises proper errors based on return values of ``recv`` functions. ## Raises proper errors based on return values of ``recv`` functions.
## ##
## If ``async`` is ``True`` no error will be thrown in the case when the ## If ``async`` is ``True`` no error will be thrown in the case when the
@ -370,18 +380,18 @@ proc socketError*(socket: TSocket, err: int = -1, async = false) =
when defined(windows): when defined(windows):
if lastError.int32 == WSAEWOULDBLOCK: if lastError.int32 == WSAEWOULDBLOCK:
return return
else: osError(lastError) else: raiseOSError(lastError)
else: else:
if lastError.int32 == EAGAIN or lastError.int32 == EWOULDBLOCK: if lastError.int32 == EAGAIN or lastError.int32 == EWOULDBLOCK:
return return
else: osError(lastError) else: raiseOSError(lastError)
else: osError(lastError) else: raiseOSError(lastError)
proc listen*(socket: TSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} = proc listen*(socket: TSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
## Marks ``socket`` as accepting connections. ## Marks ``socket`` as accepting connections.
## ``Backlog`` specifies the maximum length of the ## ``Backlog`` specifies the maximum length of the
## queue of pending connections. ## queue of pending connections.
if listen(socket.fd, cint(backlog)) < 0'i32: osError(osLastError()) if listen(socket.fd, cint(backlog)) < 0'i32: raiseOSError(osLastError())
proc invalidIp4(s: string) {.noreturn, noinline.} = proc invalidIp4(s: string) {.noreturn, noinline.} =
raise newException(ValueError, "invalid ip4 address: " & s) raise newException(ValueError, "invalid ip4 address: " & s)
@ -420,7 +430,7 @@ template gaiNim(a, p, h, list: expr): stmt =
var gaiResult = getaddrinfo(a, $p, addr(h), list) var gaiResult = getaddrinfo(a, $p, addr(h), list)
if gaiResult != 0'i32: if gaiResult != 0'i32:
when defined(windows): when defined(windows):
osError(osLastError()) raiseOSError(osLastError())
else: else:
raise newException(OSError, $gai_strerror(gaiResult)) raise newException(OSError, $gai_strerror(gaiResult))
@ -440,7 +450,7 @@ proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {.
name.sin_addr.s_addr = sockets.htonl(INADDR_ANY) name.sin_addr.s_addr = sockets.htonl(INADDR_ANY)
if bindSocket(socket.fd, cast[ptr TSockAddr](addr(name)), if bindSocket(socket.fd, cast[ptr TSockAddr](addr(name)),
sizeof(name).TSocklen) < 0'i32: sizeof(name).TSocklen) < 0'i32:
osError(osLastError()) raiseOSError(osLastError())
else: else:
var hints: Taddrinfo var hints: Taddrinfo
var aiList: ptr Taddrinfo = nil var aiList: ptr Taddrinfo = nil
@ -449,7 +459,7 @@ proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {.
hints.ai_protocol = toInt(IPPROTO_TCP) hints.ai_protocol = toInt(IPPROTO_TCP)
gaiNim(address, port, hints, aiList) gaiNim(address, port, hints, aiList)
if bindSocket(socket.fd, aiList.ai_addr, aiList.ai_addrlen.TSocklen) < 0'i32: if bindSocket(socket.fd, aiList.ai_addr, aiList.ai_addrlen.TSocklen) < 0'i32:
osError(osLastError()) raiseOSError(osLastError())
proc getSockName*(socket: TSocket): TPort = proc getSockName*(socket: TSocket): TPort =
## returns the socket's associated port number. ## returns the socket's associated port number.
@ -463,7 +473,7 @@ proc getSockName*(socket: TSocket): TPort =
var namelen = sizeof(name).TSocklen var namelen = sizeof(name).TSocklen
if getsockname(socket.fd, cast[ptr TSockAddr](addr(name)), if getsockname(socket.fd, cast[ptr TSockAddr](addr(name)),
addr(namelen)) == -1'i32: addr(namelen)) == -1'i32:
osError(osLastError()) raiseOSError(osLastError())
result = TPort(sockets.ntohs(name.sin_port)) result = TPort(sockets.ntohs(name.sin_port))
template acceptAddrPlain(noClientRet, successRet: expr, template acceptAddrPlain(noClientRet, successRet: expr,
@ -484,7 +494,7 @@ template acceptAddrPlain(noClientRet, successRet: expr,
return return
else: else:
return noClientRet return noClientRet
else: osError(err) else: raiseOSError(err)
else: else:
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK: if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
client = invalidSocket client = invalidSocket
@ -493,7 +503,7 @@ template acceptAddrPlain(noClientRet, successRet: expr,
return return
else: else:
return noClientRet return noClientRet
else: osError(err) else: raiseOSError(err)
else: else:
client.fd = sock client.fd = sock
client.isBuffered = server.isBuffered client.isBuffered = server.isBuffered
@ -680,7 +690,7 @@ proc getHostByAddr*(ip: string): Thostent {.tags: [ReadIOEffect].} =
when defined(windows): when defined(windows):
var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint, var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
cint(sockets.AF_INET)) cint(sockets.AF_INET))
if s == nil: osError(osLastError()) if s == nil: raiseOSError(osLastError())
else: else:
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).TSocklen, var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).TSocklen,
cint(posix.AF_INET)) cint(posix.AF_INET))
@ -707,7 +717,7 @@ proc getHostByName*(name: string): Thostent {.tags: [ReadIOEffect].} =
var s = winlean.gethostbyname(name) var s = winlean.gethostbyname(name)
else: else:
var s = posix.gethostbyname(name) var s = posix.gethostbyname(name)
if s == nil: osError(osLastError()) if s == nil: raiseOSError(osLastError())
result.name = $s.h_name result.name = $s.h_name
result.aliases = cstringArrayToSeq(s.h_aliases) result.aliases = cstringArrayToSeq(s.h_aliases)
when defined(windows): when defined(windows):
@ -729,7 +739,7 @@ proc getSockOptInt*(socket: TSocket, level, optname: int): int {.
var size = sizeof(res).TSocklen var size = sizeof(res).TSocklen
if getsockopt(socket.fd, cint(level), cint(optname), if getsockopt(socket.fd, cint(level), cint(optname),
addr(res), addr(size)) < 0'i32: addr(res), addr(size)) < 0'i32:
osError(osLastError()) raiseOSError(osLastError())
result = int(res) result = int(res)
proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {. proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {.
@ -738,7 +748,7 @@ proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {.
var value = cint(optval) var value = cint(optval)
if setsockopt(socket.fd, cint(level), cint(optname), addr(value), if setsockopt(socket.fd, cint(level), cint(optname), addr(value),
sizeof(value).TSocklen) < 0'i32: sizeof(value).TSocklen) < 0'i32:
osError(osLastError()) raiseOSError(osLastError())
proc toCInt(opt: TSOBool): cint = proc toCInt(opt: TSOBool): cint =
case opt case opt
@ -757,7 +767,7 @@ proc getSockOpt*(socket: TSocket, opt: TSOBool, level = SOL_SOCKET): bool {.
var size = sizeof(res).TSocklen var size = sizeof(res).TSocklen
if getsockopt(socket.fd, cint(level), toCInt(opt), if getsockopt(socket.fd, cint(level), toCInt(opt),
addr(res), addr(size)) < 0'i32: addr(res), addr(size)) < 0'i32:
osError(osLastError()) raiseOSError(osLastError())
result = res != 0 result = res != 0
proc setSockOpt*(socket: TSocket, opt: TSOBool, value: bool, level = SOL_SOCKET) {. proc setSockOpt*(socket: TSocket, opt: TSOBool, value: bool, level = SOL_SOCKET) {.
@ -766,7 +776,7 @@ proc setSockOpt*(socket: TSocket, opt: TSOBool, value: bool, level = SOL_SOCKET)
var valuei = cint(if value: 1 else: 0) var valuei = cint(if value: 1 else: 0)
if setsockopt(socket.fd, cint(level), toCInt(opt), addr(valuei), if setsockopt(socket.fd, cint(level), toCInt(opt), addr(valuei),
sizeof(valuei).TSocklen) < 0'i32: sizeof(valuei).TSocklen) < 0'i32:
osError(osLastError()) raiseOSError(osLastError())
proc connect*(socket: TSocket, address: string, port = TPort(0), proc connect*(socket: TSocket, address: string, port = TPort(0),
af: TDomain = AF_INET) {.tags: [ReadIOEffect].} = af: TDomain = AF_INET) {.tags: [ReadIOEffect].} =
@ -784,7 +794,7 @@ proc connect*(socket: TSocket, address: string, port = TPort(0),
gaiNim(address, port, hints, aiList) gaiNim(address, port, hints, aiList)
# try all possibilities: # try all possibilities:
var success = false var success = false
var lastError: TOSErrorCode var lastError: OSErrorCode
var it = aiList var it = aiList
while it != nil: while it != nil:
if connect(socket.fd, it.ai_addr, it.ai_addrlen.TSocklen) == 0'i32: if connect(socket.fd, it.ai_addr, it.ai_addrlen.TSocklen) == 0'i32:
@ -794,7 +804,7 @@ proc connect*(socket: TSocket, address: string, port = TPort(0),
it = it.ai_next it = it.ai_next
freeaddrinfo(aiList) freeaddrinfo(aiList)
if not success: osError(lastError) if not success: raiseOSError(lastError)
when defined(ssl): when defined(ssl):
if socket.isSSL: if socket.isSSL:
@ -847,7 +857,7 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0),
gaiNim(name, port, hints, aiList) gaiNim(name, port, hints, aiList)
# try all possibilities: # try all possibilities:
var success = false var success = false
var lastError: TOSErrorCode var lastError: OSErrorCode
var it = aiList var it = aiList
while it != nil: while it != nil:
var ret = connect(socket.fd, it.ai_addr, it.ai_addrlen.TSocklen) var ret = connect(socket.fd, it.ai_addr, it.ai_addrlen.TSocklen)
@ -869,7 +879,7 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0),
it = it.ai_next it = it.ai_next
freeaddrinfo(aiList) freeaddrinfo(aiList)
if not success: osError(lastError) if not success: raiseOSError(lastError)
when defined(ssl): when defined(ssl):
if socket.isSSL: if socket.isSSL:
socket.sslNoHandshake = true socket.sslNoHandshake = true
@ -1133,7 +1143,7 @@ proc waitFor(socket: TSocket, waited: var float, timeout, size: int,
result = min(result, size) result = min(result, size)
else: else:
if timeout - int(waited * 1000.0) < 1: if timeout - int(waited * 1000.0) < 1:
raise newException(ETimeout, "Call to '" & funcName & "' timed out.") raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
when defined(ssl): when defined(ssl):
if socket.isSSL: if socket.isSSL:
@ -1147,9 +1157,9 @@ proc waitFor(socket: TSocket, waited: var float, timeout, size: int,
var s = @[socket] var s = @[socket]
var startTime = epochTime() var startTime = epochTime()
let selRet = select(s, timeout - int(waited * 1000.0)) let selRet = select(s, timeout - int(waited * 1000.0))
if selRet < 0: osError(osLastError()) if selRet < 0: raiseOSError(osLastError())
if selRet != 1: if selRet != 1:
raise newException(ETimeout, "Call to '" & funcName & "' timed out.") raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
waited += (epochTime() - startTime) waited += (epochTime() - startTime)
proc recv*(socket: TSocket, data: pointer, size: int, timeout: int): int {. proc recv*(socket: TSocket, data: pointer, size: int, timeout: int): int {.
@ -1185,7 +1195,7 @@ proc recv*(socket: TSocket, data: var string, size: int, timeout = -1): int =
result = recv(socket, cstring(data), size, timeout) result = recv(socket, cstring(data), size, timeout)
if result < 0: if result < 0:
data.setLen(0) data.setLen(0)
socket.socketError(result) socket.raiseSocketError(result)
data.setLen(result) data.setLen(result)
proc recvAsync*(socket: TSocket, data: var string, size: int): int = proc recvAsync*(socket: TSocket, data: var string, size: int): int =
@ -1199,7 +1209,7 @@ proc recvAsync*(socket: TSocket, data: var string, size: int): int =
result = recv(socket, cstring(data), size) result = recv(socket, cstring(data), size)
if result < 0: if result < 0:
data.setLen(0) data.setLen(0)
socket.socketError(async = true) socket.raiseSocketError(async = true)
result = -1 result = -1
data.setLen(result) data.setLen(result)
@ -1296,14 +1306,14 @@ proc readLine*(socket: TSocket, line: var TaintedString, timeout = -1) {.
var c: char var c: char
discard waitFor(socket, waited, timeout, 1, "readLine") discard waitFor(socket, waited, timeout, 1, "readLine")
var n = recv(socket, addr(c), 1) var n = recv(socket, addr(c), 1)
if n < 0: socket.socketError() if n < 0: socket.raiseSocketError()
elif n == 0: return elif n == 0: return
if c == '\r': if c == '\r':
discard waitFor(socket, waited, timeout, 1, "readLine") discard waitFor(socket, waited, timeout, 1, "readLine")
n = peekChar(socket, c) n = peekChar(socket, c)
if n > 0 and c == '\L': if n > 0 and c == '\L':
discard recv(socket, addr(c), 1) discard recv(socket, addr(c), 1)
elif n <= 0: socket.socketError() elif n <= 0: socket.raiseSocketError()
addNLIfEmpty() addNLIfEmpty()
return return
elif c == '\L': elif c == '\L':
@ -1357,7 +1367,7 @@ proc readLineAsync*(socket: TSocket,
setLen(line.string, 0) setLen(line.string, 0)
template errorOrNone = template errorOrNone =
socket.socketError(async = true) socket.raiseSocketError(async = true)
return ReadNone return ReadNone
while true: while true:
@ -1390,7 +1400,7 @@ proc recv*(socket: TSocket): TaintedString {.tags: [ReadIOEffect], deprecated.}
var pos = 0 var pos = 0
while true: while true:
var bytesRead = recv(socket, addr(string(result)[pos]), bufSize-1) var bytesRead = recv(socket, addr(string(result)[pos]), bufSize-1)
if bytesRead == -1: osError(osLastError()) if bytesRead == -1: raiseOSError(osLastError())
setLen(result.string, pos + bytesRead) setLen(result.string, pos + bytesRead)
if bytesRead != bufSize-1: break if bytesRead != bufSize-1: break
# increase capacity: # increase capacity:
@ -1463,11 +1473,11 @@ proc recvAsync*(socket: TSocket, s: var TaintedString): bool {.
when defined(windows): when defined(windows):
if err.int32 == WSAEWOULDBLOCK: if err.int32 == WSAEWOULDBLOCK:
return false return false
else: osError(err) else: raiseOSError(err)
else: else:
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK: if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
return false return false
else: osError(err) else: raiseOSError(err)
setLen(s.string, pos + bytesRead) setLen(s.string, pos + bytesRead)
if bytesRead != bufSize-1: break if bytesRead != bufSize-1: break
@ -1516,11 +1526,11 @@ proc recvFromAsync*(socket: TSocket, data: var string, length: int,
when defined(windows): when defined(windows):
if err.int32 == WSAEWOULDBLOCK: if err.int32 == WSAEWOULDBLOCK:
return false return false
else: osError(err) else: raiseOSError(err)
else: else:
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK: if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
return false return false
else: osError(err) else: raiseOSError(err)
proc skip*(socket: TSocket) {.tags: [ReadIOEffect], deprecated.} = proc skip*(socket: TSocket) {.tags: [ReadIOEffect], deprecated.} =
## skips all the data that is pending for the socket ## skips all the data that is pending for the socket
@ -1570,7 +1580,7 @@ proc send*(socket: TSocket, data: string) {.tags: [WriteIOEffect].} =
if socket.isSSL: if socket.isSSL:
SSLError() SSLError()
osError(osLastError()) raiseOSError(osLastError())
if sent != data.len: if sent != data.len:
raise newException(OSError, "Could not send all data.") raise newException(OSError, "Could not send all data.")
@ -1607,11 +1617,11 @@ proc sendAsync*(socket: TSocket, data: string): int {.tags: [WriteIOEffect].} =
when defined(windows): when defined(windows):
if err.int32 == WSAEINPROGRESS: if err.int32 == WSAEINPROGRESS:
return 0 return 0
else: osError(err) else: raiseOSError(err)
else: else:
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK: if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
return 0 return 0
else: osError(err) else: raiseOSError(err)
proc trySend*(socket: TSocket, data: string): bool {.tags: [WriteIOEffect].} = proc trySend*(socket: TSocket, data: string): bool {.tags: [WriteIOEffect].} =
@ -1667,15 +1677,15 @@ proc setBlocking(s: TSocket, blocking: bool) =
when defined(Windows): when defined(Windows):
var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
if ioctlsocket(s.fd, FIONBIO, addr(mode)) == -1: if ioctlsocket(s.fd, FIONBIO, addr(mode)) == -1:
osError(osLastError()) raiseOSError(osLastError())
else: # BSD sockets else: # BSD sockets
var x: int = fcntl(s.fd, F_GETFL, 0) var x: int = fcntl(s.fd, F_GETFL, 0)
if x == -1: if x == -1:
osError(osLastError()) raiseOSError(osLastError())
else: else:
var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
if fcntl(s.fd, F_SETFL, mode) == -1: if fcntl(s.fd, F_SETFL, mode) == -1:
osError(osLastError()) raiseOSError(osLastError())
s.nonblocking = not blocking s.nonblocking = not blocking
discard """ proc setReuseAddr*(s: TSocket) = discard """ proc setReuseAddr*(s: TSocket) =
@ -1715,6 +1725,6 @@ proc isBlocking*(socket: TSocket): bool = not socket.nonblocking
when defined(Windows): when defined(Windows):
var wsa: TWSAData var wsa: TWSAData
if wsaStartup(0x0101'i16, addr wsa) != 0: osError(osLastError()) if wsaStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError())

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -8,8 +8,8 @@
# #
## This module provides a stream interface and two implementations thereof: ## This module provides a stream interface and two implementations thereof:
## the `PFileStream` and the `PStringStream` which implement the stream ## the `FileStream` and the `StringStream` which implement the stream
## interface for Nimrod file objects (`TFile`) and strings. Other modules ## interface for Nimrod file objects (`File`) and strings. Other modules
## may provide other implementations for this standard stream interface. ## may provide other implementations for this standard stream interface.
include "system/inclrtl" include "system/inclrtl"
@ -19,81 +19,83 @@ proc newEIO(msg: string): ref IOError =
result.msg = msg result.msg = msg
type type
PStream* = ref TStream Stream* = ref StreamObj
TStream* = object of RootObj ## Stream interface that supports StreamObj* = object of RootObj ## Stream interface that supports
## writing or reading. Note that these fields ## writing or reading. Note that these fields
## here shouldn't be used directly. They are ## here shouldn't be used directly. They are
## accessible so that a stream implementation ## accessible so that a stream implementation
## can override them. ## can override them.
closeImpl*: proc (s: PStream) {.nimcall, tags: [], gcsafe.} closeImpl*: proc (s: Stream) {.nimcall, tags: [], gcsafe.}
atEndImpl*: proc (s: PStream): bool {.nimcall, tags: [], gcsafe.} atEndImpl*: proc (s: Stream): bool {.nimcall, tags: [], gcsafe.}
setPositionImpl*: proc (s: PStream, pos: int) {.nimcall, tags: [], gcsafe.} setPositionImpl*: proc (s: Stream, pos: int) {.nimcall, tags: [], gcsafe.}
getPositionImpl*: proc (s: PStream): int {.nimcall, tags: [], gcsafe.} getPositionImpl*: proc (s: Stream): int {.nimcall, tags: [], gcsafe.}
readDataImpl*: proc (s: PStream, buffer: pointer, readDataImpl*: proc (s: Stream, buffer: pointer,
bufLen: int): int {.nimcall, tags: [ReadIOEffect], gcsafe.} bufLen: int): int {.nimcall, tags: [ReadIOEffect], gcsafe.}
writeDataImpl*: proc (s: PStream, buffer: pointer, bufLen: int) {.nimcall, writeDataImpl*: proc (s: Stream, buffer: pointer, bufLen: int) {.nimcall,
tags: [WriteIOEffect], gcsafe.} tags: [WriteIOEffect], gcsafe.}
flushImpl*: proc (s: PStream) {.nimcall, tags: [WriteIOEffect], gcsafe.} flushImpl*: proc (s: Stream) {.nimcall, tags: [WriteIOEffect], gcsafe.}
proc flush*(s: PStream) = {.deprecated: [PStream: Stream, TStream: StreamObj].}
proc flush*(s: Stream) =
## flushes the buffers that the stream `s` might use. ## flushes the buffers that the stream `s` might use.
if not isNil(s.flushImpl): s.flushImpl(s) if not isNil(s.flushImpl): s.flushImpl(s)
proc close*(s: PStream) = proc close*(s: Stream) =
## closes the stream `s`. ## closes the stream `s`.
if not isNil(s.closeImpl): s.closeImpl(s) if not isNil(s.closeImpl): s.closeImpl(s)
proc close*(s, unused: PStream) {.deprecated.} = proc close*(s, unused: Stream) {.deprecated.} =
## closes the stream `s`. ## closes the stream `s`.
s.closeImpl(s) s.closeImpl(s)
proc atEnd*(s: PStream): bool = proc atEnd*(s: Stream): bool =
## checks if more data can be read from `f`. Returns true if all data has ## checks if more data can be read from `f`. Returns true if all data has
## been read. ## been read.
result = s.atEndImpl(s) result = s.atEndImpl(s)
proc atEnd*(s, unused: PStream): bool {.deprecated.} = proc atEnd*(s, unused: Stream): bool {.deprecated.} =
## checks if more data can be read from `f`. Returns true if all data has ## checks if more data can be read from `f`. Returns true if all data has
## been read. ## been read.
result = s.atEndImpl(s) result = s.atEndImpl(s)
proc setPosition*(s: PStream, pos: int) = proc setPosition*(s: Stream, pos: int) =
## sets the position `pos` of the stream `s`. ## sets the position `pos` of the stream `s`.
s.setPositionImpl(s, pos) s.setPositionImpl(s, pos)
proc setPosition*(s, unused: PStream, pos: int) {.deprecated.} = proc setPosition*(s, unused: Stream, pos: int) {.deprecated.} =
## sets the position `pos` of the stream `s`. ## sets the position `pos` of the stream `s`.
s.setPositionImpl(s, pos) s.setPositionImpl(s, pos)
proc getPosition*(s: PStream): int = proc getPosition*(s: Stream): int =
## retrieves the current position in the stream `s`. ## retrieves the current position in the stream `s`.
result = s.getPositionImpl(s) result = s.getPositionImpl(s)
proc getPosition*(s, unused: PStream): int {.deprecated.} = proc getPosition*(s, unused: Stream): int {.deprecated.} =
## retrieves the current position in the stream `s`. ## retrieves the current position in the stream `s`.
result = s.getPositionImpl(s) result = s.getPositionImpl(s)
proc readData*(s: PStream, buffer: pointer, bufLen: int): int = proc readData*(s: Stream, buffer: pointer, bufLen: int): int =
## low level proc that reads data into an untyped `buffer` of `bufLen` size. ## low level proc that reads data into an untyped `buffer` of `bufLen` size.
result = s.readDataImpl(s, buffer, bufLen) result = s.readDataImpl(s, buffer, bufLen)
proc readData*(s, unused: PStream, buffer: pointer, proc readData*(s, unused: Stream, buffer: pointer,
bufLen: int): int {.deprecated.} = bufLen: int): int {.deprecated.} =
## low level proc that reads data into an untyped `buffer` of `bufLen` size. ## low level proc that reads data into an untyped `buffer` of `bufLen` size.
result = s.readDataImpl(s, buffer, bufLen) result = s.readDataImpl(s, buffer, bufLen)
proc writeData*(s: PStream, buffer: pointer, bufLen: int) = proc writeData*(s: Stream, buffer: pointer, bufLen: int) =
## low level proc that writes an untyped `buffer` of `bufLen` size ## low level proc that writes an untyped `buffer` of `bufLen` size
## to the stream `s`. ## to the stream `s`.
s.writeDataImpl(s, buffer, bufLen) s.writeDataImpl(s, buffer, bufLen)
proc writeData*(s, unused: PStream, buffer: pointer, proc writeData*(s, unused: Stream, buffer: pointer,
bufLen: int) {.deprecated.} = bufLen: int) {.deprecated.} =
## low level proc that writes an untyped `buffer` of `bufLen` size ## low level proc that writes an untyped `buffer` of `bufLen` size
## to the stream `s`. ## to the stream `s`.
s.writeDataImpl(s, buffer, bufLen) s.writeDataImpl(s, buffer, bufLen)
proc write*[T](s: PStream, x: T) = proc write*[T](s: Stream, x: T) =
## generic write procedure. Writes `x` to the stream `s`. Implementation: ## generic write procedure. Writes `x` to the stream `s`. Implementation:
## ##
## .. code-block:: Nimrod ## .. code-block:: Nimrod
@ -103,63 +105,63 @@ proc write*[T](s: PStream, x: T) =
shallowCopy(y, x) shallowCopy(y, x)
writeData(s, addr(y), sizeof(y)) writeData(s, addr(y), sizeof(y))
proc write*(s: PStream, x: string) = proc write*(s: Stream, x: string) =
## writes the string `x` to the the stream `s`. No length field or ## writes the string `x` to the the stream `s`. No length field or
## terminating zero is written. ## terminating zero is written.
writeData(s, cstring(x), x.len) writeData(s, cstring(x), x.len)
proc writeln*(s: PStream, args: varargs[string, `$`]) = proc writeln*(s: Stream, args: varargs[string, `$`]) =
## writes one or more strings to the the stream `s` followed ## writes one or more strings to the the stream `s` followed
## by a new line. No length field or terminating zero is written. ## by a new line. No length field or terminating zero is written.
for str in args: write(s, str) for str in args: write(s, str)
write(s, "\n") write(s, "\n")
proc read[T](s: PStream, result: var T) = proc read[T](s: Stream, result: var T) =
## generic read procedure. Reads `result` from the stream `s`. ## generic read procedure. Reads `result` from the stream `s`.
if readData(s, addr(result), sizeof(T)) != sizeof(T): if readData(s, addr(result), sizeof(T)) != sizeof(T):
raise newEIO("cannot read from stream") raise newEIO("cannot read from stream")
proc readChar*(s: PStream): char = proc readChar*(s: Stream): char =
## reads a char from the stream `s`. Raises `EIO` if an error occured. ## reads a char from the stream `s`. Raises `EIO` if an error occured.
## Returns '\0' as an EOF marker. ## Returns '\0' as an EOF marker.
if readData(s, addr(result), sizeof(result)) != 1: result = '\0' if readData(s, addr(result), sizeof(result)) != 1: result = '\0'
proc readBool*(s: PStream): bool = proc readBool*(s: Stream): bool =
## reads a bool from the stream `s`. Raises `EIO` if an error occured. ## reads a bool from the stream `s`. Raises `EIO` if an error occured.
read(s, result) read(s, result)
proc readInt8*(s: PStream): int8 = proc readInt8*(s: Stream): int8 =
## reads an int8 from the stream `s`. Raises `EIO` if an error occured. ## reads an int8 from the stream `s`. Raises `EIO` if an error occured.
read(s, result) read(s, result)
proc readInt16*(s: PStream): int16 = proc readInt16*(s: Stream): int16 =
## reads an int16 from the stream `s`. Raises `EIO` if an error occured. ## reads an int16 from the stream `s`. Raises `EIO` if an error occured.
read(s, result) read(s, result)
proc readInt32*(s: PStream): int32 = proc readInt32*(s: Stream): int32 =
## reads an int32 from the stream `s`. Raises `EIO` if an error occured. ## reads an int32 from the stream `s`. Raises `EIO` if an error occured.
read(s, result) read(s, result)
proc readInt64*(s: PStream): int64 = proc readInt64*(s: Stream): int64 =
## reads an int64 from the stream `s`. Raises `EIO` if an error occured. ## reads an int64 from the stream `s`. Raises `EIO` if an error occured.
read(s, result) read(s, result)
proc readFloat32*(s: PStream): float32 = proc readFloat32*(s: Stream): float32 =
## reads a float32 from the stream `s`. Raises `EIO` if an error occured. ## reads a float32 from the stream `s`. Raises `EIO` if an error occured.
read(s, result) read(s, result)
proc readFloat64*(s: PStream): float64 = proc readFloat64*(s: Stream): float64 =
## reads a float64 from the stream `s`. Raises `EIO` if an error occured. ## reads a float64 from the stream `s`. Raises `EIO` if an error occured.
read(s, result) read(s, result)
proc readStr*(s: PStream, length: int): TaintedString = proc readStr*(s: Stream, length: int): TaintedString =
## reads a string of length `length` from the stream `s`. Raises `EIO` if ## reads a string of length `length` from the stream `s`. Raises `EIO` if
## an error occured. ## an error occured.
result = newString(length).TaintedString result = newString(length).TaintedString
var L = readData(s, addr(string(result)[0]), length) var L = readData(s, addr(string(result)[0]), length)
if L != length: setLen(result.string, L) if L != length: setLen(result.string, L)
proc readLine*(s: PStream, line: var TaintedString): bool = proc readLine*(s: Stream, line: var TaintedString): bool =
## reads a line of text from the stream `s` into `line`. `line` must not be ## reads a line of text from the stream `s` into `line`. `line` must not be
## ``nil``! May throw an IO exception. ## ``nil``! May throw an IO exception.
## A line of text may be delimited by ``CR``, ``LF`` or ## A line of text may be delimited by ``CR``, ``LF`` or
@ -179,7 +181,7 @@ proc readLine*(s: PStream, line: var TaintedString): bool =
line.string.add(c) line.string.add(c)
result = true result = true
proc readLine*(s: PStream): TaintedString = proc readLine*(s: Stream): TaintedString =
## Reads a line from a stream `s`. Note: This is not very efficient. Raises ## Reads a line from a stream `s`. Note: This is not very efficient. Raises
## `EIO` if an error occured. ## `EIO` if an error occured.
result = TaintedString"" result = TaintedString""
@ -194,42 +196,44 @@ proc readLine*(s: PStream): TaintedString =
result.string.add(c) result.string.add(c)
type type
PStringStream* = ref TStringStream ## a stream that encapsulates a string StringStream* = ref StringStreamObj ## a stream that encapsulates a string
TStringStream* = object of TStream StringStreamObj* = object of StreamObj
data*: string data*: string
pos: int pos: int
proc ssAtEnd(s: PStream): bool = {.deprecated: [PStringStream: StringStream, TStringStream: StringStreamObj].}
var s = PStringStream(s)
proc ssAtEnd(s: Stream): bool =
var s = StringStream(s)
return s.pos >= s.data.len return s.pos >= s.data.len
proc ssSetPosition(s: PStream, pos: int) = proc ssSetPosition(s: Stream, pos: int) =
var s = PStringStream(s) var s = StringStream(s)
s.pos = clamp(pos, 0, s.data.high) s.pos = clamp(pos, 0, s.data.high)
proc ssGetPosition(s: PStream): int = proc ssGetPosition(s: Stream): int =
var s = PStringStream(s) var s = StringStream(s)
return s.pos return s.pos
proc ssReadData(s: PStream, buffer: pointer, bufLen: int): int = proc ssReadData(s: Stream, buffer: pointer, bufLen: int): int =
var s = PStringStream(s) var s = StringStream(s)
result = min(bufLen, s.data.len - s.pos) result = min(bufLen, s.data.len - s.pos)
if result > 0: if result > 0:
copyMem(buffer, addr(s.data[s.pos]), result) copyMem(buffer, addr(s.data[s.pos]), result)
inc(s.pos, result) inc(s.pos, result)
proc ssWriteData(s: PStream, buffer: pointer, bufLen: int) = proc ssWriteData(s: Stream, buffer: pointer, bufLen: int) =
var s = PStringStream(s) var s = StringStream(s)
if bufLen > 0: if bufLen > 0:
setLen(s.data, s.data.len + bufLen) setLen(s.data, s.data.len + bufLen)
copyMem(addr(s.data[s.pos]), buffer, bufLen) copyMem(addr(s.data[s.pos]), buffer, bufLen)
inc(s.pos, bufLen) inc(s.pos, bufLen)
proc ssClose(s: PStream) = proc ssClose(s: Stream) =
var s = PStringStream(s) var s = StringStream(s)
s.data = nil s.data = nil
proc newStringStream*(s: string = ""): PStringStream = proc newStringStream*(s: string = ""): StringStream =
## creates a new stream from the string `s`. ## creates a new stream from the string `s`.
new(result) new(result)
result.data = s result.data = s
@ -244,27 +248,28 @@ proc newStringStream*(s: string = ""): PStringStream =
when not defined(js): when not defined(js):
type type
PFileStream* = ref TFileStream ## a stream that encapsulates a `TFile` FileStream* = ref FileStreamObj ## a stream that encapsulates a `TFile`
TFileStream* = object of TStream FileStreamObj* = object of Stream
f: File f: File
{.deprecated: [PFileStream: FileStream, TFileStream: FileStreamObj].}
proc fsClose(s: PStream) = proc fsClose(s: Stream) =
if PFileStream(s).f != nil: if FileStream(s).f != nil:
close(PFileStream(s).f) close(FileStream(s).f)
PFileStream(s).f = nil FileStream(s).f = nil
proc fsFlush(s: PStream) = flushFile(PFileStream(s).f) proc fsFlush(s: Stream) = flushFile(FileStream(s).f)
proc fsAtEnd(s: PStream): bool = return endOfFile(PFileStream(s).f) proc fsAtEnd(s: Stream): bool = return endOfFile(FileStream(s).f)
proc fsSetPosition(s: PStream, pos: int) = setFilePos(PFileStream(s).f, pos) proc fsSetPosition(s: Stream, pos: int) = setFilePos(FileStream(s).f, pos)
proc fsGetPosition(s: PStream): int = return int(getFilePos(PFileStream(s).f)) proc fsGetPosition(s: Stream): int = return int(getFilePos(FileStream(s).f))
proc fsReadData(s: PStream, buffer: pointer, bufLen: int): int = proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int =
result = readBuffer(PFileStream(s).f, buffer, bufLen) result = readBuffer(FileStream(s).f, buffer, bufLen)
proc fsWriteData(s: PStream, buffer: pointer, bufLen: int) = proc fsWriteData(s: Stream, buffer: pointer, bufLen: int) =
if writeBuffer(PFileStream(s).f, buffer, bufLen) != bufLen: if writeBuffer(FileStream(s).f, buffer, bufLen) != bufLen:
raise newEIO("cannot write to stream") raise newEIO("cannot write to stream")
proc newFileStream*(f: File): PFileStream = proc newFileStream*(f: File): FileStream =
## creates a new stream from the file `f`. ## creates a new stream from the file `f`.
new(result) new(result)
result.f = f result.f = f
@ -276,7 +281,7 @@ when not defined(js):
result.writeDataImpl = fsWriteData result.writeDataImpl = fsWriteData
result.flushImpl = fsFlush result.flushImpl = fsFlush
proc newFileStream*(filename: string, mode: FileMode): PFileStream = proc newFileStream*(filename: string, mode: FileMode): FileStream =
## creates a new stream from the file named `filename` with the mode `mode`. ## creates a new stream from the file named `filename` with the mode `mode`.
## If the file cannot be opened, nil is returned. See the `system ## If the file cannot be opened, nil is returned. See the `system
## <system.html>`_ module for a list of available TFileMode enums. ## <system.html>`_ module for a list of available TFileMode enums.
@ -288,45 +293,47 @@ when true:
discard discard
else: else:
type type
TFileHandle* = cint ## Operating system file handle FileHandleStream* = ref FileHandleStreamObj
PFileHandleStream* = ref TFileHandleStream FileHandleStreamObj* = object of Stream
TFileHandleStream* = object of TStream handle*: FileHandle
handle*: TFileHandle
pos: int pos: int
proc newEOS(msg: string): ref EOS = {.deprecated: [PFileHandleStream: FileHandleStream,
TFileHandleStream: FileHandleStreamObj].}
proc newEOS(msg: string): ref OSError =
new(result) new(result)
result.msg = msg result.msg = msg
proc hsGetPosition(s: PFileHandleStream): int = proc hsGetPosition(s: FileHandleStream): int =
return s.pos return s.pos
when defined(windows): when defined(windows):
# do not import windows as this increases compile times: # do not import windows as this increases compile times:
nil discard
else: else:
import posix import posix
proc hsSetPosition(s: PFileHandleStream, pos: int) = proc hsSetPosition(s: FileHandleStream, pos: int) =
discard lseek(s.handle, pos, SEEK_SET) discard lseek(s.handle, pos, SEEK_SET)
proc hsClose(s: PFileHandleStream) = discard close(s.handle) proc hsClose(s: FileHandleStream) = discard close(s.handle)
proc hsAtEnd(s: PFileHandleStream): bool = proc hsAtEnd(s: FileHandleStream): bool =
var pos = hsGetPosition(s) var pos = hsGetPosition(s)
var theEnd = lseek(s.handle, 0, SEEK_END) var theEnd = lseek(s.handle, 0, SEEK_END)
result = pos >= theEnd result = pos >= theEnd
hsSetPosition(s, pos) # set position back hsSetPosition(s, pos) # set position back
proc hsReadData(s: PFileHandleStream, buffer: pointer, bufLen: int): int = proc hsReadData(s: FileHandleStream, buffer: pointer, bufLen: int): int =
result = posix.read(s.handle, buffer, bufLen) result = posix.read(s.handle, buffer, bufLen)
inc(s.pos, result) inc(s.pos, result)
proc hsWriteData(s: PFileHandleStream, buffer: pointer, bufLen: int) = proc hsWriteData(s: FileHandleStream, buffer: pointer, bufLen: int) =
if posix.write(s.handle, buffer, bufLen) != bufLen: if posix.write(s.handle, buffer, bufLen) != bufLen:
raise newEIO("cannot write to stream") raise newEIO("cannot write to stream")
inc(s.pos, bufLen) inc(s.pos, bufLen)
proc newFileHandleStream*(handle: TFileHandle): PFileHandleStream = proc newFileHandleStream*(handle: FileHandle): FileHandleStream =
new(result) new(result)
result.handle = handle result.handle = handle
result.pos = 0 result.pos = 0
@ -338,9 +345,9 @@ else:
result.writeData = hsWriteData result.writeData = hsWriteData
proc newFileHandleStream*(filename: string, proc newFileHandleStream*(filename: string,
mode: TFileMode): PFileHandleStream = mode: FileMode): FileHandleStream =
when defined(windows): when defined(windows):
nil discard
else: else:
var flags: cint var flags: cint
case mode case mode

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -18,20 +18,23 @@ import
include "system/inclrtl" include "system/inclrtl"
type type
TStringTableMode* = enum ## describes the tables operation mode StringTableMode* = enum ## describes the tables operation mode
modeCaseSensitive, ## the table is case sensitive modeCaseSensitive, ## the table is case sensitive
modeCaseInsensitive, ## the table is case insensitive modeCaseInsensitive, ## the table is case insensitive
modeStyleInsensitive ## the table is style insensitive modeStyleInsensitive ## the table is style insensitive
TKeyValuePair = tuple[key, val: string] KeyValuePair = tuple[key, val: string]
TKeyValuePairSeq = seq[TKeyValuePair] KeyValuePairSeq = seq[KeyValuePair]
TStringTable* = object of RootObj StringTableObj* = object of RootObj
counter: int counter: int
data: TKeyValuePairSeq data: KeyValuePairSeq
mode: TStringTableMode mode: StringTableMode
PStringTable* = ref TStringTable ## use this type to declare string tables StringTableRef* = ref StringTableObj ## use this type to declare string tables
proc len*(t: PStringTable): int {.rtl, extern: "nst$1".} = {.deprecated: [TStringTableMode: StringTableMode,
TStringTable: StringTableObj, PStringTable: StringTableRef].}
proc len*(t: StringTableRef): int {.rtl, extern: "nst$1".} =
## returns the number of keys in `t`. ## returns the number of keys in `t`.
result = t.counter result = t.counter
@ -54,7 +57,7 @@ iterator values*(t: PStringTable): string =
yield t.data[h].val yield t.data[h].val
type type
TFormatFlag* = enum ## flags for the `%` operator FormatFlag* = enum ## flags for the `%` operator
useEnvironment, ## use environment variable if the ``$key`` useEnvironment, ## use environment variable if the ``$key``
## is not found in the table ## is not found in the table
useEmpty, ## use the empty string as a default, thus it useEmpty, ## use the empty string as a default, thus it
@ -63,6 +66,8 @@ type
useKey ## do not replace ``$key`` if it is not found useKey ## do not replace ``$key`` if it is not found
## in the table (or in the environment) ## in the table (or in the environment)
{.deprecated: [TFormatFlag: FormatFlag].}
# implementation # implementation
const const

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -23,7 +23,7 @@ import parseutils
include "system/inclrtl" include "system/inclrtl"
type type
TCharSet* = set[char] # for compatibility with Nim TCharSet* {.deprecated.} = set[char] # for compatibility with Nim
const const
Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'} Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'}
@ -54,7 +54,7 @@ const
## make the `find() proc <#find,string,set[char],int>`_ find **invalid** ## make the `find() proc <#find,string,set[char],int>`_ find **invalid**
## characters in strings. Example: ## characters in strings. Example:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## let invalid = AllChars - Digits ## let invalid = AllChars - Digits
## doAssert "01234".find(invalid) == -1 ## doAssert "01234".find(invalid) == -1
## doAssert "01A34".find(invalid) == 2 ## doAssert "01A34".find(invalid) == 2
@ -205,7 +205,7 @@ iterator split*(s: string, seps: set[char] = Whitespace): string =
## a single split point and leading/trailing separators will be ignored. ## a single split point and leading/trailing separators will be ignored.
## The following example: ## The following example:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## for word in split(" this is an example "): ## for word in split(" this is an example "):
## writeln(stdout, word) ## writeln(stdout, word)
## ##
@ -219,13 +219,13 @@ iterator split*(s: string, seps: set[char] = Whitespace): string =
## ##
## And the following code: ## And the following code:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## for word in split(";;this;is;an;;example;;;", {';'}): ## for word in split(";;this;is;an;;example;;;", {';'}):
## writeln(stdout, word) ## writeln(stdout, word)
## ##
## ...produces the same output as the first example. The code: ## ...produces the same output as the first example. The code:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## let date = "2012-11-20T22:08:08.398990" ## let date = "2012-11-20T22:08:08.398990"
## let separators = {' ', '-', ':', 'T'} ## let separators = {' ', '-', ':', 'T'}
## for number in split(date, separators): ## for number in split(date, separators):
@ -258,7 +258,7 @@ iterator split*(s: string, sep: char): string =
## characters, this proc will not coalesce groups of the ## characters, this proc will not coalesce groups of the
## separator, returning a string for each found character. The code: ## separator, returning a string for each found character. The code:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## for word in split(";;this;is;an;;example;;;", ';'): ## for word in split(";;this;is;an;;example;;;", ';'):
## writeln(stdout, word) ## writeln(stdout, word)
## ##
@ -308,13 +308,13 @@ iterator splitLines*(s: string): string =
## ##
## Example: ## Example:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## for line in splitLines("\nthis\nis\nan\n\nexample\n"): ## for line in splitLines("\nthis\nis\nan\n\nexample\n"):
## writeln(stdout, line) ## writeln(stdout, line)
## ##
## Results in: ## Results in:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## "" ## ""
## "this" ## "this"
## "is" ## "is"
@ -417,7 +417,7 @@ proc parseInt*(s: string): int {.noSideEffect, procvar,
rtl, extern: "nsuParseInt".} = rtl, extern: "nsuParseInt".} =
## Parses a decimal integer value contained in `s`. ## Parses a decimal integer value contained in `s`.
## ##
## If `s` is not a valid integer, `EInvalidValue` is raised. ## If `s` is not a valid integer, `ValueError` is raised.
var L = parseutils.parseInt(s, result, 0) var L = parseutils.parseInt(s, result, 0)
if L != s.len or L == 0: if L != s.len or L == 0:
raise newException(ValueError, "invalid integer: " & s) raise newException(ValueError, "invalid integer: " & s)
@ -426,7 +426,7 @@ proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect, procvar,
rtl, extern: "nsuParseBiggestInt".} = rtl, extern: "nsuParseBiggestInt".} =
## Parses a decimal integer value contained in `s`. ## Parses a decimal integer value contained in `s`.
## ##
## If `s` is not a valid integer, `EInvalidValue` is raised. ## If `s` is not a valid integer, `ValueError` is raised.
var L = parseutils.parseBiggestInt(s, result, 0) var L = parseutils.parseBiggestInt(s, result, 0)
if L != s.len or L == 0: if L != s.len or L == 0:
raise newException(ValueError, "invalid integer: " & s) raise newException(ValueError, "invalid integer: " & s)
@ -434,7 +434,7 @@ proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect, procvar,
proc parseFloat*(s: string): float {.noSideEffect, procvar, proc parseFloat*(s: string): float {.noSideEffect, procvar,
rtl, extern: "nsuParseFloat".} = rtl, extern: "nsuParseFloat".} =
## Parses a decimal floating point value contained in `s`. If `s` is not ## Parses a decimal floating point value contained in `s`. If `s` is not
## a valid floating point number, `EInvalidValue` is raised. ``NAN``, ## a valid floating point number, `ValueError` is raised. ``NAN``,
## ``INF``, ``-INF`` are also supported (case insensitive comparison). ## ``INF``, ``-INF`` are also supported (case insensitive comparison).
var L = parseutils.parseFloat(s, result, 0) var L = parseutils.parseFloat(s, result, 0)
if L != s.len or L == 0: if L != s.len or L == 0:
@ -444,7 +444,7 @@ proc parseHexInt*(s: string): int {.noSideEffect, procvar,
rtl, extern: "nsuParseHexInt".} = rtl, extern: "nsuParseHexInt".} =
## Parses a hexadecimal integer value contained in `s`. ## Parses a hexadecimal integer value contained in `s`.
## ##
## If `s` is not a valid integer, `EInvalidValue` is raised. `s` can have one ## If `s` is not a valid integer, `ValueError` is raised. `s` can have one
## of the following optional prefixes: ``0x``, ``0X``, ``#``. Underscores ## of the following optional prefixes: ``0x``, ``0X``, ``#``. Underscores
## within `s` are ignored. ## within `s` are ignored.
var i = 0 var i = 0
@ -471,7 +471,7 @@ proc parseBool*(s: string): bool =
## If ``s`` is one of the following values: ``y, yes, true, 1, on``, then ## If ``s`` is one of the following values: ``y, yes, true, 1, on``, then
## returns `true`. If ``s`` is one of the following values: ``n, no, false, ## returns `true`. If ``s`` is one of the following values: ``n, no, false,
## 0, off``, then returns `false`. If ``s`` is something else a ## 0, off``, then returns `false`. If ``s`` is something else a
## ``EInvalidValue`` exception is raised. ## ``ValueError`` exception is raised.
case normalize(s) case normalize(s)
of "y", "yes", "true", "1", "on": result = true of "y", "yes", "true", "1", "on": result = true
of "n", "no", "false", "0", "off": result = false of "n", "no", "false", "0", "off": result = false
@ -480,7 +480,7 @@ proc parseBool*(s: string): bool =
proc parseEnum*[T: enum](s: string): T = proc parseEnum*[T: enum](s: string): T =
## Parses an enum ``T``. ## Parses an enum ``T``.
## ##
## Raises ``EInvalidValue`` for an invalid value in `s`. The comparison is ## Raises ``ValueError`` for an invalid value in `s`. The comparison is
## done in a style insensitive way. ## done in a style insensitive way.
for e in low(T)..high(T): for e in low(T)..high(T):
if cmpIgnoreStyle(s, $e) == 0: if cmpIgnoreStyle(s, $e) == 0:
@ -502,7 +502,7 @@ proc repeatChar*(count: int, c: char = ' '): string {.noSideEffect,
## Returns a string of length `count` consisting only of ## Returns a string of length `count` consisting only of
## the character `c`. You can use this proc to left align strings. Example: ## the character `c`. You can use this proc to left align strings. Example:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## let ## let
## width = 15 ## width = 15
## text1 = "Hello user!" ## text1 = "Hello user!"
@ -527,7 +527,7 @@ proc align*(s: string, count: int, padding = ' '): string {.
## returned unchanged. If you need to left align a string use the `repeatChar ## returned unchanged. If you need to left align a string use the `repeatChar
## proc <#repeatChar>`_. Example: ## proc <#repeatChar>`_. Example:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## assert align("abc", 4) == " abc" ## assert align("abc", 4) == " abc"
## assert align("a", 0) == "a" ## assert align("a", 0) == "a"
## assert align("1232", 6) == " 1232" ## assert align("1232", 6) == " 1232"
@ -547,13 +547,13 @@ iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[
## Substrings are separated by a substring containing only `seps`. ## Substrings are separated by a substring containing only `seps`.
## Examples: ## Examples:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## for word in tokenize(" this is an example "): ## for word in tokenize(" this is an example "):
## writeln(stdout, word) ## writeln(stdout, word)
## ##
## Results in: ## Results in:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## (" ", true) ## (" ", true)
## ("this", false) ## ("this", false)
## (" ", true) ## (" ", true)
@ -676,7 +676,7 @@ proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect,
## ##
## A shorthand for: ## A shorthand for:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## if dest.len > startLen: add(dest, sep) ## if dest.len > startLen: add(dest, sep)
## ##
## This is often useful for generating some code where the items need to ## This is often useful for generating some code where the items need to
@ -684,7 +684,7 @@ proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect,
## `startLen`. The following example creates a string describing ## `startLen`. The following example creates a string describing
## an array of integers: ## an array of integers:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## var arr = "[" ## var arr = "["
## for x in items([2, 3, 5, 7, 11]): ## for x in items([2, 3, 5, 7, 11]):
## addSep(arr, startLen=len("[")) ## addSep(arr, startLen=len("["))
@ -692,7 +692,7 @@ proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect,
## add(arr, "]") ## add(arr, "]")
if dest.len > startLen: add(dest, sep) if dest.len > startLen: add(dest, sep)
proc allCharsInSet*(s: string, theSet: TCharSet): bool = proc allCharsInSet*(s: string, theSet: set[char]): bool =
## Returns true iff each character of `s` is in the set `theSet`. ## Returns true iff each character of `s` is in the set `theSet`.
for c in items(s): for c in items(s):
if c notin theSet: return false if c notin theSet: return false
@ -739,14 +739,14 @@ proc join*(a: openArray[string]): string {.
result = "" result = ""
type type
TSkipTable = array[char, int] SkipTable = array[char, int]
proc preprocessSub(sub: string, a: var TSkipTable) = proc preprocessSub(sub: string, a: var SkipTable) =
var m = len(sub) var m = len(sub)
for i in 0..0xff: a[chr(i)] = m+1 for i in 0..0xff: a[chr(i)] = m+1
for i in 0..m-1: a[sub[i]] = m-i for i in 0..m-1: a[sub[i]] = m-i
proc findAux(s, sub: string, start: int, a: TSkipTable): int = proc findAux(s, sub: string, start: int, a: SkipTable): int =
# Fast "quick search" algorithm: # Fast "quick search" algorithm:
var var
m = len(sub) m = len(sub)
@ -762,7 +762,7 @@ proc findAux(s, sub: string, start: int, a: TSkipTable): int =
return -1 return -1
proc find*(s, sub: string, start: int = 0): int {.noSideEffect, proc find*(s, sub: string, start: int = 0): int {.noSideEffect,
rtl, extern: "nsuFindStr", operator: 6.} = rtl, extern: "nsuFindStr".} =
## Searches for `sub` in `s` starting at position `start`. ## Searches for `sub` in `s` starting at position `start`.
## ##
## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
@ -827,9 +827,9 @@ proc contains*(s: string, chars: set[char]): bool {.noSideEffect.} =
return find(s, chars) >= 0 return find(s, chars) >= 0
proc replace*(s, sub: string, by = ""): string {.noSideEffect, proc replace*(s, sub: string, by = ""): string {.noSideEffect,
rtl, extern: "nsuReplaceStr", operator: 1.} = rtl, extern: "nsuReplaceStr".} =
## Replaces `sub` in `s` by the string `by`. ## Replaces `sub` in `s` by the string `by`.
var a {.noinit.}: TSkipTable var a {.noinit.}: SkipTable
result = "" result = ""
preprocessSub(sub, a) preprocessSub(sub, a)
var i = 0 var i = 0
@ -862,7 +862,7 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
## (comparable to ``\\w`` in regular expressions), otherwise it is not ## (comparable to ``\\w`` in regular expressions), otherwise it is not
## replaced. ## replaced.
const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'} const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
var a {.noinit.}: TSkipTable var a {.noinit.}: SkipTable
result = "" result = ""
preprocessSub(sub, a) preprocessSub(sub, a)
var i = 0 var i = 0
@ -899,7 +899,7 @@ proc parseOctInt*(s: string): int {.noSideEffect,
rtl, extern: "nsuParseOctInt".} = rtl, extern: "nsuParseOctInt".} =
## Parses an octal integer value contained in `s`. ## Parses an octal integer value contained in `s`.
## ##
## If `s` is not a valid integer, `EInvalidValue` is raised. `s` can have one ## If `s` is not a valid integer, `ValueError` is raised. `s` can have one
## of the following optional prefixes: ``0o``, ``0O``. Underscores within ## of the following optional prefixes: ``0o``, ``0O``. Underscores within
## `s` are ignored. ## `s` are ignored.
var i = 0 var i = 0
@ -999,7 +999,7 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
## operations. ## operations.
## ##
## If `s` does not begin with ``prefix`` and end with ``suffix`` a ## If `s` does not begin with ``prefix`` and end with ``suffix`` a
## EInvalidValue exception will be raised. ## ValueError exception will be raised.
result = newStringOfCap(s.len) result = newStringOfCap(s.len)
var i = 0 var i = 0
if s[0 .. prefix.len-1] != prefix: if s[0 .. prefix.len-1] != prefix:
@ -1138,12 +1138,14 @@ proc c_sprintf(buf, frmt: cstring) {.header: "<stdio.h>", importc: "sprintf",
varargs, noSideEffect.} varargs, noSideEffect.}
type type
TFloatFormat* = enum ## the different modes of floating point formating FloatFormatMode* = enum ## the different modes of floating point formating
ffDefault, ## use the shorter floating point notation ffDefault, ## use the shorter floating point notation
ffDecimal, ## use decimal floating point notation ffDecimal, ## use decimal floating point notation
ffScientific ## use scientific notation (using ``e`` character) ffScientific ## use scientific notation (using ``e`` character)
proc formatBiggestFloat*(f: BiggestFloat, format: TFloatFormat = ffDefault, {.deprecated: [TFloatFormat: FloatFormatMode].}
proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
precision: range[0..32] = 16): string {. precision: range[0..32] = 16): string {.
noSideEffect, operator: 2, rtl, extern: "nsu$1".} = noSideEffect, operator: 2, rtl, extern: "nsu$1".} =
## Converts a floating point value `f` to a string. ## Converts a floating point value `f` to a string.
@ -1174,7 +1176,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: TFloatFormat = ffDefault,
c_sprintf(buf, frmtstr, f) c_sprintf(buf, frmtstr, f)
result = $buf result = $buf
proc formatFloat*(f: float, format: TFloatFormat = ffDefault, proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
precision: range[0..32] = 16): string {. precision: range[0..32] = 16): string {.
noSideEffect, operator: 2, rtl, extern: "nsu$1".} = noSideEffect, operator: 2, rtl, extern: "nsu$1".} =
## Converts a floating point value `f` to a string. ## Converts a floating point value `f` to a string.
@ -1190,7 +1192,7 @@ proc formatFloat*(f: float, format: TFloatFormat = ffDefault,
proc formatSize*(bytes: BiggestInt, decimalSep = '.'): string = proc formatSize*(bytes: BiggestInt, decimalSep = '.'): string =
## Rounds and formats `bytes`. Examples: ## Rounds and formats `bytes`. Examples:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## ##
## formatSize(1'i64 shl 31 + 300'i64) == "2.204GB" ## formatSize(1'i64 shl 31 + 300'i64) == "2.204GB"
## formatSize(4096) == "4KB" ## formatSize(4096) == "4KB"
@ -1280,12 +1282,12 @@ proc `%` *(formatstr: string, a: openArray[string]): string {.noSideEffect,
## ##
## This is best explained by an example: ## This is best explained by an example:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## "$1 eats $2." % ["The cat", "fish"] ## "$1 eats $2." % ["The cat", "fish"]
## ##
## Results in: ## Results in:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## "The cat eats fish." ## "The cat eats fish."
## ##
## The substitution variables (the thing after the ``$``) are enumerated ## The substitution variables (the thing after the ``$``) are enumerated
@ -1294,7 +1296,7 @@ proc `%` *(formatstr: string, a: openArray[string]): string {.noSideEffect,
## The notation ``$#`` can be used to refer to the next substitution ## The notation ``$#`` can be used to refer to the next substitution
## variable: ## variable:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## "$# eats $#." % ["The cat", "fish"] ## "$# eats $#." % ["The cat", "fish"]
## ##
## Substitution variables can also be words (that is ## Substitution variables can also be words (that is
@ -1302,15 +1304,15 @@ proc `%` *(formatstr: string, a: openArray[string]): string {.noSideEffect,
## indices are keys and with odd indices are the corresponding values. ## indices are keys and with odd indices are the corresponding values.
## An example: ## An example:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## "$animal eats $food." % ["animal", "The cat", "food", "fish"] ## "$animal eats $food." % ["animal", "The cat", "food", "fish"]
## ##
## Results in: ## Results in:
## ##
## .. code-block:: nimrod ## .. code-block:: nim
## "The cat eats fish." ## "The cat eats fish."
## ##
## The variables are compared with `cmpIgnoreStyle`. `EInvalidValue` is ## The variables are compared with `cmpIgnoreStyle`. `ValueError` is
## raised if an ill-formed format string has been passed to the `%` operator. ## raised if an ill-formed format string has been passed to the `%` operator.
result = newStringOfCap(formatstr.len + a.len shl 4) result = newStringOfCap(formatstr.len + a.len shl 4)
addf(result, formatstr, a) addf(result, formatstr, a)
@ -1350,7 +1352,7 @@ when isMainModule:
doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz " doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz "
doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc" doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc"
type TMyEnum = enum enA, enB, enC, enuD, enE type MyEnum = enum enA, enB, enC, enuD, enE
doAssert parseEnum[TMyEnum]("enu_D") == enuD doAssert parseEnum[MyEnum]("enu_D") == enuD
doAssert parseEnum("invalid enum value", enC) == enC doAssert parseEnum("invalid enum value", enC) == enC

View file

@ -1,13 +1,13 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 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.
# #
## Nimrod support for `substitution expressions`:idx: (`subex`:idx:). ## Nim support for `substitution expressions`:idx: (`subex`:idx:).
## ##
## .. include:: ../doc/subexes.txt ## .. include:: ../doc/subexes.txt
## ##
@ -28,11 +28,13 @@ proc findNormalized(x: string, inArray: openarray[string]): int =
return -1 return -1
type type
EInvalidSubex* = object of EInvalidValue ## exception that is raised for SubexError* = object of ValueError ## exception that is raised for
## an invalid subex ## an invalid subex
{.deprecated: [EInvalidSubex: SubexError].}
proc raiseInvalidFormat(msg: string) {.noinline.} = proc raiseInvalidFormat(msg: string) {.noinline.} =
raise newException(EInvalidSubex, "invalid format string: " & msg) raise newException(SubexError, "invalid format string: " & msg)
type type
TFormatParser = object {.pure, final.} TFormatParser = object {.pure, final.}
@ -291,14 +293,16 @@ proc scanDollar(p: var TFormatParser, a: openarray[string], s: var string) =
type type
TSubex* = distinct string ## string that contains a substitution expression Subex* = distinct string ## string that contains a substitution expression
proc subex*(s: string): TSubex = {.deprecated: [TSubex: Subex].}
proc subex*(s: string): Subex =
## constructs a *substitution expression* from `s`. Currently this performs ## constructs a *substitution expression* from `s`. Currently this performs
## no syntax checking but this may change in later versions. ## no syntax checking but this may change in later versions.
result = TSubex(s) result = Subex(s)
proc addf*(s: var string, formatstr: TSubex, a: varargs[string, `$`]) {. proc addf*(s: var string, formatstr: Subex, a: varargs[string, `$`]) {.
noSideEffect, rtl, extern: "nfrmtAddf".} = noSideEffect, rtl, extern: "nfrmtAddf".} =
## The same as ``add(s, formatstr % a)``, but more efficient. ## The same as ``add(s, formatstr % a)``, but more efficient.
var p: TFormatParser var p: TFormatParser
@ -312,7 +316,7 @@ proc addf*(s: var string, formatstr: TSubex, a: varargs[string, `$`]) {.
emitChar(p, s, p.f[i]) emitChar(p, s, p.f[i])
inc(i) inc(i)
proc `%` *(formatstr: TSubex, a: openarray[string]): string {.noSideEffect, proc `%` *(formatstr: Subex, a: openarray[string]): string {.noSideEffect,
rtl, extern: "nfrmtFormatOpenArray".} = rtl, extern: "nfrmtFormatOpenArray".} =
## The `substitution`:idx: operator performs string substitutions in ## The `substitution`:idx: operator performs string substitutions in
## `formatstr` and returns a modified `formatstr`. This is often called ## `formatstr` and returns a modified `formatstr`. This is often called
@ -321,13 +325,13 @@ proc `%` *(formatstr: TSubex, a: openarray[string]): string {.noSideEffect,
result = newStringOfCap(formatstr.string.len + a.len shl 4) result = newStringOfCap(formatstr.string.len + a.len shl 4)
addf(result, formatstr, a) addf(result, formatstr, a)
proc `%` *(formatstr: TSubex, a: string): string {.noSideEffect, proc `%` *(formatstr: Subex, a: string): string {.noSideEffect,
rtl, extern: "nfrmtFormatSingleElem".} = rtl, extern: "nfrmtFormatSingleElem".} =
## This is the same as ``formatstr % [a]``. ## This is the same as ``formatstr % [a]``.
result = newStringOfCap(formatstr.string.len + a.len) result = newStringOfCap(formatstr.string.len + a.len)
addf(result, formatstr, [a]) addf(result, formatstr, [a])
proc format*(formatstr: TSubex, a: varargs[string, `$`]): string {.noSideEffect, proc format*(formatstr: Subex, a: varargs[string, `$`]): string {.noSideEffect,
rtl, extern: "nfrmtFormatVarargs".} = rtl, extern: "nfrmtFormatVarargs".} =
## The `substitution`:idx: operator performs string substitutions in ## The `substitution`:idx: operator performs string substitutions in
## `formatstr` and returns a modified `formatstr`. This is often called ## `formatstr` and returns a modified `formatstr`. This is often called

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -27,11 +27,11 @@ when defined(windows):
var hTemp = GetStdHandle(STD_OUTPUT_HANDLE) var hTemp = GetStdHandle(STD_OUTPUT_HANDLE)
if DuplicateHandle(GetCurrentProcess(), hTemp, GetCurrentProcess(), if DuplicateHandle(GetCurrentProcess(), hTemp, GetCurrentProcess(),
addr(conHandle), 0, 1, DUPLICATE_SAME_ACCESS) == 0: addr(conHandle), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
osError(osLastError()) raiseOSError(osLastError())
proc getCursorPos(): tuple [x,y: int] = proc getCursorPos(): tuple [x,y: int] =
var c: TCONSOLESCREENBUFFERINFO var c: TCONSOLESCREENBUFFERINFO
if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0: osError(osLastError()) if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0: raiseOSError(osLastError())
return (int(c.dwCursorPosition.x), int(c.dwCursorPosition.y)) return (int(c.dwCursorPosition.x), int(c.dwCursorPosition.y))
proc getAttributes(): int16 = proc getAttributes(): int16 =
@ -51,7 +51,7 @@ proc setCursorPos*(x, y: int) =
var c: TCOORD var c: TCOORD
c.x = int16(x) c.x = int16(x)
c.y = int16(y) c.y = int16(y)
if SetConsoleCursorPosition(conHandle, c) == 0: osError(osLastError()) if SetConsoleCursorPosition(conHandle, c) == 0: raiseOSError(osLastError())
else: else:
stdout.write("\e[" & $y & ';' & $x & 'f') stdout.write("\e[" & $y & ';' & $x & 'f')
@ -61,10 +61,10 @@ proc setCursorXPos*(x: int) =
when defined(windows): when defined(windows):
var scrbuf: TCONSOLESCREENBUFFERINFO var scrbuf: TCONSOLESCREENBUFFERINFO
var hStdout = conHandle var hStdout = conHandle
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: osError(osLastError()) if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: raiseOSError(osLastError())
var origin = scrbuf.dwCursorPosition var origin = scrbuf.dwCursorPosition
origin.x = int16(x) origin.x = int16(x)
if SetConsoleCursorPosition(conHandle, origin) == 0: osError(osLastError()) if SetConsoleCursorPosition(conHandle, origin) == 0: raiseOSError(osLastError())
else: else:
stdout.write("\e[" & $x & 'G') stdout.write("\e[" & $x & 'G')
@ -75,10 +75,10 @@ when defined(windows):
when defined(windows): when defined(windows):
var scrbuf: TCONSOLESCREENBUFFERINFO var scrbuf: TCONSOLESCREENBUFFERINFO
var hStdout = conHandle var hStdout = conHandle
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: osError(osLastError()) if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: raiseOSError(osLastError())
var origin = scrbuf.dwCursorPosition var origin = scrbuf.dwCursorPosition
origin.y = int16(y) origin.y = int16(y)
if SetConsoleCursorPosition(conHandle, origin) == 0: osError(osLastError()) if SetConsoleCursorPosition(conHandle, origin) == 0: raiseOSError(osLastError())
else: else:
discard discard
@ -155,18 +155,18 @@ proc eraseLine* =
var scrbuf: TCONSOLESCREENBUFFERINFO var scrbuf: TCONSOLESCREENBUFFERINFO
var numwrote: DWORD var numwrote: DWORD
var hStdout = conHandle var hStdout = conHandle
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: osError(osLastError()) if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: raiseOSError(osLastError())
var origin = scrbuf.dwCursorPosition var origin = scrbuf.dwCursorPosition
origin.x = 0'i16 origin.x = 0'i16
if SetConsoleCursorPosition(conHandle, origin) == 0: osError(osLastError()) if SetConsoleCursorPosition(conHandle, origin) == 0: raiseOSError(osLastError())
var ht = scrbuf.dwSize.Y - origin.Y var ht = scrbuf.dwSize.Y - origin.Y
var wt = scrbuf.dwSize.X - origin.X var wt = scrbuf.dwSize.X - origin.X
if FillConsoleOutputCharacter(hStdout,' ', ht*wt, if FillConsoleOutputCharacter(hStdout,' ', ht*wt,
origin, addr(numwrote)) == 0: origin, addr(numwrote)) == 0:
osError(osLastError()) raiseOSError(osLastError())
if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, ht * wt, if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, ht * wt,
scrbuf.dwCursorPosition, addr(numwrote)) == 0: scrbuf.dwCursorPosition, addr(numwrote)) == 0:
osError(osLastError()) raiseOSError(osLastError())
else: else:
stdout.write("\e[2K") stdout.write("\e[2K")
setCursorXPos(0) setCursorXPos(0)
@ -178,14 +178,14 @@ proc eraseScreen* =
var numwrote: DWORD var numwrote: DWORD
var origin: TCOORD # is inititalized to 0, 0 var origin: TCOORD # is inititalized to 0, 0
var hStdout = conHandle var hStdout = conHandle
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: osError(osLastError()) if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: raiseOSError(osLastError())
if FillConsoleOutputCharacter(hStdout, ' ', scrbuf.dwSize.X*scrbuf.dwSize.Y, if FillConsoleOutputCharacter(hStdout, ' ', scrbuf.dwSize.X*scrbuf.dwSize.Y,
origin, addr(numwrote)) == 0: origin, addr(numwrote)) == 0:
osError(osLastError()) raiseOSError(osLastError())
if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes,
scrbuf.dwSize.X * scrbuf.dwSize.Y, scrbuf.dwSize.X * scrbuf.dwSize.Y,
origin, addr(numwrote)) == 0: origin, addr(numwrote)) == 0:
osError(osLastError()) raiseOSError(osLastError())
setCursorXPos(0) setCursorXPos(0)
else: else:
stdout.write("\e[2J") stdout.write("\e[2J")
@ -199,7 +199,7 @@ proc resetAttributes* {.noconv.} =
stdout.write("\e[0m") stdout.write("\e[0m")
type type
TStyle* = enum ## different styles for text output Style* = enum ## different styles for text output
styleBright = 1, ## bright text styleBright = 1, ## bright text
styleDim, ## dim text styleDim, ## dim text
styleUnknown, ## unknown styleUnknown, ## unknown
@ -208,6 +208,8 @@ type
styleReverse = 7, ## unknown styleReverse = 7, ## unknown
styleHidden ## hidden text styleHidden ## hidden text
{.deprecated: [TStyle: Style].}
when not defined(windows): when not defined(windows):
var var
# XXX: These better be thread-local # XXX: These better be thread-local
@ -244,7 +246,7 @@ proc writeStyled*(txt: string, style: set[TStyle] = {styleBright}) =
stdout.write("\e[" & $ord(gBG) & 'm') stdout.write("\e[" & $ord(gBG) & 'm')
type type
TForegroundColor* = enum ## terminal's foreground colors ForegroundColor* = enum ## terminal's foreground colors
fgBlack = 30, ## black fgBlack = 30, ## black
fgRed, ## red fgRed, ## red
fgGreen, ## green fgGreen, ## green
@ -254,7 +256,7 @@ type
fgCyan, ## cyan fgCyan, ## cyan
fgWhite ## white fgWhite ## white
TBackgroundColor* = enum ## terminal's background colors BackgroundColor* = enum ## terminal's background colors
bgBlack = 40, ## black bgBlack = 40, ## black
bgRed, ## red bgRed, ## red
bgGreen, ## green bgGreen, ## green
@ -264,13 +266,16 @@ type
bgCyan, ## cyan bgCyan, ## cyan
bgWhite ## white bgWhite ## white
proc setForegroundColor*(fg: TForegroundColor, bright=false) = {.deprecated: [TForegroundColor: ForegroundColor,
TBackgroundColor: BackgroundColor].}
proc setForegroundColor*(fg: ForegroundColor, bright=false) =
## sets the terminal's foreground color ## sets the terminal's foreground color
when defined(windows): when defined(windows):
var old = getAttributes() and not 0x0007 var old = getAttributes() and not 0x0007
if bright: if bright:
old = old or FOREGROUND_INTENSITY old = old or FOREGROUND_INTENSITY
const lookup: array [TForegroundColor, int] = [ const lookup: array [ForegroundColor, int] = [
0, 0,
(FOREGROUND_RED), (FOREGROUND_RED),
(FOREGROUND_GREEN), (FOREGROUND_GREEN),
@ -285,13 +290,13 @@ proc setForegroundColor*(fg: TForegroundColor, bright=false) =
if bright: inc(gFG, 60) if bright: inc(gFG, 60)
stdout.write("\e[" & $gFG & 'm') stdout.write("\e[" & $gFG & 'm')
proc setBackgroundColor*(bg: TBackgroundColor, bright=false) = proc setBackgroundColor*(bg: BackgroundColor, bright=false) =
## sets the terminal's background color ## sets the terminal's background color
when defined(windows): when defined(windows):
var old = getAttributes() and not 0x0070 var old = getAttributes() and not 0x0070
if bright: if bright:
old = old or BACKGROUND_INTENSITY old = old or BACKGROUND_INTENSITY
const lookup: array [TBackgroundColor, int] = [ const lookup: array [BackgroundColor, int] = [
0, 0,
(BACKGROUND_RED), (BACKGROUND_RED),
(BACKGROUND_GREEN), (BACKGROUND_GREEN),
@ -306,22 +311,22 @@ proc setBackgroundColor*(bg: TBackgroundColor, bright=false) =
if bright: inc(gBG, 60) if bright: inc(gBG, 60)
stdout.write("\e[" & $gBG & 'm') stdout.write("\e[" & $gBG & 'm')
proc isatty*(f: TFile): bool = proc isatty*(f: File): bool =
## returns true if `f` is associated with a terminal device. ## returns true if `f` is associated with a terminal device.
when defined(posix): when defined(posix):
proc isatty(fildes: TFileHandle): cint {. proc isatty(fildes: FileHandle): cint {.
importc: "isatty", header: "<unistd.h>".} importc: "isatty", header: "<unistd.h>".}
else: else:
proc isatty(fildes: TFileHandle): cint {. proc isatty(fildes: FileHandle): cint {.
importc: "_isatty", header: "<io.h>".} importc: "_isatty", header: "<io.h>".}
result = isatty(fileHandle(f)) != 0'i32 result = isatty(getFileHandle(f)) != 0'i32
proc styledEchoProcessArg(s: string) = write stdout, s proc styledEchoProcessArg(s: string) = write stdout, s
proc styledEchoProcessArg(style: TStyle) = setStyle({style}) proc styledEchoProcessArg(style: Style) = setStyle({style})
proc styledEchoProcessArg(style: set[TStyle]) = setStyle style proc styledEchoProcessArg(style: set[Style]) = setStyle style
proc styledEchoProcessArg(color: TForegroundColor) = setForegroundColor color proc styledEchoProcessArg(color: ForegroundColor) = setForegroundColor color
proc styledEchoProcessArg(color: TBackgroundColor) = setBackgroundColor color proc styledEchoProcessArg(color: BackgroundColor) = setBackgroundColor color
macro styledEcho*(m: varargs[expr]): stmt = macro styledEcho*(m: varargs[expr]): stmt =
## to be documented. ## to be documented.

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2013 Andreas Rumpf # (c) Copyright 2013 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -21,9 +21,9 @@ import
include "system/inclrtl" include "system/inclrtl"
type type
TMonth* = enum ## represents a month Month* = enum ## represents a month
mJan, mFeb, mMar, mApr, mMay, mJun, mJul, mAug, mSep, mOct, mNov, mDec mJan, mFeb, mMar, mApr, mMay, mJun, mJul, mAug, mSep, mOct, mNov, mDec
TWeekDay* = enum ## represents a weekday WeekDay* = enum ## represents a weekday
dMon, dTue, dWed, dThu, dFri, dSat, dSun dMon, dTue, dWed, dThu, dFri, dSat, dSun
var var
@ -32,12 +32,12 @@ var
when defined(posix) and not defined(JS): when defined(posix) and not defined(JS):
type type
TTimeImpl {.importc: "time_t", header: "<sys/time.h>".} = int TimeImpl {.importc: "time_t", header: "<sys/time.h>".} = int
TTime* = distinct TTimeImpl ## distinct type that represents a time Time* = distinct TimeImpl ## distinct type that represents a time
## measured as number of seconds since the epoch ## measured as number of seconds since the epoch
Ttimeval {.importc: "struct timeval", header: "<sys/select.h>", Timeval {.importc: "struct timeval",
final, pure.} = object ## struct timeval header: "<sys/select.h>".} = object ## struct timeval
tv_sec: int ## Seconds. tv_sec: int ## Seconds.
tv_usec: int ## Microseconds. tv_usec: int ## Microseconds.
@ -45,7 +45,7 @@ when defined(posix) and not defined(JS):
# Ok, we could, but I don't want circular dependencies. # Ok, we could, but I don't want circular dependencies.
# And gettimeofday() is not defined in the posix module anyway. Sigh. # And gettimeofday() is not defined in the posix module anyway. Sigh.
proc posix_gettimeofday(tp: var Ttimeval, unused: pointer = nil) {. proc posix_gettimeofday(tp: var Timeval, unused: pointer = nil) {.
importc: "gettimeofday", header: "<sys/time.h>".} importc: "gettimeofday", header: "<sys/time.h>".}
elif defined(windows): elif defined(windows):
@ -53,16 +53,16 @@ elif defined(windows):
when defined(vcc): when defined(vcc):
# newest version of Visual C++ defines time_t to be of 64 bits # newest version of Visual C++ defines time_t to be of 64 bits
type TTimeImpl {.importc: "time_t", header: "<time.h>".} = int64 type TimeImpl {.importc: "time_t", header: "<time.h>".} = int64
else: else:
type TTimeImpl {.importc: "time_t", header: "<time.h>".} = int32 type TimeImpl {.importc: "time_t", header: "<time.h>".} = int32
type type
TTime* = distinct TTimeImpl Time* = distinct TimeImpl
elif defined(JS): elif defined(JS):
type type
TTime* {.final, importc.} = object Time* {.importc.} = object
getDay: proc (): int {.tags: [], raises: [], gcsafe.} getDay: proc (): int {.tags: [], raises: [], gcsafe.}
getFullYear: proc (): int {.tags: [], raises: [], gcsafe.} getFullYear: proc (): int {.tags: [], raises: [], gcsafe.}
getHours: proc (): int {.tags: [], raises: [], gcsafe.} getHours: proc (): int {.tags: [], raises: [], gcsafe.}
@ -82,7 +82,7 @@ elif defined(JS):
getUTCSeconds: proc (): int {.tags: [], raises: [], gcsafe.} getUTCSeconds: proc (): int {.tags: [], raises: [], gcsafe.}
getUTCDay: proc (): int {.tags: [], raises: [], gcsafe.} getUTCDay: proc (): int {.tags: [], raises: [], gcsafe.}
getYear: proc (): int {.tags: [], raises: [], gcsafe.} getYear: proc (): int {.tags: [], raises: [], gcsafe.}
parse: proc (s: cstring): TTime {.tags: [], raises: [], gcsafe.} parse: proc (s: cstring): Time {.tags: [], raises: [], gcsafe.}
setDate: proc (x: int) {.tags: [], raises: [], gcsafe.} setDate: proc (x: int) {.tags: [], raises: [], gcsafe.}
setFullYear: proc (x: int) {.tags: [], raises: [], gcsafe.} setFullYear: proc (x: int) {.tags: [], raises: [], gcsafe.}
setHours: proc (x: int) {.tags: [], raises: [], gcsafe.} setHours: proc (x: int) {.tags: [], raises: [], gcsafe.}
@ -103,7 +103,7 @@ elif defined(JS):
toLocaleString: proc (): cstring {.tags: [], raises: [], gcsafe.} toLocaleString: proc (): cstring {.tags: [], raises: [], gcsafe.}
type type
TTimeInfo* = object of RootObj ## represents a time in different parts TimeInfo* = object of RootObj ## represents a time in different parts
second*: range[0..61] ## The number of seconds after the minute, second*: range[0..61] ## The number of seconds after the minute,
## normally in the range 0 to 59, but can ## normally in the range 0 to 59, but can
## be up to 61 to allow for leap seconds. ## be up to 61 to allow for leap seconds.
@ -112,9 +112,9 @@ type
hour*: range[0..23] ## The number of hours past midnight, hour*: range[0..23] ## The number of hours past midnight,
## in the range 0 to 23. ## in the range 0 to 23.
monthday*: range[1..31] ## The day of the month, in the range 1 to 31. monthday*: range[1..31] ## The day of the month, in the range 1 to 31.
month*: TMonth ## The current month. month*: Month ## The current month.
year*: range[-10_000..10_000] ## The current year. year*: range[-10_000..10_000] ## The current year.
weekday*: TWeekDay ## The current day of the week. weekday*: WeekDay ## The current day of the week.
yearday*: range[0..365] ## The number of days since January 1, yearday*: range[0..365] ## The number of days since January 1,
## in the range 0 to 365. ## in the range 0 to 365.
## Always 0 if the target is JS. ## Always 0 if the target is JS.
@ -127,7 +127,7 @@ type
## I make some assumptions about the data in here. Either ## I make some assumptions about the data in here. Either
## everything should be positive or everything negative. Zero is ## everything should be positive or everything negative. Zero is
## fine too. Mixed signs will lead to unexpected results. ## fine too. Mixed signs will lead to unexpected results.
TTimeInterval* {.pure.} = object ## a time interval TimeInterval* = object ## a time interval
miliseconds*: int ## The number of miliseconds miliseconds*: int ## The number of miliseconds
seconds*: int ## The number of seconds seconds*: int ## The number of seconds
minutes*: int ## The number of minutes minutes*: int ## The number of minutes
@ -136,55 +136,58 @@ type
months*: int ## The number of months months*: int ## The number of months
years*: int ## The number of years years*: int ## The number of years
proc getTime*(): TTime {.tags: [TimeEffect], gcsafe.} {.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time, TTimeval: Timeval,
TTimeInterval: TimeInterval, TTimeInfo: TimeInfo].}
proc getTime*(): Time {.tags: [TimeEffect], gcsafe.}
## gets the current calendar time as a UNIX epoch value (number of seconds ## gets the current calendar time as a UNIX epoch value (number of seconds
## elapsed since 1970) with integer precission. Use epochTime for higher ## elapsed since 1970) with integer precission. Use epochTime for higher
## resolution. ## resolution.
proc getLocalTime*(t: TTime): TTimeInfo {.tags: [TimeEffect], raises: [], gcsafe.} proc getLocalTime*(t: Time): TimeInfo {.tags: [TimeEffect], raises: [], gcsafe.}
## converts the calendar time `t` to broken-time representation, ## converts the calendar time `t` to broken-time representation,
## expressed relative to the user's specified time zone. ## expressed relative to the user's specified time zone.
proc getGMTime*(t: TTime): TTimeInfo {.tags: [TimeEffect], raises: [], gcsafe.} proc getGMTime*(t: Time): TimeInfo {.tags: [TimeEffect], raises: [], gcsafe.}
## converts the calendar time `t` to broken-down time representation, ## converts the calendar time `t` to broken-down time representation,
## expressed in Coordinated Universal Time (UTC). ## expressed in Coordinated Universal Time (UTC).
proc timeInfoToTime*(timeInfo: TTimeInfo): TTime {.tags: [], gcsafe.} proc timeInfoToTime*(timeInfo: TimeInfo): Time {.tags: [], gcsafe.}
## converts a broken-down time structure to ## converts a broken-down time structure to
## calendar time representation. The function ignores the specified ## calendar time representation. The function ignores the specified
## contents of the structure members `weekday` and `yearday` and recomputes ## contents of the structure members `weekday` and `yearday` and recomputes
## them from the other information in the broken-down time structure. ## them from the other information in the broken-down time structure.
proc fromSeconds*(since1970: float): TTime {.tags: [], raises: [], gcsafe.} proc fromSeconds*(since1970: float): Time {.tags: [], raises: [], gcsafe.}
## Takes a float which contains the number of seconds since the unix epoch and ## Takes a float which contains the number of seconds since the unix epoch and
## returns a time object. ## returns a time object.
proc fromSeconds*(since1970: int64): TTime {.tags: [], raises: [], gcsafe.} = proc fromSeconds*(since1970: int64): Time {.tags: [], raises: [], gcsafe.} =
## Takes an int which contains the number of seconds since the unix epoch and ## Takes an int which contains the number of seconds since the unix epoch and
## returns a time object. ## returns a time object.
fromSeconds(float(since1970)) fromSeconds(float(since1970))
proc toSeconds*(time: TTime): float {.tags: [], raises: [], gcsafe.} proc toSeconds*(time: Time): float {.tags: [], raises: [], gcsafe.}
## Returns the time in seconds since the unix epoch. ## Returns the time in seconds since the unix epoch.
proc `$` *(timeInfo: TTimeInfo): string {.tags: [], raises: [], gcsafe.} proc `$` *(timeInfo: TimeInfo): string {.tags: [], raises: [], gcsafe.}
## converts a `TTimeInfo` object to a string representation. ## converts a `TimeInfo` object to a string representation.
proc `$` *(time: TTime): string {.tags: [], raises: [], gcsafe.} proc `$` *(time: Time): string {.tags: [], raises: [], gcsafe.}
## converts a calendar time to a string representation. ## converts a calendar time to a string representation.
proc `-`*(a, b: TTime): int64 {. proc `-`*(a, b: Time): int64 {.
rtl, extern: "ntDiffTime", tags: [], raises: [].} rtl, extern: "ntDiffTime", tags: [], raises: [].}
## computes the difference of two calendar times. Result is in seconds. ## computes the difference of two calendar times. Result is in seconds.
proc `<`*(a, b: TTime): bool {. proc `<`*(a, b: Time): bool {.
rtl, extern: "ntLtTime", tags: [], raises: [].} = rtl, extern: "ntLtTime", tags: [], raises: [].} =
## returns true iff ``a < b``, that is iff a happened before b. ## returns true iff ``a < b``, that is iff a happened before b.
result = a - b < 0 result = a - b < 0
proc `<=` * (a, b: TTime): bool {. proc `<=` * (a, b: Time): bool {.
rtl, extern: "ntLeTime", tags: [], raises: [].}= rtl, extern: "ntLeTime", tags: [], raises: [].}=
## returns true iff ``a <= b``. ## returns true iff ``a <= b``.
result = a - b <= 0 result = a - b <= 0
proc `==`*(a, b: TTime): bool {. proc `==`*(a, b: Time): bool {.
rtl, extern: "ntEqTime", tags: [], raises: [].} = rtl, extern: "ntEqTime", tags: [], raises: [].} =
## returns true if ``a == b``, that is if both times represent the same value ## returns true if ``a == b``, that is if both times represent the same value
result = a - b == 0 result = a - b == 0
@ -203,8 +206,8 @@ proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], gcsafe.}
## version 0.8.10.** Use ``epochTime`` or ``cpuTime`` instead. ## version 0.8.10.** Use ``epochTime`` or ``cpuTime`` instead.
proc initInterval*(miliseconds, seconds, minutes, hours, days, months, proc initInterval*(miliseconds, seconds, minutes, hours, days, months,
years: int = 0): TTimeInterval = years: int = 0): TimeInterval =
## creates a new ``TTimeInterval``. ## creates a new ``TimeInterval``.
result.miliseconds = miliseconds result.miliseconds = miliseconds
result.seconds = seconds result.seconds = seconds
result.minutes = minutes result.minutes = minutes
@ -234,7 +237,7 @@ proc getDaysInMonth*(month: TMonth, year: int): int =
of mApr, mJun, mSep, mNov: result = 30 of mApr, mJun, mSep, mNov: result = 30
else: result = 31 else: result = 31
proc toSeconds(a: TTimeInfo, interval: TTimeInterval): float = proc toSeconds(a: TimeInfo, interval: TimeInterval): float =
## Calculates how many seconds the interval is worth by adding up ## Calculates how many seconds the interval is worth by adding up
## all the fields ## all the fields
@ -257,7 +260,7 @@ proc toSeconds(a: TTimeInfo, interval: TTimeInterval): float =
result += float(newinterv.seconds) result += float(newinterv.seconds)
result += newinterv.miliseconds / 1000 result += newinterv.miliseconds / 1000
proc `+`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo = proc `+`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
## adds ``interval`` time. ## adds ``interval`` time.
## ##
## **Note:** This has been only briefly tested and it may not be ## **Note:** This has been only briefly tested and it may not be
@ -269,7 +272,7 @@ proc `+`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo =
else: else:
result = getLocalTime(fromSeconds(t + secs)) result = getLocalTime(fromSeconds(t + secs))
proc `-`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo = proc `-`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
## subtracts ``interval`` time. ## subtracts ``interval`` time.
## ##
## **Note:** This has been only briefly tested, it is inaccurate especially ## **Note:** This has been only briefly tested, it is inaccurate especially
@ -314,38 +317,36 @@ when not defined(JS):
yearday {.importc: "tm_yday".}, yearday {.importc: "tm_yday".},
isdst {.importc: "tm_isdst".}: cint isdst {.importc: "tm_isdst".}: cint
PTimeInfo = ptr StructTM TimeInfoPtr = ptr StructTM
PTime = ptr TTime Clock {.importc: "clock_t".} = distinct int
TClock {.importc: "clock_t".} = distinct int proc localtime(timer: ptr Time): TimeInfoPtr {.
proc localtime(timer: PTime): PTimeInfo {.
importc: "localtime", header: "<time.h>", tags: [].} importc: "localtime", header: "<time.h>", tags: [].}
proc gmtime(timer: PTime): PTimeInfo {. proc gmtime(timer: ptr Time): TimeInfoPtr {.
importc: "gmtime", header: "<time.h>", tags: [].} importc: "gmtime", header: "<time.h>", tags: [].}
proc timec(timer: PTime): TTime {. proc timec(timer: ptr Time): Time {.
importc: "time", header: "<time.h>", tags: [].} importc: "time", header: "<time.h>", tags: [].}
proc mktime(t: StructTM): TTime {. proc mktime(t: StructTM): Time {.
importc: "mktime", header: "<time.h>", tags: [].} importc: "mktime", header: "<time.h>", tags: [].}
proc asctime(tblock: StructTM): cstring {. proc asctime(tblock: StructTM): cstring {.
importc: "asctime", header: "<time.h>", tags: [].} importc: "asctime", header: "<time.h>", tags: [].}
proc ctime(time: PTime): cstring {. proc ctime(time: ptr Time): cstring {.
importc: "ctime", header: "<time.h>", tags: [].} importc: "ctime", header: "<time.h>", tags: [].}
# strftime(s: CString, maxsize: int, fmt: CString, t: tm): int {. # strftime(s: CString, maxsize: int, fmt: CString, t: tm): int {.
# importc: "strftime", header: "<time.h>".} # importc: "strftime", header: "<time.h>".}
proc clock(): TClock {.importc: "clock", header: "<time.h>", tags: [TimeEffect].} proc getClock(): Clock {.importc: "clock", header: "<time.h>", tags: [TimeEffect].}
proc difftime(a, b: TTime): float {.importc: "difftime", header: "<time.h>", proc difftime(a, b: Time): float {.importc: "difftime", header: "<time.h>",
tags: [].} tags: [].}
var var
clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl.}: int clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl.}: int
# our own procs on top of that: # our own procs on top of that:
proc tmToTimeInfo(tm: StructTM, local: bool): TTimeInfo = proc tmToTimeInfo(tm: StructTM, local: bool): TimeInfo =
const const
weekDays: array [0..6, TWeekDay] = [ weekDays: array [0..6, WeekDay] = [
dSun, dMon, dTue, dWed, dThu, dFri, dSat] dSun, dMon, dTue, dWed, dThu, dFri, dSat]
TTimeInfo(second: int(tm.second), TimeInfo(second: int(tm.second),
minute: int(tm.minute), minute: int(tm.minute),
hour: int(tm.hour), hour: int(tm.hour),
monthday: int(tm.monthday), monthday: int(tm.monthday),
@ -364,9 +365,9 @@ when not defined(JS):
timezone: if local: getTimezone() else: 0 timezone: if local: getTimezone() else: 0
) )
proc timeInfoToTM(t: TTimeInfo): StructTM = proc timeInfoToTM(t: TimeInfo): StructTM =
const const
weekDays: array [TWeekDay, int8] = [1'i8,2'i8,3'i8,4'i8,5'i8,6'i8,0'i8] weekDays: array [WeekDay, int8] = [1'i8,2'i8,3'i8,4'i8,5'i8,6'i8,0'i8]
result.second = t.second result.second = t.second
result.minute = t.minute result.minute = t.minute
result.hour = t.hour result.hour = t.hour
@ -378,36 +379,36 @@ when not defined(JS):
result.isdst = if t.isDST: 1 else: 0 result.isdst = if t.isDST: 1 else: 0
when not defined(useNimRtl): when not defined(useNimRtl):
proc `-` (a, b: TTime): int64 = proc `-` (a, b: Time): int64 =
return toBiggestInt(difftime(a, b)) return toBiggestInt(difftime(a, b))
proc getStartMilsecs(): int = proc getStartMilsecs(): int =
#echo "clocks per sec: ", clocksPerSec, "clock: ", int(clock()) #echo "clocks per sec: ", clocksPerSec, "clock: ", int(getClock())
#return clock() div (clocksPerSec div 1000) #return getClock() div (clocksPerSec div 1000)
when defined(macosx): when defined(macosx):
result = toInt(toFloat(int(clock())) / (toFloat(clocksPerSec) / 1000.0)) result = toInt(toFloat(int(getClock())) / (toFloat(clocksPerSec) / 1000.0))
else: else:
result = int(clock()) div (clocksPerSec div 1000) result = int(getClock()) div (clocksPerSec div 1000)
when false: when false:
var a: Ttimeval var a: Timeval
posix_gettimeofday(a) posix_gettimeofday(a)
result = a.tv_sec * 1000'i64 + a.tv_usec div 1000'i64 result = a.tv_sec * 1000'i64 + a.tv_usec div 1000'i64
#echo "result: ", result #echo "result: ", result
proc getTime(): TTime = return timec(nil) proc getTime(): Time = return timec(nil)
proc getLocalTime(t: TTime): TTimeInfo = proc getLocalTime(t: Time): TimeInfo =
var a = t var a = t
result = tmToTimeInfo(localtime(addr(a))[], true) result = tmToTimeInfo(localtime(addr(a))[], true)
# copying is needed anyway to provide reentrancity; thus # copying is needed anyway to provide reentrancity; thus
# the conversion is not expensive # the conversion is not expensive
proc getGMTime(t: TTime): TTimeInfo = proc getGMTime(t: Time): TimeInfo =
var a = t var a = t
result = tmToTimeInfo(gmtime(addr(a))[], false) result = tmToTimeInfo(gmtime(addr(a))[], false)
# copying is needed anyway to provide reentrancity; thus # copying is needed anyway to provide reentrancity; thus
# the conversion is not expensive # the conversion is not expensive
proc timeInfoToTime(timeInfo: TTimeInfo): TTime = proc timeInfoToTime(timeInfo: TimeInfo): Time =
var cTimeInfo = timeInfo # for C++ we have to make a copy, var cTimeInfo = timeInfo # for C++ we have to make a copy,
# because the header of mktime is broken in my version of libc # because the header of mktime is broken in my version of libc
return mktime(timeInfoToTM(cTimeInfo)) return mktime(timeInfoToTM(cTimeInfo))
@ -419,12 +420,12 @@ when not defined(JS):
add(result, p[i]) add(result, p[i])
inc(i) inc(i)
proc `$`(timeInfo: TTimeInfo): string = proc `$`(timeInfo: TimeInfo): string =
# BUGFIX: asctime returns a newline at the end! # BUGFIX: asctime returns a newline at the end!
var p = asctime(timeInfoToTM(timeInfo)) var p = asctime(timeInfoToTM(timeInfo))
result = toStringTillNL(p) result = toStringTillNL(p)
proc `$`(time: TTime): string = proc `$`(time: Time): string =
# BUGFIX: ctime returns a newline at the end! # BUGFIX: ctime returns a newline at the end!
var a = time var a = time
return toStringTillNL(ctime(addr(a))) return toStringTillNL(ctime(addr(a)))
@ -433,13 +434,13 @@ when not defined(JS):
epochDiff = 116444736000000000'i64 epochDiff = 116444736000000000'i64
rateDiff = 10000000'i64 # 100 nsecs rateDiff = 10000000'i64 # 100 nsecs
proc unixTimeToWinTime*(t: TTime): int64 = proc unixTimeToWinTime*(t: Time): int64 =
## converts a UNIX `TTime` (``time_t``) to a Windows file time ## converts a UNIX `Time` (``time_t``) to a Windows file time
result = int64(t) * rateDiff + epochDiff result = int64(t) * rateDiff + epochDiff
proc winTimeToUnixTime*(t: int64): TTime = proc winTimeToUnixTime*(t: int64): Time =
## converts a Windows time to a UNIX `TTime` (``time_t``) ## converts a Windows time to a UNIX `Time` (``time_t``)
result = TTime((t - epochDiff) div rateDiff) result = Time((t - epochDiff) div rateDiff)
proc getTzname(): tuple[nonDST, DST: string] = proc getTzname(): tuple[nonDST, DST: string] =
return ($tzname[0], $tzname[1]) return ($tzname[0], $tzname[1])
@ -447,9 +448,9 @@ when not defined(JS):
proc getTimezone(): int = proc getTimezone(): int =
return timezone return timezone
proc fromSeconds(since1970: float): TTime = TTime(since1970) proc fromSeconds(since1970: float): Time = Time(since1970)
proc toSeconds(time: TTime): float = float(time) proc toSeconds(time: Time): float = float(time)
when not defined(useNimRtl): when not defined(useNimRtl):
proc epochTime(): float = proc epochTime(): float =
@ -468,15 +469,15 @@ when not defined(JS):
{.error: "unknown OS".} {.error: "unknown OS".}
proc cpuTime(): float = proc cpuTime(): float =
result = toFloat(int(clock())) / toFloat(clocksPerSec) result = toFloat(int(getClock())) / toFloat(clocksPerSec)
elif defined(JS): elif defined(JS):
proc newDate(): TTime {.importc: "new Date".} proc newDate(): Time {.importc: "new Date".}
proc internGetTime(): TTime {.importc: "new Date", tags: [].} proc internGetTime(): Time {.importc: "new Date", tags: [].}
proc newDate(value: float): TTime {.importc: "new Date".} proc newDate(value: float): Time {.importc: "new Date".}
proc newDate(value: string): TTime {.importc: "new Date".} proc newDate(value: string): Time {.importc: "new Date".}
proc getTime(): TTime = proc getTime(): Time =
# Warning: This is something different in JS. # Warning: This is something different in JS.
return newDate() return newDate()
@ -484,7 +485,7 @@ elif defined(JS):
weekDays: array [0..6, TWeekDay] = [ weekDays: array [0..6, TWeekDay] = [
dSun, dMon, dTue, dWed, dThu, dFri, dSat] dSun, dMon, dTue, dWed, dThu, dFri, dSat]
proc getLocalTime(t: TTime): TTimeInfo = proc getLocalTime(t: Time): TimeInfo =
result.second = t.getSeconds() result.second = t.getSeconds()
result.minute = t.getMinutes() result.minute = t.getMinutes()
result.hour = t.getHours() result.hour = t.getHours()
@ -494,7 +495,7 @@ elif defined(JS):
result.weekday = weekDays[t.getDay()] result.weekday = weekDays[t.getDay()]
result.yearday = 0 result.yearday = 0
proc getGMTime(t: TTime): TTimeInfo = proc getGMTime(t: Time): TimeInfo =
result.second = t.getUTCSeconds() result.second = t.getUTCSeconds()
result.minute = t.getUTCMinutes() result.minute = t.getUTCMinutes()
result.hour = t.getUTCHours() result.hour = t.getUTCHours()
@ -504,7 +505,7 @@ elif defined(JS):
result.weekday = weekDays[t.getUTCDay()] result.weekday = weekDays[t.getUTCDay()]
result.yearday = 0 result.yearday = 0
proc timeInfoToTime*(timeInfo: TTimeInfo): TTime = proc timeInfoToTime*(timeInfo: TimeInfo): Time =
result = internGetTime() result = internGetTime()
result.setSeconds(timeInfo.second) result.setSeconds(timeInfo.second)
result.setMinutes(timeInfo.minute) result.setMinutes(timeInfo.minute)
@ -513,10 +514,10 @@ elif defined(JS):
result.setFullYear(timeInfo.year) result.setFullYear(timeInfo.year)
result.setDate(timeInfo.monthday) result.setDate(timeInfo.monthday)
proc `$`(timeInfo: TTimeInfo): string = return $(TimeInfoToTIme(timeInfo)) proc `$`(timeInfo: TimeInfo): string = return $(TimeInfoToTIme(timeInfo))
proc `$`(time: TTime): string = return $time.toLocaleString() proc `$`(time: Time): string = return $time.toLocaleString()
proc `-` (a, b: TTime): int64 = proc `-` (a, b: Time): int64 =
return a.getTime() - b.getTime() return a.getTime() - b.getTime()
var var
@ -526,11 +527,11 @@ elif defined(JS):
## get the miliseconds from the start of the program ## get the miliseconds from the start of the program
return int(getTime() - startMilsecs) return int(getTime() - startMilsecs)
proc valueOf(time: TTime): float {.importcpp: "getTime", tags:[]} proc valueOf(time: Time): float {.importcpp: "getTime", tags:[]}
proc fromSeconds(since1970: float): TTime = result = newDate(since1970) proc fromSeconds(since1970: float): Time = result = newDate(since1970)
proc toSeconds(time: TTime): float = result = time.valueOf() / 1000 proc toSeconds(time: Time): float = result = time.valueOf() / 1000
proc getTimezone(): int = result = newDate().getTimezoneOffset() proc getTimezone(): int = result = newDate().getTimezoneOffset()
@ -546,20 +547,20 @@ proc getClockStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} =
result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) & result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) &
':' & intToStr(ti.second, 2) ':' & intToStr(ti.second, 2)
proc `$`*(day: TWeekDay): string = proc `$`*(day: WeekDay): string =
## stingify operator for ``TWeekDay``. ## stingify operator for ``WeekDay``.
const lookup: array[TWeekDay, string] = ["Monday", "Tuesday", "Wednesday", const lookup: array[WeekDay, string] = ["Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday", "Sunday"] "Thursday", "Friday", "Saturday", "Sunday"]
return lookup[day] return lookup[day]
proc `$`*(m: TMonth): string = proc `$`*(m: Month): string =
## stingify operator for ``TMonth``. ## stingify operator for ``TMonth``.
const lookup: array[TMonth, string] = ["January", "February", "March", const lookup: array[Month, string] = ["January", "February", "March",
"April", "May", "June", "July", "August", "September", "October", "April", "May", "June", "July", "August", "September", "October",
"November", "December"] "November", "December"]
return lookup[m] return lookup[m]
proc format_token(info: TTimeInfo, token: string, buf: var string) = proc formatToken(info: TimeInfo, token: string, buf: var string) =
## Helper of the format proc to parse individual tokens. ## Helper of the format proc to parse individual tokens.
## ##
## Pass the found token in the user input string, and the buffer where the ## Pass the found token in the user input string, and the buffer where the
@ -672,7 +673,7 @@ proc format_token(info: TTimeInfo, token: string, buf: var string) =
raise newException(ValueError, "Invalid format string: " & token) raise newException(ValueError, "Invalid format string: " & token)
proc format*(info: TTimeInfo, f: string): string = proc format*(info: TimeInfo, f: string): string =
## This function formats `info` as specified by `f`. The following format ## This function formats `info` as specified by `f`. The following format
## specifiers are available: ## specifiers are available:
## ##
@ -718,7 +719,7 @@ proc format*(info: TTimeInfo, f: string): string =
while true: while true:
case f[i] case f[i]
of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',': of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
format_token(info, currentF, result) formatToken(info, currentF, result)
currentF = "" currentF = ""
if f[i] == '\0': break if f[i] == '\0': break
@ -735,7 +736,7 @@ proc format*(info: TTimeInfo, f: string): string =
if currentF.len < 1 or currentF[high(currentF)] == f[i]: if currentF.len < 1 or currentF[high(currentF)] == f[i]:
currentF.add(f[i]) currentF.add(f[i])
else: else:
format_token(info, currentF, result) formatToken(info, currentF, result)
dec(i) # Move position back to re-process the character separately. dec(i) # Move position back to re-process the character separately.
currentF = "" currentF = ""
@ -764,7 +765,7 @@ when isMainModule:
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") == " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
"27 27 Mon Monday 4 04 16 16 6 06 1 01 Jan January 29 29 P PM 5 75 975 1975 01975 0 00 00:00 UTC" "27 27 Mon Monday 4 04 16 16 6 06 1 01 Jan January 29 29 P PM 5 75 975 1975 01975 0 00 00:00 UTC"
when not defined(JS) and sizeof(TTime) == 8: when not defined(JS) and sizeof(Time) == 8:
var t3 = getGMTime(fromSeconds(889067643645)) # Fri 7 Jun 19:20:45 BST 30143 var t3 = getGMTime(fromSeconds(889067643645)) # Fri 7 Jun 19:20:45 BST 30143
assert t3.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" & assert t3.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") == " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Nimrod Contributors # (c) Copyright 2012 Nimrod Contributors
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -14,13 +14,15 @@
include "system/inclrtl" include "system/inclrtl"
type type
IRune = int # underlying type of TRune RuneImpl = int # underlying type of Rune
TRune* = distinct IRune ## type that can hold any Unicode character Rune* = distinct RuneImpl ## type that can hold any Unicode character
TRune16* = distinct int16 ## 16 bit Unicode character Rune16* = distinct int16 ## 16 bit Unicode character
{.deprecated: [TRune: Rune, TRune16: Rune16].}
proc `<=%`*(a, b: TRune): bool = return int(a) <=% int(b) proc `<=%`*(a, b: Rune): bool = return int(a) <=% int(b)
proc `<%`*(a, b: TRune): bool = return int(a) <% int(b) proc `<%`*(a, b: Rune): bool = return int(a) <% int(b)
proc `==`*(a, b: TRune): bool = return int(a) == int(b) proc `==`*(a, b: Rune): bool = return int(a) == int(b)
template ones(n: expr): expr = ((1 shl n)-1) template ones(n: expr): expr = ((1 shl n)-1)
@ -52,17 +54,17 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) =
## `i` is incremented by the number of bytes that have been processed. ## `i` is incremented by the number of bytes that have been processed.
bind ones bind ones
if ord(s[i]) <=% 127: if ord(s[i]) <=% 127:
result = TRune(ord(s[i])) result = Rune(ord(s[i]))
when doInc: inc(i) when doInc: inc(i)
elif ord(s[i]) shr 5 == 0b110: elif ord(s[i]) shr 5 == 0b110:
# assert(ord(s[i+1]) shr 6 == 0b10) # assert(ord(s[i+1]) shr 6 == 0b10)
result = TRune((ord(s[i]) and (ones(5))) shl 6 or result = Rune((ord(s[i]) and (ones(5))) shl 6 or
(ord(s[i+1]) and ones(6))) (ord(s[i+1]) and ones(6)))
when doInc: inc(i, 2) when doInc: inc(i, 2)
elif ord(s[i]) shr 4 == 0b1110: elif ord(s[i]) shr 4 == 0b1110:
# assert(ord(s[i+1]) shr 6 == 0b10) # assert(ord(s[i+1]) shr 6 == 0b10)
# assert(ord(s[i+2]) shr 6 == 0b10) # assert(ord(s[i+2]) shr 6 == 0b10)
result = TRune((ord(s[i]) and ones(4)) shl 12 or result = Rune((ord(s[i]) and ones(4)) shl 12 or
(ord(s[i+1]) and ones(6)) shl 6 or (ord(s[i+1]) and ones(6)) shl 6 or
(ord(s[i+2]) and ones(6))) (ord(s[i+2]) and ones(6)))
when doInc: inc(i, 3) when doInc: inc(i, 3)
@ -70,7 +72,7 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) =
# assert(ord(s[i+1]) shr 6 == 0b10) # assert(ord(s[i+1]) shr 6 == 0b10)
# assert(ord(s[i+2]) shr 6 == 0b10) # assert(ord(s[i+2]) shr 6 == 0b10)
# assert(ord(s[i+3]) shr 6 == 0b10) # assert(ord(s[i+3]) shr 6 == 0b10)
result = TRune((ord(s[i]) and ones(3)) shl 18 or result = Rune((ord(s[i]) and ones(3)) shl 18 or
(ord(s[i+1]) and ones(6)) shl 12 or (ord(s[i+1]) and ones(6)) shl 12 or
(ord(s[i+2]) and ones(6)) shl 6 or (ord(s[i+2]) and ones(6)) shl 6 or
(ord(s[i+3]) and ones(6))) (ord(s[i+3]) and ones(6)))
@ -80,7 +82,7 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) =
# assert(ord(s[i+2]) shr 6 == 0b10) # assert(ord(s[i+2]) shr 6 == 0b10)
# assert(ord(s[i+3]) shr 6 == 0b10) # assert(ord(s[i+3]) shr 6 == 0b10)
# assert(ord(s[i+4]) shr 6 == 0b10) # assert(ord(s[i+4]) shr 6 == 0b10)
result = TRune((ord(s[i]) and ones(2)) shl 24 or result = Rune((ord(s[i]) and ones(2)) shl 24 or
(ord(s[i+1]) and ones(6)) shl 18 or (ord(s[i+1]) and ones(6)) shl 18 or
(ord(s[i+2]) and ones(6)) shl 12 or (ord(s[i+2]) and ones(6)) shl 12 or
(ord(s[i+3]) and ones(6)) shl 6 or (ord(s[i+3]) and ones(6)) shl 6 or
@ -92,7 +94,7 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) =
# assert(ord(s[i+3]) shr 6 == 0b10) # assert(ord(s[i+3]) shr 6 == 0b10)
# assert(ord(s[i+4]) shr 6 == 0b10) # assert(ord(s[i+4]) shr 6 == 0b10)
# assert(ord(s[i+5]) shr 6 == 0b10) # assert(ord(s[i+5]) shr 6 == 0b10)
result = TRune((ord(s[i]) and ones(1)) shl 30 or result = Rune((ord(s[i]) and ones(1)) shl 30 or
(ord(s[i+1]) and ones(6)) shl 24 or (ord(s[i+1]) and ones(6)) shl 24 or
(ord(s[i+2]) and ones(6)) shl 18 or (ord(s[i+2]) and ones(6)) shl 18 or
(ord(s[i+3]) and ones(6)) shl 12 or (ord(s[i+3]) and ones(6)) shl 12 or
@ -100,16 +102,16 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) =
(ord(s[i+5]) and ones(6))) (ord(s[i+5]) and ones(6)))
when doInc: inc(i, 6) when doInc: inc(i, 6)
else: else:
result = TRune(ord(s[i])) result = Rune(ord(s[i]))
when doInc: inc(i) when doInc: inc(i)
proc runeAt*(s: string, i: int): TRune = proc runeAt*(s: string, i: int): Rune =
## returns the unicode character in `s` at byte index `i` ## returns the unicode character in `s` at byte index `i`
fastRuneAt(s, i, result, false) fastRuneAt(s, i, result, false)
proc toUTF8*(c: TRune): string {.rtl, extern: "nuc$1".} = proc toUTF8*(c: Rune): string {.rtl, extern: "nuc$1".} =
## converts a rune into its UTF8 representation ## converts a rune into its UTF8 representation
var i = IRune(c) var i = RuneImpl(c)
if i <=% 127: if i <=% 127:
result = newString(1) result = newString(1)
result[0] = chr(i) result[0] = chr(i)
@ -132,11 +134,11 @@ proc toUTF8*(c: TRune): string {.rtl, extern: "nuc$1".} =
result = newString(1) result = newString(1)
result[0] = chr(i) result[0] = chr(i)
proc `$`*(rune: TRune): string = proc `$`*(rune: Rune): string =
## converts a rune to a string ## converts a rune to a string
rune.toUTF8 rune.toUTF8
proc `$`*(runes: seq[TRune]): string = proc `$`*(runes: seq[Rune]): string =
## converts a sequence of runes to a string ## converts a sequence of runes to a string
result = "" result = ""
for rune in runes: result.add(rune.toUTF8) for rune in runes: result.add(rune.toUTF8)
@ -1100,7 +1102,7 @@ const
0x01f1, 501, # 0x01f1, 501, #
0x01f3, 499] # 0x01f3, 499] #
proc binarySearch(c: IRune, tab: openArray[IRune], len, stride: int): int = proc binarySearch(c: RuneImpl, tab: openArray[RuneImpl], len, stride: int): int =
var n = len var n = len
var t = 0 var t = 0
while n > 1: while n > 1:
@ -1115,41 +1117,41 @@ proc binarySearch(c: IRune, tab: openArray[IRune], len, stride: int): int =
return t return t
return -1 return -1
proc toLower*(c: TRune): TRune {.rtl, extern: "nuc$1", procvar.} = proc toLower*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
## 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`. ## If possible, prefer `toLower` over `toUpper`.
var c = IRune(c) var c = RuneImpl(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 TRune(c + tolowerRanges[p+2] - 500) return Rune(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 TRune(c + tolowerSinglets[p+1] - 500) return Rune(c + tolowerSinglets[p+1] - 500)
return TRune(c) return Rune(c)
proc toUpper*(c: TRune): TRune {.rtl, extern: "nuc$1", procvar.} = proc toUpper*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
## 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`. ## If possible, prefer `toLower` over `toUpper`.
var c = IRune(c) var c = RuneImpl(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 TRune(c + toupperRanges[p+2] - 500) return Rune(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 TRune(c + toupperSinglets[p+1] - 500) return Rune(c + toupperSinglets[p+1] - 500)
return TRune(c) return Rune(c)
proc toTitle*(c: TRune): TRune {.rtl, extern: "nuc$1", procvar.} = proc toTitle*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
var c = IRune(c) var c = RuneImpl(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 TRune(c + toTitleSinglets[p+1] - 500) return Rune(c + toTitleSinglets[p+1] - 500)
return TRune(c) return Rune(c)
proc isLower*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} = proc isLower*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
## 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`. ## If possible, prefer `isLower` over `isUpper`.
var c = IRune(c) var c = RuneImpl(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]:
@ -1158,10 +1160,10 @@ proc isLower*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
if p >= 0 and c == toupperSinglets[p]: if p >= 0 and c == toupperSinglets[p]:
return true return true
proc isUpper*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} = proc isUpper*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
## 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`. ## If possible, prefer `isLower` over `isUpper`.
var c = IRune(c) var c = RuneImpl(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]:
@ -1170,11 +1172,11 @@ proc isUpper*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
if p >= 0 and c == tolowerSinglets[p]: if p >= 0 and c == tolowerSinglets[p]:
return true return true
proc isAlpha*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} = proc isAlpha*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
## 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 c = RuneImpl(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
@ -1182,21 +1184,21 @@ proc isAlpha*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
if p >= 0 and c == alphaSinglets[p]: if p >= 0 and c == alphaSinglets[p]:
return true return true
proc isTitle*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} = proc isTitle*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
return isUpper(c) and isLower(c) return isUpper(c) and isLower(c)
proc isWhiteSpace*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} = proc isWhiteSpace*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
## returns true iff `c` is a Unicode whitespace character ## returns true iff `c` is a Unicode whitespace character
var c = IRune(c) var c = RuneImpl(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
iterator runes*(s: string): TRune = iterator runes*(s: string): Rune =
## iterates over any unicode character of the string `s`. ## iterates over any unicode character of the string `s`.
var var
i = 0 i = 0
result: TRune result: Rune
while i < len(s): while i < len(s):
fastRuneAt(s, i, result, true) fastRuneAt(s, i, result, true)
yield result yield result
@ -1209,12 +1211,12 @@ proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1", procvar.} =
## | > 0 iff a > b ## | > 0 iff a > b
var i = 0 var i = 0
var j = 0 var j = 0
var ar, br: TRune var ar, br: Rune
while i < a.len and j < b.len: while i < a.len and j < b.len:
# slow path: # slow path:
fastRuneAt(a, i, ar) fastRuneAt(a, i, ar)
fastRuneAt(b, j, br) fastRuneAt(b, j, br)
result = IRune(toLower(ar)) - IRune(toLower(br)) result = RuneImpl(toLower(ar)) - RuneImpl(toLower(br))
if result != 0: return if result != 0: return
result = a.len - b.len result = a.len - b.len

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -55,7 +55,7 @@ proc unidecode*(s: string): string =
## ##
## Example: ## Example:
## ##
## ..code-block:: nimrod ## ..code-block:: nim
## ##
## unidecode("\x53\x17\x4E\xB0") ## unidecode("\x53\x17\x4E\xB0")
## ##

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Nimrod Contributors # (c) Copyright 2012 Nimrod Contributors
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -26,16 +26,19 @@ when not defined(ECMAScript):
import terminal import terminal
type type
TTestStatus* = enum OK, FAILED TestStatus* = enum OK, FAILED
TOutputLevel* = enum PRINT_ALL, PRINT_FAILURES, PRINT_NONE OutputLevel* = enum PRINT_ALL, PRINT_FAILURES, PRINT_NONE
{.deprecated: [TTestStatus: TestStatus, TOutputLevel: OutputLevel]}
var var
# XXX: These better be thread-local abortOnError* {.threadvar.}: bool
AbortOnError*: bool outputLevel* {.threadvar.}: OutputLevel
OutputLevel*: TOutputLevel colorOutput* {.threadvar.}: bool
ColorOutput*: bool
checkpoints: seq[string] = @[] checkpoints {.threadvar.}: seq[string]
checkpoints = @[]
template TestSetupIMPL*: stmt {.immediate, dirty.} = discard template TestSetupIMPL*: stmt {.immediate, dirty.} = discard
template TestTeardownIMPL*: stmt {.immediate, dirty.} = discard template TestTeardownIMPL*: stmt {.immediate, dirty.} = discard
@ -53,7 +56,7 @@ template suite*(name: expr, body: stmt): stmt {.immediate, dirty.} =
body body
proc testDone(name: string, s: TTestStatus) = proc testDone(name: string, s: TestStatus) =
if s == FAILED: if s == FAILED:
program_result += 1 program_result += 1
@ -192,15 +195,15 @@ when declared(stdout):
## Reading settings ## Reading settings
var envOutLvl = os.getEnv("NIMTEST_OUTPUT_LVL").string var envOutLvl = os.getEnv("NIMTEST_OUTPUT_LVL").string
AbortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR") abortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")
ColorOutput = not existsEnv("NIMTEST_NO_COLOR") colorOutput = not existsEnv("NIMTEST_NO_COLOR")
else: else:
var envOutLvl = "" # TODO var envOutLvl = "" # TODO
ColorOutput = false colorOutput = false
if envOutLvl.len > 0: if envOutLvl.len > 0:
for opt in countup(low(TOutputLevel), high(TOutputLevel)): for opt in countup(low(OutputLevel), high(OutputLevel)):
if $opt == envOutLvl: if $opt == envOutLvl:
OutputLevel = opt outputLevel = opt
break break

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2014 Dominik Picheta # (c) Copyright 2014 Dominik Picheta
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -11,12 +11,14 @@
import strutils, parseutils import strutils, parseutils
type type
TUrl* = distinct string Url* = distinct string
TUri* = object Uri* = object
scheme*, username*, password*: string scheme*, username*, password*: string
hostname*, port*, path*, query*, anchor*: string hostname*, port*, path*, query*, anchor*: string
{.deprecated: [TUrl: Url, TUri: Uri].}
proc `$`*(url: TUrl): string {.deprecated.} = proc `$`*(url: TUrl): string {.deprecated.} =
## **Deprecated since 0.9.6**: Use ``TUri`` instead. ## **Deprecated since 0.9.6**: Use ``TUri`` instead.
return string(url) return string(url)
@ -172,7 +174,7 @@ proc combine*(base: TUri, reference: TUri): TUri =
## ##
## Examples: ## Examples:
## ##
## .. code-block:: nimrod ## .. code-block::
## let foo = combine(parseUri("http://example.com/foo/bar"), parseUri("/baz")) ## let foo = combine(parseUri("http://example.com/foo/bar"), parseUri("/baz"))
## assert foo.path == "/baz" ## assert foo.path == "/baz"
## ##
@ -229,7 +231,7 @@ proc `/`*(x: TUri, path: string): TUri =
## ##
## Examples: ## Examples:
## ##
## .. code-block:: nimrod ## .. code-block::
## let foo = parseUri("http://example.com/foo/bar") / parseUri("/baz") ## let foo = parseUri("http://example.com/foo/bar") / parseUri("/baz")
## assert foo.path == "/foo/bar/baz" ## assert foo.path == "/foo/bar/baz"
## ##

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2010 Dominik Picheta # (c) Copyright 2010 Dominik Picheta
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -9,7 +9,8 @@
import strutils import strutils
## This module implements XML DOM Level 2 Core specification(http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html) ## This module implements XML DOM Level 2 Core
## specification (http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html)
#http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html #http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2010 Dominik Picheta # (c) Copyright 2010 Dominik Picheta
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -156,7 +156,7 @@ proc loadXMLFile*(path: string): PDocument =
when isMainModule: when isMainModule:
var xml = loadXMLFile(r"C:\Users\Dominik\Desktop\Code\Nimrod\xmldom\test.xml") var xml = loadXMLFile("nim/xmldom/test.xml")
#echo(xml.getElementsByTagName("m:test2")[0].namespaceURI) #echo(xml.getElementsByTagName("m:test2")[0].namespaceURI)
#echo(xml.getElementsByTagName("bla:test")[0].namespaceURI) #echo(xml.getElementsByTagName("bla:test")[0].namespaceURI)
#echo(xml.getElementsByTagName("test")[0].namespaceURI) #echo(xml.getElementsByTagName("test")[0].namespaceURI)

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2010 Andreas Rumpf # (c) Copyright 2010 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -12,9 +12,11 @@
import streams, parsexml, strtabs, xmltree import streams, parsexml, strtabs, xmltree
type type
EInvalidXml* = object of EInvalidValue ## exception that is raised XmlError* = object of ValueError ## exception that is raised
## for invalid XML ## for invalid XML
errors*: seq[string] ## all detected parsing errors errors*: seq[string] ## all detected parsing errors
{.deprecated: [EInvalidXml: XmlError].}
proc raiseInvalidXml(errors: seq[string]) = proc raiseInvalidXml(errors: seq[string]) =
var e: ref EInvalidXml var e: ref EInvalidXml
@ -23,12 +25,12 @@ proc raiseInvalidXml(errors: seq[string]) =
e.errors = errors e.errors = errors
raise e raise e
proc addNode(father, son: PXmlNode) = proc addNode(father, son: XmlNode) =
if son != nil: add(father, son) if son != nil: add(father, son)
proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode proc parse(x: var XmlParser, errors: var seq[string]): XmlNode
proc untilElementEnd(x: var TXmlParser, result: PXmlNode, proc untilElementEnd(x: var XmlParser, result: XmlNode,
errors: var seq[string]) = errors: var seq[string]) =
while true: while true:
case x.kind case x.kind
@ -45,7 +47,7 @@ proc untilElementEnd(x: var TXmlParser, result: PXmlNode,
else: else:
result.addNode(parse(x, errors)) result.addNode(parse(x, errors))
proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode = proc parse(x: var XmlParser, errors: var seq[string]): XmlNode =
case x.kind case x.kind
of xmlComment: of xmlComment:
result = newComment(x.charData) result = newComment(x.charData)
@ -98,11 +100,11 @@ proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode =
next(x) next(x)
of xmlEof: discard of xmlEof: discard
proc parseXml*(s: PStream, filename: string, proc parseXml*(s: Stream, filename: string,
errors: var seq[string]): PXmlNode = errors: var seq[string]): XmlNode =
## parses the XML from stream `s` and returns a ``PXmlNode``. Every ## parses the XML from stream `s` and returns a ``PXmlNode``. Every
## occured parsing error is added to the `errors` sequence. ## occured parsing error is added to the `errors` sequence.
var x: TXmlParser var x: XmlParser
open(x, s, filename, {reportComments}) open(x, s, filename, {reportComments})
while true: while true:
x.next() x.next()
@ -118,14 +120,14 @@ proc parseXml*(s: PStream, filename: string,
break break
close(x) close(x)
proc parseXml*(s: PStream): PXmlNode = proc parseXml*(s: Stream): XmlNode =
## parses the XTML from stream `s` and returns a ``PXmlNode``. All parsing ## parses the XTML from stream `s` and returns a ``PXmlNode``. All parsing
## errors are turned into an ``EInvalidXML`` exception. ## errors are turned into an ``EInvalidXML`` exception.
var errors: seq[string] = @[] var errors: seq[string] = @[]
result = parseXml(s, "unknown_html_doc", errors) result = parseXml(s, "unknown_html_doc", errors)
if errors.len > 0: raiseInvalidXMl(errors) if errors.len > 0: raiseInvalidXMl(errors)
proc loadXml*(path: string, errors: var seq[string]): PXmlNode = proc loadXml*(path: string, errors: var seq[string]): XmlNode =
## Loads and parses XML from file specified by ``path``, and returns ## Loads and parses XML from file specified by ``path``, and returns
## a ``PXmlNode``. Every occured parsing error is added to the `errors` ## a ``PXmlNode``. Every occured parsing error is added to the `errors`
## sequence. ## sequence.
@ -133,7 +135,7 @@ proc loadXml*(path: string, errors: var seq[string]): PXmlNode =
if s == nil: raise newException(EIO, "Unable to read file: " & path) if s == nil: raise newException(EIO, "Unable to read file: " & path)
result = parseXml(s, path, errors) result = parseXml(s, path, errors)
proc loadXml*(path: string): PXmlNode = proc loadXml*(path: string): XmlNode =
## Loads and parses XML from file specified by ``path``, and returns ## Loads and parses XML from file specified by ``path``, and returns
## a ``PXmlNode``. All parsing errors are turned into an ``EInvalidXML`` ## a ``PXmlNode``. All parsing errors are turned into an ``EInvalidXML``
## exception. ## exception.
@ -148,7 +150,7 @@ when isMainModule:
var x = loadXml(paramStr(1), errors) var x = loadXml(paramStr(1), errors)
for e in items(errors): echo e for e in items(errors): echo e
var f: TFile var f: File
if open(f, "xmltest.txt", fmWrite): if open(f, "xmltest.txt", fmWrite):
f.write($x) f.write($x)
f.close() f.close()

View file

@ -1,6 +1,6 @@
# #
# #
# Nimrod's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
@ -12,33 +12,36 @@
import macros, strtabs import macros, strtabs
type type
PXmlNode* = ref TXmlNode ## an XML tree consists of ``PXmlNode``'s. XmlNode* = ref TXmlNode ## an XML tree consists of ``PXmlNode``'s.
TXmlNodeKind* = enum ## different kinds of ``PXmlNode``'s XmlNodeKind* = enum ## different kinds of ``PXmlNode``'s
xnText, ## a text element xnText, ## a text element
xnElement, ## an element with 0 or more children xnElement, ## an element with 0 or more children
xnCData, ## a CDATA node xnCData, ## a CDATA node
xnEntity, ## an entity (like ``&thing;``) xnEntity, ## an entity (like ``&thing;``)
xnComment ## an XML comment xnComment ## an XML comment
PXmlAttributes* = PStringTable ## an alias for a string to string mapping XmlAttributes* = StringTableRef ## an alias for a string to string mapping
TXmlNode {.pure, final, acyclic.} = object XmlNodeObj {.acyclic.} = object
case k: TXmlNodeKind # private, use the kind() proc to read this field. case k: XmlNodeKind # private, use the kind() proc to read this field.
of xnText, xnComment, xnCData, xnEntity: of xnText, xnComment, xnCData, xnEntity:
fText: string fText: string
of xnElement: of xnElement:
fTag: string fTag: string
s: seq[PXmlNode] s: seq[XmlNode]
fAttr: PXmlAttributes fAttr: XmlAttributes
fClientData: int ## for other clients fClientData: int ## for other clients
proc newXmlNode(kind: TXmlNodeKind): PXmlNode = {.deprecated: [PXmlNode: XmlNode, TXmlNodeKind: XmlNodeKind, PXmlAttributes:
## creates a new ``PXmlNode``. XmlAttributes, TXmlNode: XmlNodeObj].}
proc newXmlNode(kind: XmlNodeKind): XmlNode =
## creates a new ``XmlNode``.
new(result) new(result)
result.k = kind result.k = kind
proc newElement*(tag: string): PXmlNode = proc newElement*(tag: string): XmlNode =
## creates a new ``PXmlNode`` of kind ``xnText`` with the given `tag`. ## creates a new ``PXmlNode`` of kind ``xnText`` with the given `tag`.
result = newXmlNode(xnElement) result = newXmlNode(xnElement)
result.fTag = tag result.fTag = tag
@ -304,10 +307,10 @@ proc findAll*(n: PXmlNode, tag: string, result: var seq[PXmlNode]) =
## Found nodes satisfying the condition will be appended to the `result` ## Found nodes satisfying the condition will be appended to the `result`
## sequence, which can't be nil or the proc will crash. Usage example: ## sequence, which can't be nil or the proc will crash. Usage example:
## ##
## .. code-block:: nimrod ## .. code-block::
## var ## var
## html: PXmlNode ## html: XmlNode
## tags: seq[PXmlNode] = @[] ## tags: seq[XmlNode] = @[]
## ##
## html = buildHtml() ## html = buildHtml()
## findAll(html, "img", tags) ## findAll(html, "img", tags)
@ -326,8 +329,8 @@ proc findAll*(n: PXmlNode, tag: string, result: var seq[PXmlNode]) =
proc findAll*(n: PXmlNode, tag: string): seq[PXmlNode] = proc findAll*(n: PXmlNode, tag: string): seq[PXmlNode] =
## Shortcut version to assign in let blocks. Example: ## Shortcut version to assign in let blocks. Example:
## ##
## .. code-block:: nimrod ## .. code-block::
## var html: PXmlNode ## var html: XmlNode
## ##
## html = buildHtml(html) ## html = buildHtml(html)
## for imgTag in html.findAll("img"): ## for imgTag in html.findAll("img"):

View file

@ -2437,6 +2437,8 @@ when not defined(JS): #and not defined(NimrodVM):
## returns the OS file handle of the file ``f``. This is only useful for ## returns the OS file handle of the file ``f``. This is only useful for
## platform specific programming. ## platform specific programming.
{.deprecated: [fileHandle: getFileHandle].}
proc cstringArrayToSeq*(a: cstringArray, len: int): seq[string] = proc cstringArrayToSeq*(a: cstringArray, len: int): seq[string] =
## converts a ``cstringArray`` to a ``seq[string]``. `a` is supposed to be ## converts a ``cstringArray`` to a ``seq[string]``. `a` is supposed to be
## of length ``len``. ## of length ``len``.

View file

@ -49,7 +49,6 @@ Bugs
- bug: type conversions concerning proc types are weird - bug: type conversions concerning proc types are weird
- compilation of niminst takes way too long. looks like a regression - compilation of niminst takes way too long. looks like a regression
- docgen: sometimes effects are listed twice - docgen: sometimes effects are listed twice
- 'result' is not properly cleaned for NRVO --> use uninit checking instead
- blocks can "export" an identifier but the CCG generates {} for them ... - blocks can "export" an identifier but the CCG generates {} for them ...