big rename

This commit is contained in:
Araq
2014-08-27 23:42:51 +02:00
parent 15a7bcc89f
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
#
# 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
#
# See the file "copying.txt", included in this

View File

@@ -1,5 +1,5 @@
# Configuration file for the Nimrod Compiler.
# (c) 2013 Andreas Rumpf
# Configuration file for the Nim Compiler.
# (c) 2014 Andreas Rumpf
# 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.linkerexe = "arm-linux-gcc"
cs:partial
path="$lib/core"
path="$lib/pure"
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
--------
Check if `s` matches Nimrod's "while" keyword:
Check if `s` matches Nim's "while" keyword:
.. code-block:: nimrod
s =~ peg" y'while'"
@@ -212,7 +212,7 @@ example ``*`` should not be greedy, so ``\[.*?\]`` should be used instead.
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
`peg` proc.
(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
:Version: |nimrodversion|
:Version: |nimversion|
.. contents::
@@ -16,7 +16,7 @@ Introduction
</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
like variables, types or statements but is kept very basic. The `manual
<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:
.. code-block:: Nimrod
.. code-block:: Nim
# This is a comment
echo("What's your name? ")
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::
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
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::
nimrod c -r greetings.nim
nim c -r greetings.nim
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
`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
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.
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
inference`:idx:). So this will work too:
.. code-block:: Nimrod
.. code-block:: Nim
var name = readLine(stdin)
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
to the compiler: ``echo``, `readLine <system.html#readLine,TFile>`_, etc.
@@ -85,8 +85,8 @@ imported by any other module.
Lexical elements
================
Let us look at Nimrod's lexical elements in more detail: like other
programming languages Nimrod consists of (string) literals, identifiers,
Let us look at Nim's lexical elements in more detail: like other
programming languages Nim consists of (string) literals, identifiers,
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``
means tabulator, etc. There are also *raw* string literals:
.. code-block:: Nimrod
.. code-block:: Nim
r"C:\program files\nim"
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
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
# 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
a backslash:
.. code-block:: nimrod
.. code-block:: nim
type
TMyObject {.final, pure, acyclic.} = object # comment continues: \
# 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
``when false:`` statement.
.. code-block:: nimrod
.. code-block:: nim
when false:
brokenCode()
Another option is to use the `discard statement`_ together with *long string
literals* to create block comments:
.. code-block:: nimrod
discard """ You can have any Nimrod code text commented
.. code-block:: nim
discard """ You can have any Nim code text commented
out inside this with no indentation restrictions.
yes("May I ask a pointless question?") """
@@ -204,7 +204,7 @@ to a storage location:
``=`` is the *assignment operator*. The assignment operator cannot be
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:
.. 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
constant declaration at compile time:
.. code-block:: nimrod
.. code-block:: nim
const x = "abc" # the constant x contains the string "abc"
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:
.. code-block:: nimrod
.. code-block:: nim
let name = readLine(stdin)
if 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
a multi-branch:
.. code-block:: nimrod
.. code-block:: nim
let name = readLine(stdin)
case name
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.)
For integers or other ordinal types value ranges are also possible:
.. code-block:: nimrod
.. code-block:: nim
# this statement will be explained later:
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
the compiler that for every other value nothing should be done:
.. code-block:: nimrod
.. code-block:: nim
...
case n
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:
.. code-block:: nimrod
.. code-block:: nim
echo("What's your name? ")
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>`_
iterator:
.. code-block:: nimrod
.. code-block:: nim
echo("Counting to ten: ")
for i in countup(1, 10):
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.
Each value is ``echo``-ed. This code does the same:
.. code-block:: nimrod
.. code-block:: nim
echo("Counting to 10: ")
var i = 1
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):
.. code-block:: nimrod
.. code-block:: nim
echo("Counting down from 10 to 1: ")
for i in countdown(10, 1):
echo($i)
# --> 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:
.. code-block:: nimrod
.. code-block:: nim
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
outside the loop:
.. code-block:: nimrod
.. code-block:: nim
while false:
var x = "hi"
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``
statement can be used to open a new block explicitly:
.. code-block:: nimrod
.. code-block:: nim
block myblock:
var x = "hi"
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
innermost construct, unless a label of a block is given:
.. code-block:: nimrod
.. code-block:: nim
block myblock:
echo("entering block")
while true:
@@ -461,7 +461,7 @@ Continue statement
Like in many other programming languages, a ``continue`` statement starts
the next iteration immediately:
.. code-block:: nimrod
.. code-block:: nim
while true:
let x = readLine(stdin)
if x == "": continue
@@ -473,7 +473,7 @@ When statement
Example:
.. code-block:: nimrod
.. code-block:: nim
when system.hostOS == "windows":
echo("running on Windows!")
@@ -504,17 +504,17 @@ possible.
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.
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:
Assignment, procedure calls or the ``return`` statement belong to the simple
statements. *Complex statements* like ``if``, ``when``, ``for``, ``while`` can
contain other statements. To avoid ambiguities, complex statements always have
to be indented, but single simple statements do not:
.. code-block:: nimrod
.. code-block:: nim
# no indentation needed for single assignment statement:
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
contain indentation at certain places for better readability:
.. code-block:: nimrod
.. code-block:: nim
if thisIsaLongCondition() and
thisIsAnotherLongCondition(1,
@@ -548,7 +548,7 @@ an open parenthesis and after commas.
With parenthesis and semicolons ``(;)`` you can use statements where only
an expression is allowed:
.. code-block:: nimrod
.. code-block:: nim
# computes fac(4) at compile time:
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
<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:
.. code-block:: nimrod
.. code-block:: nim
proc yes(question: string): bool =
echo(question, " (y/n)")
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
the exit.
.. code-block:: nimrod
.. code-block:: nim
proc sumTillNegative(x: varargs[int]): int =
for i in x:
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
is possible, and actually an idiom:
.. code-block:: nimrod
.. code-block:: nim
proc printSeq(s: seq, nprinted: int = -1) =
var nprinted = if nprinted == -1: s.len else: min(nprinted, s.len)
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
caller, a ``var`` parameter can be used:
.. code-block:: nimrod
.. code-block:: nim
proc divmod(a, b: int; res, remainder: var int) =
res = a div b # integer division
remainder = a mod b # integer modulo operation
@@ -653,17 +653,17 @@ a tuple as a return value instead of using var parameters.
Discard statement
-----------------
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:
.. code-block:: nimrod
.. code-block:: nim
discard yes("May I ask a pointless question?")
The return value can be ignored implicitly if the called proc/iterator has
been declared with the ``discardable`` pragma:
.. code-block:: nimrod
.. code-block:: nim
proc p(x, y: int): int {.discardable.} =
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
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;
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
also possible, but not very readable:
.. code-block:: nimrod
.. code-block:: nim
var w = createWindow(0, 0, title = "My Application",
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
specify them:
.. code-block:: nimrod
.. code-block:: nim
proc createWindow(x = 0, y = 0, width = 500, height = 700,
title = "unknown",
show = true): Window =
@@ -723,9 +723,9 @@ no need to write ``title: string = "unknown"``, for example.
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: bool): string =
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
(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
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
@@ -744,13 +744,13 @@ algorithm. Ambiguous calls are reported as errors.
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
use operators in `infix notation` (``a + b``) or `prefix notation` (``+ a``).
An infix operator always receives two arguments, a prefix operator always one.
Postfix operators are not possible, because this would be ambiguous: does
``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``,
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 "``":
.. code-block:: nimrod
.. code-block:: nim
proc `$` (x: myDataType): string = ...
# now the $ operator also works with myDataType, overloading resolution
# 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
procedure:
.. code-block:: nimrod
.. code-block:: nim
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.)
However, this cannot be done for mutually recursive procedures:
.. code-block:: nimrod
.. code-block:: nim
# forward declaration:
proc even(n: int): bool
@@ -810,7 +810,7 @@ Iterators
Let's return to the boring counting example:
.. code-block:: nimrod
.. code-block:: nim
echo("Counting to ten: ")
for i in countup(1, 10):
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
loop? Lets try:
.. code-block:: nimrod
.. code-block:: nim
proc countup(a, b: int): int =
var res = a
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``
and there it is - our first iterator:
.. code-block:: nimrod
.. code-block:: nim
iterator countup(a, b: int): int =
var res = a
while res <= b:
@@ -868,7 +868,7 @@ that are available for them in detail.
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,
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
evaluation. Example:
.. code-block:: nimrod
.. code-block:: nim
while p != nil and p.name != "xyz":
# p.name is not evaluated if p == nil
@@ -885,7 +885,7 @@ evaluation. Example:
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.
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
@@ -900,13 +900,13 @@ Converting from an integer to a ``char`` is done with the ``chr`` proc.
Strings
-------
String variables in Nimrod are **mutable**, so appending to a string
is quite efficient. Strings in Nimrod are both zero-terminated and have a
String variables in Nim are **mutable**, so appending to a string
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``
procedure; the length never counts the terminating zero. Accessing the
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':
# 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
--------
Nimrod has these integer types built-in:
Nim has these integer types built-in:
``int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64``.
The default integer type is ``int``. Integer literals can have a *type suffix*
to mark them to be of another integer type:
.. code-block:: nimrod
.. code-block:: nim
let
x = 0 # x is of type ``int``
y = 0'i8 # y is of type ``int8``
@@ -964,7 +964,7 @@ cannot be detected at compile time).
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,
``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
type:
.. code-block:: nimrod
.. code-block:: nim
var
x = 0.0 # x is of type ``float``
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
there is a difference between the ``$`` and ``repr`` outputs:
.. code-block:: nimrod
.. code-block:: nim
var
myBool = true
myCharacter = 'n'
myString = "nimrod"
myString = "nim"
myInteger = 42
myFloat = 3.14
echo($myBool, ":", repr(myBool))
@@ -1013,7 +1013,7 @@ there is a difference between the ``$`` and ``repr`` outputs:
echo($myCharacter, ":", repr(myCharacter))
# --> n:'n'
echo($myString, ":", repr(myString))
# --> nimrod:0x10fa8c050"nimrod"
# --> nim:0x10fa8c050"nim"
echo($myInteger, ":", repr(myInteger))
# --> 42:42
echo($myFloat, ":", repr(myFloat))
@@ -1023,9 +1023,9 @@ there is a difference between the ``$`` and ``repr`` outputs:
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
biggestInt = int64 # biggest integer 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
at runtime by 0, the second by 1 and so on. Example:
.. code-block:: nimrod
.. code-block:: nim
type
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`
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.
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*:
.. code-block:: nimrod
.. code-block:: nim
type
TMyEnum = enum
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
(the base type). Example:
.. code-block:: nimrod
.. code-block:: nim
type
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
integers for natural numbers. This is often **wrong**: you don't want unsigned
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
@@ -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 ``[]``:
.. code-block:: nimrod
.. code-block:: nim
type
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
``--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.
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
array `a` and `high(a) <system.html#high>`_ the highest valid index.
.. code-block:: nimrod
.. code-block:: nim
type
TDirection = enum
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
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
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
subdivided in height levels accessed through their integer index:
.. code-block:: nimrod
.. code-block:: nim
type
TLightTower = array[1..10, TLevelSetting]
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``
type and instead write it embedded directly as the type of the first dimension:
.. code-block:: nimrod
.. code-block:: nim
type
TLightTower = array[1..10, array[north..west, TBlinkLights]]
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:
.. code-block:: nimrod
.. code-block:: nim
type
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
@@ -1239,7 +1239,7 @@ A sequence may be passed to an openarray parameter.
Example:
.. code-block:: nimrod
.. code-block:: nim
var
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
<system.html>`_ module. Examples:
.. code-block:: nimrod
.. code-block:: nim
for i in @[3, 4, 5]:
echo($i)
# --> 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
to an array automatically:
.. code-block:: nimrod
.. code-block:: nim
proc myWriteln(f: TFile, a: varargs[string]) =
for s in items(a):
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
type conversions in this context:
.. code-block:: nimrod
.. code-block:: nim
proc myWriteln(f: TFile, a: varargs[string, `$`]) =
for s in items(a):
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
define operators which accept TSlice objects to define ranges.
.. code-block:: nimrod
.. code-block:: nim
var
a = "Nimrod is a progamming language"
a = "Nim is a progamming language"
b = "Slices are useless."
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
integer.
.. code-block:: nimrod
.. code-block:: nim
type
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
variables! Example:
.. code-block:: nimrod
.. code-block:: nim
import os
let
path = "usr/local/nimrodc.html"
path = "usr/local/nimc.html"
(dir, name, ext) = splitFile(path)
baddir, badname, badext = splitFile(path)
echo dir # outputs `usr/local`
echo name # outputs `nimrodc`
echo name # outputs `nimc`
echo ext # outputs `.html`
# All the following output the same line:
# `(dir: usr/local, name: nimrodc, ext: .html)`
# `(dir: usr/local, name: nimc, ext: .html)`
echo baddir
echo badname
echo badext
@@ -1431,12 +1431,12 @@ variables! Example:
Tuple unpacking **only** works in ``var`` or ``let`` blocks. The following code
won't compile:
.. code-block:: nimrod
.. code-block:: nim
import os
var
path = "usr/local/nimrodc.html"
path = "usr/local/nimc.html"
dir, name, ext = ""
(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
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
objects of a garbage collected heap, untraced references point to
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)
operators perform implicit dereferencing operations for reference types:
.. code-block:: nimrod
.. code-block:: nim
type
PNode = ref TNode
@@ -1489,12 +1489,12 @@ Procedural type
---------------
A procedural type is a (somewhat abstract) pointer to a procedure.
``nil`` is an allowed value for a variable of a procedural type.
Nimrod uses procedural types to achieve `functional`:idx: programming
Nim uses procedural types to achieve `functional`:idx: programming
techniques.
Example:
.. code-block:: nimrod
.. code-block:: nim
proc echoItem(x: int) = echo(x)
proc forEach(action: proc (x: int)) =
@@ -1513,13 +1513,13 @@ listed in the `manual <manual.html>`_.
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
`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
with an asterisk (``*``) are exported:
.. code-block:: nimrod
.. code-block:: nim
# Module A
var
x*, y: int
@@ -1554,7 +1554,7 @@ The algorithm for compiling modules is:
This is best illustrated by an example:
.. code-block:: nimrod
.. code-block:: nim
# Module A
type
T1* = int # Module A exports the type ``T1``
@@ -1565,7 +1565,7 @@ This is best illustrated by an example:
main()
.. code-block:: nimrod
.. code-block:: nim
# Module B
import A # A is not parsed here! Only the already known symbols
# 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
imported by a third one:
.. code-block:: nimrod
.. code-block:: nim
# Module A
var x*: string
.. code-block:: nimrod
.. code-block:: nim
# Module B
var x*: int
.. code-block:: nimrod
.. code-block:: nim
# Module C
import A, B
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
rules apply:
.. code-block:: nimrod
.. code-block:: nim
# Module A
proc x*(a: int): string = result = $a
.. code-block:: nimrod
.. code-block:: nim
# Module B
proc x*(a: string): string = result = $a
.. code-block:: nimrod
.. code-block:: nim
# Module C
import A, B
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
the ``except`` qualifier.
.. code-block:: nimrod
.. code-block:: nim
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
``from import`` statement:
.. code-block:: nimrod
.. code-block:: nim
from mymodule import x, y, z
The ``from`` statement can also force namespace qualification on
symbols, thereby making symbols available, but needing to be qualified
to be used.
.. code-block:: nimrod
.. code-block:: nim
from mymodule import x, y, z
x() # use x without any qualification
.. code-block:: nimrod
.. code-block:: nim
from mymodule import nil
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
define a shorter alias to use when qualifying symbols.
.. code-block:: nimrod
.. code-block:: nim
from mymodule as m import nil
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``
statement is useful to split up a large module into several files:
.. code-block:: nimrod
.. code-block:: nim
include fileA, fileB, fileC
**Note**: The documentation generator currently does not follow ``include``
@@ -1683,7 +1683,7 @@ generated documentation.
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>`_

View File

@@ -1,9 +1,9 @@
=========================
Nimrod Tutorial (Part II)
=========================
======================
Nim Tutorial (Part II)
======================
:Author: Andreas Rumpf
:Version: |nimrodversion|
:Version: |nimversion|
.. contents::
@@ -15,7 +15,7 @@ Introduction
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**
`manual <manual.html>`_ **contains many more examples of the advanced language
features.**
@@ -24,18 +24,18 @@ features.**
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
enclosed in the special ``{.`` and ``.}`` curly dot brackets. This tutorial
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.
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
program, not *the only* way. Often a procedural approach leads to simpler
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
``of`` operator that can be used to check the object's type:
.. code-block:: nimrod
.. code-block:: nim
type
TPerson = object of TObject
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
(*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
------------------------
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
would require arbitrary symbol lookahead which slows down compilation.)
Example:
.. code-block:: nimrod
.. code-block:: nim
type
PNode = ref TNode # a traced reference to a TNode
TNode = object
@@ -114,7 +114,7 @@ Example:
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
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)``
(like an ordinary call):
.. code-block:: nimrod
.. code-block:: nim
proc getID(x: TPerson): int =
TStudent(x).id
@@ -141,9 +141,9 @@ variant types are needed.
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
TNodeKind = enum # the different node types
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
``join`` a string method or an array method?
Nimrod 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
Nim avoids these problems by not assigning methods to a class. All methods
in Nim are multi-methods. As we will see later, multi-methods are
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
for any type:
.. code-block:: nimrod
.. code-block:: nim
echo("abc".len) # is the same as echo(len("abc"))
echo("abc".toUpper())
@@ -211,7 +211,7 @@ postfix notation.)
So "pure object oriented" code is easy to write:
.. code-block:: nimrod
.. code-block:: nim
import strutils
stdout.writeln("Give a list of numbers (separated by spaces): ")
@@ -221,12 +221,12 @@ So "pure object oriented" code is easy to write:
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
the same. But setting a value is different; for this a special setter syntax
is needed:
.. code-block:: nimrod
.. code-block:: nim
type
TSocket* = object of TObject
@@ -252,7 +252,7 @@ is needed:
The ``[]`` array access operator can be overloaded to provide
`array properties`:idx:\ :
.. code-block:: nimrod
.. code-block:: nim
type
TVector* = object
x, y, z: float
@@ -283,7 +283,7 @@ Dynamic dispatch
Procedures always use static dispatch. For dynamic dispatch replace the
``proc`` keyword by ``method``:
.. code-block:: nimrod
.. code-block:: nim
type
PExpr = ref object of TObject ## abstract base class for an expression
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
dispatching:
.. code-block:: nimrod
.. code-block:: nim
type
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
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
calls and enables inlining. However, other optimizations like compile time
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
==========
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
exception hierarchy that you might want to stick to. Exceptions derive from
E_Base, which provides the common interface.
@@ -364,7 +364,7 @@ Raise statement
---------------
Raising an exception is done with the ``raise`` statement:
.. code-block:: nimrod
.. code-block:: nim
var
e: ref EOS
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,
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")
@@ -384,7 +384,7 @@ Try statement
The ``try`` statement handles exceptions:
.. code-block:: nimrod
.. code-block:: nim
# read the first two lines of a text file that should contain numbers
# and tries to add them
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>`_
module. Example:
.. code-block:: nimrod
.. code-block:: nim
try:
doSomethingHere()
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
prevent that proc from compiling. Usage example:
.. code-block:: nimrod
.. code-block:: nim
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
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
tracking is part of Nimrod's effect system). Another more roundabout way to
find out the list of exceptions raised by a proc is to use the Nimrod ``doc2``
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 Nim ``doc2``
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>`_.
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
containers:
.. code-block:: nimrod
.. code-block:: nim
type
TBinaryTree[T] = object # TBinaryTree is a generic type with
# with generic param ``T``
@@ -557,7 +557,7 @@ is not hidden and is used in the ``preorder`` iterator.
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
compiler. They integrate well with the rest of the language and share none
of C's preprocessor macros flaws.
@@ -566,7 +566,7 @@ To *invoke* a template, call it like a procedure.
Example:
.. code-block:: nimrod
.. code-block:: nim
template `!=` (a, b: expr): expr =
# this definition exists in the System module
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
simple proc for logging:
.. code-block:: nimrod
.. code-block:: nim
const
debug = true
@@ -602,7 +602,7 @@ evaluation for procedures is *eager*).
Turning the ``log`` proc into a template solves this problem:
.. code-block:: nimrod
.. code-block:: nim
const
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,
``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
declaration. The reason is that statements can be passed to a template
via a special ``:`` syntax:
.. code-block:: nimrod
.. code-block:: nim
template withFile(f: expr, filename: string, mode: TFileMode,
body: stmt): stmt {.immediate.} =
@@ -672,18 +651,18 @@ Macros
======
Macros enable advanced compile-time code transformations, but they cannot
change Nimrod's syntax. However, this is no real restriction because Nimrod's
syntax is flexible enough anyway. Macros have to be implemented in pure Nimrod
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 Nim
code if `foreign function interface (FFI)
<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)
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.
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
(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.
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
variable number of arguments:
.. code-block:: nimrod
# to work with Nimrod syntax trees, we need an API that is defined in the
.. code-block:: nim
# to work with Nim syntax trees, we need an API that is defined in the
# ``macros`` module:
import macros
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:
result = newNimNode(nnkStmtList, n)
# iterate over any argument that is passed to this macro:
@@ -727,7 +706,7 @@ variable number of arguments:
The macro call expands to:
.. code-block:: nimrod
.. code-block:: nim
write(stdout, "a[0]")
write(stdout, ": ")
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
regular expressions:
.. code-block:: nimrod
.. code-block:: nim
macro case_token(n: stmt): stmt =
# 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
use the following snippet of code as the starting point:
.. code-block:: nimrod
.. code-block:: nim
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
modified source code implementing the macro:
.. code-block:: nimrod
.. code-block:: nim
import macros, strutils
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).
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.
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
snippet of code:
.. code-block:: nimrod
.. code-block:: nim
const cfgversion= "1.1"
const cfglicenseOwner= "Hyori Lee"
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
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``
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
create the following source file and compile it to see what the compiler
*expects* from us:
.. code-block:: nimrod
.. code-block:: nim
import macros
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
macro:
.. code-block:: nimrod
.. code-block:: nim
import macros, strutils
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 s = newFileStream(filename, fmRead)
if s == nil: quit("cannot open the file " & filename)
var x: TXmlParser
var x: XmlParser
open(x, s, filename)
next(x) # get first event
block mainLoop:

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
@@ -10,9 +10,12 @@
## This module implements some common generic algorithms.
type
TSortOrder* = enum ## sort order
SortOrder* = enum ## sort order
Descending, Ascending
{.deprecated: [TSortOrder: SortOrder].}
proc `*`*(x: int, order: TSortOrder): int {.inline.} =
## flips `x` if ``order == Descending``;
## 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
#
# See the file "copying.txt", included in this
@@ -13,7 +13,7 @@ import os, oids, tables, strutils, macros, times
import rawsockets, net
export TPort, TSocketFlags
export Port, SocketFlags
#{.injectStmt: newGcInvariant().}
@@ -36,21 +36,24 @@ export TPort, TSocketFlags
# -- Futures
type
PFutureBase* = ref object of PObject
FutureBase* = ref object of RootObj
cb: proc () {.closure,gcsafe.}
finished: bool
error*: ref EBase
error*: ref Exception
errorStackTrace*: string
when not defined(release):
stackTrace: string ## For debugging purposes only.
id: int
fromProc: string
PFuture*[T] = ref object of PFutureBase
Future*[T] = ref object of FutureBase
value: T
{.deprecated: [PFutureBase: FutureBase, PFuture: Future].}
var currentID* = 0
proc newFuture*[T](fromProc: string = "unspecified"): PFuture[T] =
proc newFuture*[T](fromProc: string = "unspecified"): Future[T] =
## Creates a new future.
##
## 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
currentID.inc()
proc checkFinished[T](future: PFuture[T]) =
proc checkFinished[T](future: Future[T]) =
when not defined(release):
if future.finished:
echo("<-----> ", future.id, " ", future.fromProc)
@@ -76,7 +79,7 @@ proc checkFinished[T](future: PFuture[T]) =
echo getStackTrace()
assert false
proc complete*[T](future: PFuture[T], val: T) =
proc complete*[T](future: Future[T], val: T) =
## Completes ``future`` with value ``val``.
#assert(not future.finished, "Future already finished, cannot finish twice.")
checkFinished(future)
@@ -86,7 +89,7 @@ proc complete*[T](future: PFuture[T], val: T) =
if future.cb != nil:
future.cb()
proc complete*(future: PFuture[void]) =
proc complete*(future: Future[void]) =
## Completes a void ``future``.
#assert(not future.finished, "Future already finished, cannot finish twice.")
checkFinished(future)
@@ -95,7 +98,7 @@ proc complete*(future: PFuture[void]) =
if future.cb != nil:
future.cb()
proc fail*[T](future: PFuture[T], error: ref EBase) =
proc fail*[T](future: Future[T], error: ref Exception) =
## Completes ``future`` with ``error``.
#assert(not future.finished, "Future already finished, cannot finish twice.")
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.
# Turns out this is a bad idea.
#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.
##
## 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:
future.cb()
proc `callback=`*[T](future: PFuture[T],
cb: proc (future: PFuture[T]) {.closure,gcsafe.}) =
proc `callback=`*[T](future: Future[T],
cb: proc (future: Future[T]) {.closure,gcsafe.}) =
## Sets the callback proc to be called when the future completes.
##
## If future has already completed then ``cb`` will be called immediately.
future.callback = proc () = cb(future)
proc echoOriginalStackTrace[T](future: PFuture[T]) =
proc echoOriginalStackTrace[T](future: Future[T]) =
# TODO: Come up with something better.
when not defined(release):
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)
else:
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
## 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?
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
else:
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.
##
## ``True`` may indicate an error or a value. Use ``failed`` to distinguish.
future.finished
proc failed*(future: PFutureBase): bool =
proc failed*(future: FutureBase): bool =
## Determines whether ``future`` completed with an error.
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
## finished with an error.
##
@@ -180,7 +184,7 @@ proc asyncCheck*[T](future: PFuture[T]) =
echoOriginalStackTrace(future)
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``
## complete.
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()
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``
## complete.
var retFuture = newFuture[void]()
@@ -203,7 +207,7 @@ proc `or`*[T, Y](fut1: PFuture[T], fut2: PFuture[Y]): PFuture[void] =
type
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) =
var oldTimers = p.timers
@@ -375,10 +379,10 @@ when defined(windows) or defined(nimdoc):
RemoteSockaddr, RemoteSockaddrLength)
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``.
##
## Returns a ``PFuture`` which will complete when the connection succeeds
## Returns a ``Future`` which will complete when the connection succeeds
## or an error occurs.
verifyPresence(socket)
var retFuture = newFuture[void]("connect")
@@ -437,7 +441,7 @@ when defined(windows) or defined(nimdoc):
return retFuture
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
## 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
@@ -525,7 +529,7 @@ when defined(windows) or defined(nimdoc):
return retFuture
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
## data has been sent.
verifyPresence(socket)
@@ -568,7 +572,7 @@ when defined(windows) or defined(nimdoc):
return retFuture
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
## corresponding to that connection and the remote address of the client.
## The future will complete when the connection is successfully accepted.
@@ -789,7 +793,7 @@ else:
processTimers(p)
proc connect*(socket: TAsyncFD, address: string, port: TPort,
af = AF_INET): PFuture[void] =
af = AF_INET): Future[void] =
var retFuture = newFuture[void]("connect")
proc cb(sock: TAsyncFD): bool =
@@ -824,7 +828,7 @@ else:
return retFuture
proc recv*(socket: TAsyncFD, size: int,
flags = {TSocketFlags.SafeDisconn}): PFuture[string] =
flags = {TSocketFlags.SafeDisconn}): Future[string] =
var retFuture = newFuture[string]("recv")
var readBuffer = newString(size)
@@ -855,7 +859,7 @@ else:
return retFuture
proc send*(socket: TAsyncFD, data: string,
flags = {TSocketFlags.SafeDisconn}): PFuture[void] =
flags = {TSocketFlags.SafeDisconn}): Future[void] =
var retFuture = newFuture[void]("send")
var written = 0
@@ -887,7 +891,7 @@ else:
return retFuture
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,
client: TAsyncFD]]("acceptAddr")
proc cb(sock: TAsyncFD): bool =
@@ -912,7 +916,7 @@ else:
addRead(socket, cb)
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
## ``ms`` miliseconds.
var retFuture = newFuture[void]("sleepAsync")
@@ -921,14 +925,14 @@ proc sleepAsync*(ms: int): PFuture[void] =
return retFuture
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
## corresponding to that connection.
## The future will complete when the connection is successfully accepted.
var retFut = newFuture[TAsyncFD]("accept")
var fut = acceptAddr(socket, flags)
fut.callback =
proc (future: PFuture[tuple[address: string, client: TAsyncFD]]) =
proc (future: Future[tuple[address: string, client: TAsyncFD]]) =
assert future.finished
if future.failed:
retFut.fail(future.error)
@@ -1111,12 +1115,12 @@ macro async*(prc: stmt): stmt {.immediate.} =
hint("Processing " & prc[0].getName & " as an async proc.")
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:
error("Expected return type of 'PFuture' got '" & $returnType & "'")
error("Expected return type of 'Future' got '" & $returnType & "'")
elif returnType.kind == nnkBracketExpr:
if $returnType[0] != "PFuture":
error("Expected return type of 'PFuture' got '" & $returnType[0] & "'")
if $returnType[0] != "Future":
error("Expected return type of 'Future' got '" & $returnType[0] & "'")
let subtypeIsVoid = returnType.kind == nnkEmpty or
(returnType.kind == nnkBracketExpr and
@@ -1137,7 +1141,7 @@ macro async*(prc: stmt): stmt {.immediate.} =
subRetType),
newLit(prc[0].getName)))) # Get type from return type of this proc
# -> iterator nameIter(): PFutureBase {.closure.} =
# -> iterator nameIter(): FutureBase {.closure.} =
# -> var result: T
# -> <proc_body>
# -> complete(retFuture, result)
@@ -1153,7 +1157,7 @@ macro async*(prc: stmt): stmt {.immediate.} =
# -> complete(retFuture)
procBody.add(newCall(newIdentNode("complete"), retFutureSym))
var closureIterator = newProc(iteratorNameSym, [newIdentNode("PFutureBase")],
var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase")],
procBody, nnkIteratorDef)
closureIterator[4] = newNimNode(nnkPragma, prc[6]).add(newIdentNode("closure"))
outerProcBody.add(closureIterator)
@@ -1176,8 +1180,8 @@ macro async*(prc: stmt): stmt {.immediate.} =
if subtypeIsVoid:
# Add discardable pragma.
if returnType.kind == nnkEmpty:
# Add PFuture[void]
result[3][0] = parseExpr("PFuture[void]")
# Add Future[void]
result[3][0] = parseExpr("Future[void]")
result[6] = outerProcBody
@@ -1185,7 +1189,7 @@ macro async*(prc: stmt): stmt {.immediate.} =
#if prc[0].getName == "processClient":
# 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
## 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
#
# See the file "copying.txt", included in this
@@ -13,7 +13,7 @@
import strtabs, asyncnet, asyncdispatch, parseutils, uri, strutils
type
TRequest* = object
Request* = object
client*: PAsyncSocket # TODO: Separate this into a Response object?
reqMethod*: string
headers*: PStringTable
@@ -22,10 +22,10 @@ type
hostname*: string ## The hostname of the client that made the request.
body*: string
PAsyncHttpServer* = ref object
AsyncHttpServer* = ref object
socket: PAsyncSocket
THttpCode* = enum
HttpCode* = enum
Http200 = "200 OK",
Http303 = "303 Moved",
Http400 = "400 Bad Request",
@@ -33,10 +33,13 @@ type
Http500 = "500 Internal Server Error",
Http502 = "502 Bad Gateway"
THttpVersion* = enum
HttpVersion* = enum
HttpVer11,
HttpVer10
{.deprecated: [TRequest: Request, PAsyncHttpServer: AsyncHttpServer,
THttpCode: HttpCode, THttpVersion: HttpVersion].}
proc `==`*(protocol: tuple[orig: string, major, minor: int],
ver: THttpVersion): bool =
let major =

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf, Dominik Picheta
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -10,8 +10,8 @@ include "system/inclrtl"
import sockets, os
## This module implements an asynchronous event loop together with asynchronous sockets
## which use this event loop.
## This module implements an asynchronous event loop together with asynchronous
## sockets which use this event loop.
## 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
## ``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
## 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
## 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
## your code.
##
@@ -44,10 +44,10 @@ import sockets, os
## Asynchronous sockets
## ====================
##
## For most purposes you do not need to worry about the ``TDelegate`` type. The
## ``PAsyncSocket`` is what you are after. It's a reference to the ``TAsyncSocket``
## object. This object defines events which you should overwrite by your own
## procedures.
## For most purposes you do not need to worry about the ``Delegate`` type. The
## ``AsyncSocket`` is what you are after. It's a reference to
## the ``AsyncSocketObj`` object. This object defines events which you should
## overwrite by your own procedures.
##
## 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
@@ -57,13 +57,13 @@ import sockets, os
##
## 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.")
## var client: PAsyncSocket
## var client: AsyncSocket
## new(client)
## s.accept(client)
## client.handleRead = ...
@@ -76,29 +76,29 @@ import sockets, os
## the socket has established a connection to a server socket; from that point
## 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
## synchronously then you can use the ``getSocket`` converter to get a TSocket
## object from the PAsyncSocket object, this can then be combined with ``accept``
## like so:
## synchronously then you can use the ``getSocket`` converter to get
## a ``Socket`` from the ``AsyncSocket`` object, this can then be combined
## with ``accept`` like so:
##
## .. code-block:: nimrod
## .. code-block:: nim
##
## proc handleAccept(s: PAsyncSocket) =
## var client: TSocket
## proc handleAccept(s: AsyncSocket) =
## var client: Socket
## getSocket(s).accept(client)
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:
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
TDelegate* = object
fd*: TSocketHandle
deleVal*: PObject
DelegateObj* = object
fd*: SocketHandle
deleVal*: RootRef
handleRead*: proc (h: PObject) {.nimcall, gcsafe.}
handleWrite*: proc (h: PObject) {.nimcall, gcsafe.}
@@ -107,18 +107,18 @@ type
open*: bool
task*: proc (h: PObject) {.nimcall, gcsafe.}
mode*: TFileMode
mode*: FileMode
PDelegate* = ref TDelegate
Delegate* = ref DelegateObj
PDispatcher* = ref TDispatcher
TDispatcher = object
Dispatcher* = ref DispatcherObj
DispatcherObj = object
delegates: seq[PDelegate]
PAsyncSocket* = ref TAsyncSocket
TAsyncSocket* = object of TObject
socket: TSocket
info: TInfo
AsyncSocket* = ref AsyncSocketObj
AsyncSocketObj* = object of RootObj
socket: Socket
info: SocketStatus
handleRead*: proc (s: PAsyncSocket) {.closure, gcsafe.}
handleWrite: proc (s: PAsyncSocket) {.closure, gcsafe.}
@@ -134,10 +134,17 @@ type
proto: TProtocol
deleg: PDelegate
TInfo* = enum
SocketStatus* = enum
SockIdle, SockConnecting, SockConnected, SockListening, SockClosed,
SockUDPBound
{.deprecated: [TDelegate: DelegateObj, PDelegate: Delegate,
TProcessOption: ProcessOption,
TInfo: SocketStatus, PAsyncSocket: AsyncSocket, TAsyncSocket: AsyncSocketObj,
TDispatcher: DispatcherObj, PDispatcher: Dispatcher,
].}
proc newDelegate*(): PDelegate =
## Creates a new delegate.
new(result)

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2014 Dominik Picheta
#
# See the file "copying.txt", included in this
@@ -19,9 +19,9 @@
##
## import asyncnet, asyncdispatch
##
## var clients: seq[PAsyncSocket] = @[]
## var clients: seq[AsyncSocket] = @[]
##
## proc processClient(client: PAsyncSocket) {.async.} =
## proc processClient(client: AsyncSocket) {.async.} =
## while true:
## let line = await client.recvLine()
## for c in clients:
@@ -29,7 +29,7 @@
##
## proc serve() {.async.} =
## var server = newAsyncSocket()
## server.bindAddr(TPort(12345))
## server.bindAddr(Port(12345))
## server.listen()
##
## while true:
@@ -54,8 +54,10 @@ when defined(ssl):
type
# TODO: I would prefer to just do:
# PAsyncSocket* {.borrow: `.`.} = distinct PSocket. But that doesn't work.
TAsyncSocket {.borrow: `.`.} = distinct TSocketImpl
PAsyncSocket* = ref TAsyncSocket
AsyncSocketDesc {.borrow: `.`.} = distinct TSocketImpl
AsyncSocket* = ref AsyncSocketDesc
{.deprecated: [PAsyncSocket: AsyncSocket].}
# 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
#
# 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
#
# 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
#
# 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
#
# 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!
discard startProcess(command=b, args=[url], options={poUseShell})
return
except EOS:
except OSError:
discard

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
@@ -9,7 +9,7 @@
## This module implements helper procs for CGI applications. Example:
##
## .. code-block:: Nimrod
## .. code-block:: Nim
##
## import strtabs, cgi
##
@@ -93,12 +93,14 @@ proc XMLencode*(s: string): string =
for i in 0..len(s)-1: addXmlChar(result, s[i])
type
ECgi* = object of IOError ## the exception that is raised, if a CGI error occurs
TRequestMethod* = enum ## the used request method
CgiError* = object of IOError ## exception that is raised if a CGI error occurs
RequestMethod* = enum ## the used request method
methodNone, ## no REQUEST_METHOD environment variable
methodPost, ## query uses the POST method
methodGet ## query uses the GET method
{.deprecated: [TRequestMethod: RequestMethod, ECgi: CgiError].}
proc cgiError*(msg: string) {.noreturn.} =
## raises an ECgi exception with message `msg`.
var e: ref ECgi
@@ -377,7 +379,7 @@ proc setCookie*(name, value: string) =
write(stdout, "Set-Cookie: ", name, "=", value, "\n")
var
gcookies {.threadvar.}: PStringTable
gcookies {.threadvar.}: StringTableRef
proc getCookie*(name: string): TaintedString =
## 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
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
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
oldVal = atomic_load_n(oldTbl[idx].value.addr, ATOMIC_RELAXED)
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
if atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr,
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
if isTomb(oldVal):
echo("oldVal is Tomb!!!, should not happen")
if pop(oldVal) != NULL:
result = setVal(newTbl, pop(oldKey), pop(oldVal), NULL, true) == NULL
if pop(oldVal) != 0:
result = setVal(newTbl, pop(oldKey), pop(oldVal), 0, true) == 0
if result:
#echo("Copied a Slot! idx= " & $idx & " key= " & $oldKey & " val= " & $oldVal)
else:
@@ -323,7 +355,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
idx = idx and (table.len - 1)
#echo("try set idx = " & $idx & "for" & $key)
var
probedKey = NULL
probedKey = 0
openKey = atomic_compare_exchange_n(table[idx].key.addr, probedKey.addr,
key, false, ATOMIC_RELAXED, ATOMIC_RELAXED)
if openKey:
@@ -339,7 +371,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
if keyEQ[K](probedKey, key):
#echo("we found the 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 probes >= reProbeLimit: echo("Too much probing " & $probes)
#echo("try to resize")
@@ -361,7 +393,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
return oldVal
nextTable = atomic_load_n(table.next.addr, ATOMIC_SEQ_CST)
if nextTable == nil and
((oldVal == NULL and
((oldVal == 0 and
(probes >= reProbeLimit or table.used / table.len > 0.8)) or
(isPrime(oldVal))):
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,
val, false, ATOMIC_RELEASE, ATOMIC_RELAXED):
#echo("val set at table " & $cast[int](table))
if expVal != NULL:
if (oldVal == NULL or isTomb(oldVal)) and not isTomb(val):
if expVal != 0:
if (oldVal == 0 or isTomb(oldVal)) and not isTomb(val):
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)
if oldVal == NULL and expVal != NULL:
if oldVal == 0 and expVal != 0:
return setTomb(oldVal)
else: return 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 isTomb(val):
#echo("val was tomb but not prime")
return NULL
return 0
else:
#echo("-GotIt- idx = ", idx, " key = ", key, " val ", 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 newTable == nil:
#echo("too many probes and no new table ", key, " ", idx )
return NULL
return 0
else:
newTable = helpCopy(table)
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) =
# 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) =
# 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) =
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))
else:
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))
when not (V is TRaw):
if newVal != oldPtr and oldPtr != NULL:
if newVal != oldPtr and oldPtr != 0:
deallocShared(cast[ptr V](oldPtr))
@@ -573,10 +605,3 @@ when isMainModule:
# echo(i, " = ", hashInt(i) and 8191)
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
#
# See the file "copying.txt", included in this
@@ -12,30 +12,32 @@
## by Adam Langley.
type
TNode[T] = object {.pure, final, acyclic.}
NodeObj[T] = object {.pure, final, acyclic.}
byte: int ## byte index of the difference
otherbits: char
case isLeaf: bool
of false: child: array[0..1, ref TNode[T]]
of false: child: array[0..1, ref NodeObj[T]]
of true:
key: string
when T isnot void:
val: T
PNode[T] = ref TNode[T]
TCritBitTree*[T] = object {.
Node[T] = ref NodeObj[T]
CritBitTree*[T] = object {.
pure, final.} ## The crit bit tree can either be used
## as a mapping from strings to
## some type ``T`` or as a set of
## strings if ``T`` is void.
root: PNode[T]
root: Node[T]
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).
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
while it != nil:
if not it.isLeaf:
@@ -45,15 +47,15 @@ proc rawGet[T](c: TCritBitTree[T], key: string): PNode[T] =
else:
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`.
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`.
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:
new c.root
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 dir = (1 + (ord(ch) or newOtherBits)) shr 8
var inner: PNode[T]
var inner: Node[T]
new inner
new result
result.isLeaf = true
@@ -106,7 +108,7 @@ proc rawInsert[T](c: var TCritBitTree[T], key: string): PNode[T] =
wherep[] = inner
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
## ``c[key] = val`` is performed.
let oldCount = c.count
@@ -115,23 +117,23 @@ proc containsOrIncl*[T](c: var TCritBitTree[T], key: string, val: T): bool =
when T isnot void:
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
## it is inserted into `c`.
let oldCount = c.count
var n = rawInsert(c, key)
result = c.count == oldCount
proc incl*(c: var TCritBitTree[void], key: string) =
proc incl*(c: var CritBitTree[void], key: string) =
## includes `key` in `c`.
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`.
var n = rawInsert(c, key)
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`,
## default empty value for the type `B` is returned
## 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)
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.
## 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)
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`.
## If the `key` does not exist, nothing happens.
var p = c.root
var wherep = addr(c.root)
var whereq: ptr PNode = nil
var whereq: ptr Node[T] = nil
if p == nil: return
var dir = 0
var q: PNode
var q: Node[T]
while not p.isLeaf:
whereq = wherep
q = p
@@ -170,7 +172,7 @@ proc excl*[T](c: var TCritBitTree[T], key: string) =
whereq[] = q.child[1 - dir]
dec c.count
iterator leaves[T](n: PNode[T]): PNode[T] =
iterator leaves[T](n: Node[T]): Node[T] =
if n != nil:
# XXX actually we could compute the necessary stack size in advance:
# it's rougly log2(c.count).
@@ -183,33 +185,33 @@ iterator leaves[T](n: PNode[T]): PNode[T] =
assert(it != nil)
yield it
iterator keys*[T](c: TCritBitTree[T]): string =
iterator keys*[T](c: CritBitTree[T]): string =
## yields all keys in lexicographical order.
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
## corresponding keys.
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
## corresponding keys. The values can be modified.
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.
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`.
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.
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 top = p
if p != nil:
@@ -223,42 +225,42 @@ proc allprefixedAux[T](c: TCritBitTree[T], key: string): PNode[T] =
if p.key[i] != key[i]: return
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`.
let top = allprefixedAux(c, prefix)
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`.
let top = allprefixedAux(c, prefix)
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
## corresponding keys.
let top = allprefixedAux(c, prefix)
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
## corresponding keys. The values can be modified.
let top = allprefixedAux(c, prefix)
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] =
## yields all (key, value)-pairs of `c` starting with `prefix`.
let top = allprefixedAux(c, prefix)
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] =
## yields all (key, value)-pairs of `c` starting with `prefix`.
## The yielded values can be modified.
let top = allprefixedAux(c, prefix)
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:
## ``{keyA: value, keyB: value}``, ``{:}``
## If `T` is void the outputs look like:
@@ -285,7 +287,7 @@ proc `$`*[T](c: TCritBitTree[T]): string =
result.add("}")
when isMainModule:
var r: TCritBitTree[void]
var r: CritBitTree[void]
r.incl "abc"
r.incl "xyz"
r.incl "def"

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
@@ -9,7 +9,7 @@
## The ``intsets`` module implements an efficient int set implemented as a
## 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
## copy; use ``assign`` to get a deep copy.
@@ -17,7 +17,7 @@ import
os, hashes, math
type
TBitScalar = int
BitScalar = int
const
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
# divisible by 64
TrunkMask = BitsPerTrunk - 1
IntsPerTrunk = BitsPerTrunk div (sizeof(TBitScalar) * 8)
IntShift = 5 + ord(sizeof(TBitScalar) == 8) # 5 or 6, depending on int width
IntsPerTrunk = BitsPerTrunk div (sizeof(BitScalar) * 8)
IntShift = 5 + ord(sizeof(BitScalar) == 8) # 5 or 6, depending on int width
IntMask = 1 shl IntShift - 1
type
@@ -34,15 +34,16 @@ type
TTrunk {.final.} = object
next: PTrunk # all nodes are connected with this pointer
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]
TIntSet* {.final.} = object ## an efficient set of 'int' implemented as a
## sparse bit set
IntSet* = object ## an efficient set of 'int' implemented as a sparse bit set
counter, max: int
head: PTrunk
data: TTrunkSeq
{.deprecated: [TIntSet: IntSet].}
proc mustRehash(length, counter: int): bool {.inline.} =
assert(length > counter)
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
#
# See the file "copying.txt", included in this
@@ -15,52 +15,58 @@ when not defined(nimhygiene):
{.pragma: dirty.}
type
TDoublyLinkedNode* {.pure,
final.}[T] = object ## a node a doubly linked list consists of
next*, prev*: ref TDoublyLinkedNode[T]
DoublyLinkedNodeObj*[T] = object ## a node a doubly linked list consists of
next*, prev*: ref DoublyLinkedNodeObj[T]
value*: T
PDoublyLinkedNode*[T] = ref TDoublyLinkedNode[T]
DoublyLinkedNode*[T] = ref DoublyLinkedNodeObj[T]
TSinglyLinkedNode* {.pure,
final.}[T] = object ## a node a singly linked list consists of
next*: ref TSinglyLinkedNode[T]
SinglyLinkedNodeObj*[T] = object ## a node a singly linked list consists of
next*: ref SinglyLinkedNodeObj[T]
value*: T
PSinglyLinkedNode*[T] = ref TSinglyLinkedNode[T]
SinglyLinkedNode*[T] = ref SinglyLinkedNodeObj[T]
TSinglyLinkedList* {.pure, final.}[T] = object ## a singly linked list
head*, tail*: PSinglyLinkedNode[T]
SinglyLinkedList*[T] = object ## a singly linked list
head*, tail*: SinglyLinkedNode[T]
TDoublyLinkedList* {.pure, final.}[T] = object ## a doubly linked list
head*, tail*: PDoublyLinkedNode[T]
DoublyLinkedList*[T] = object ## a doubly linked list
head*, tail*: DoublyLinkedNode[T]
TSinglyLinkedRing* {.pure, final.}[T] = object ## a singly linked ring
head*: PSinglyLinkedNode[T]
SinglyLinkedRing*[T] = object ## a singly linked ring
head*: SinglyLinkedNode[T]
TDoublyLinkedRing* {.pure, final.}[T] = object ## a doubly linked ring
head*: PDoublyLinkedNode[T]
DoublyLinkedRing*[T] = object ## a doubly linked ring
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.
discard
proc initDoublyLinkedList*[T](): TDoublyLinkedList[T] =
proc initDoublyLinkedList*[T](): DoublyLinkedList[T] =
## creates a new doubly linked list that is empty.
discard
proc initSinglyLinkedRing*[T](): TSinglyLinkedRing[T] =
proc initSinglyLinkedRing*[T](): SinglyLinkedRing[T] =
## creates a new singly linked ring that is empty.
discard
proc initDoublyLinkedRing*[T](): TDoublyLinkedRing[T] =
proc initDoublyLinkedRing*[T](): DoublyLinkedRing[T] =
## creates a new doubly linked ring that is empty.
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`.
new(result)
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`.
new(result)
result.value = value
@@ -99,38 +105,38 @@ template findImpl() {.dirty.} =
for x in nodes(L):
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`.
itemsListImpl()
iterator items*[T](L: TSinglyLinkedList[T]): T =
iterator items*[T](L: SinglyLinkedList[T]): T =
## yields every value of `L`.
itemsListImpl()
iterator items*[T](L: TSinglyLinkedRing[T]): T =
iterator items*[T](L: SinglyLinkedRing[T]): T =
## yields every value of `L`.
itemsRingImpl()
iterator items*[T](L: TDoublyLinkedRing[T]): T =
iterator items*[T](L: DoublyLinkedRing[T]): T =
## yields every value of `L`.
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
## list during traversal is supported.
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
## list during traversal is supported.
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
## list during traversal is supported.
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
## list during traversal is supported.
nodesRingImpl()
@@ -142,33 +148,33 @@ template dollarImpl() {.dirty.} =
result.add($x.value)
result.add("]")
proc `$`*[T](L: TSinglyLinkedList[T]): string =
proc `$`*[T](L: SinglyLinkedList[T]): string =
## turns a list into its string representation.
dollarImpl()
proc `$`*[T](L: TDoublyLinkedList[T]): string =
proc `$`*[T](L: DoublyLinkedList[T]): string =
## turns a list into its string representation.
dollarImpl()
proc `$`*[T](L: TSinglyLinkedRing[T]): string =
proc `$`*[T](L: SinglyLinkedRing[T]): string =
## turns a list into its string representation.
dollarImpl()
proc `$`*[T](L: TDoublyLinkedRing[T]): string =
proc `$`*[T](L: DoublyLinkedRing[T]): string =
## turns a list into its string representation.
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
## exist.
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
## exist.
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
## exist.
findImpl()
@@ -300,5 +306,3 @@ proc remove*[T](L: var TDoublyLinkedRing[T], n: PDoublyLinkedNode[T]) =
L.head = nil
else:
L.head = L.head.prev

View File

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

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2011 Alex Mitchell
#
# See the file "copying.txt", included in this
@@ -31,7 +31,7 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
##
## Example:
##
## .. code-block:: nimrod
## .. code-block::
## let
## s1 = @[1, 2, 3]
## s2 = @[4, 5]
@@ -50,7 +50,7 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
proc deduplicate*[T](seq1: seq[T]): seq[T] =
## Returns a new sequence without duplicates.
##
## .. code-block:: nimrod
## .. code-block::
## let
## dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4]
## 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
## longer sequence are discarded. Example:
##
## .. code-block:: nimrod
## .. code-block::
## let
## short = @[1, 2, 3]
## 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:
##
## .. code-block:: nimrod
## .. code-block::
## let numbers = @[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]]
@@ -155,7 +155,7 @@ iterator filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): T =
##
## Example:
##
## .. code-block:: nimrod
## .. code-block::
## let numbers = @[1, 4, 5, 8, 9, 7, 4]
## for n in filter(numbers, proc (x: int): bool = x mod 2 == 0):
## echo($n)
@@ -169,7 +169,7 @@ proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T] =
##
## Example:
##
## .. code-block:: nimrod
## .. code-block::
## let
## colors = @["red", "yellow", "black"]
## 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:
##
## .. code-block:: nimrod
## .. code-block::
## var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1]
## keepIf(floats, proc(x: float): bool = x > 10)
## assert floats == @[13.0, 12.5, 10.1]
@@ -202,7 +202,7 @@ proc delete*[T](s: var seq[T], first=0, last=0) =
##
## Example:
##
##.. code-block:: nimrod
##.. code-block::
## 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]
## dest.delete(3, 8)
@@ -223,7 +223,7 @@ proc insert*[T](dest: var seq[T], src: openArray[T], pos=0) =
##
## Example:
##
##.. code-block:: nimrod
##.. code-block::
## var dest = @[1,1,1,1,1,1,1,1]
## let
## 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')``.
## Example:
##
## .. code-block:: nimrod
## .. code-block::
## let
## temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44]
## 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')``.
## Example:
##
## .. code-block:: nimrod
## .. code-block::
## var candidates = @["foo", "bar", "baz", "foobar"]
## keepItIf(candidates, it.len == 3 and it[0] == 'b')
## assert candidates == @["bar", "baz"]
@@ -292,7 +292,7 @@ template toSeq*(iter: expr): expr {.immediate.} =
##
## Example:
##
## .. code-block:: nimrod
## .. code-block::
## let
## numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
## 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) -
## 3). Example:
##
## .. code-block:: nimrod
## .. code-block::
## let
## numbers = @[5, 9, 11]
## addition = foldl(numbers, a + b)
## substraction = foldl(numbers, a - b)
## multiplication = foldl(numbers, a * b)
## words = @["nim", "rod", "is", "cool"]
## words = @["nim", "is", "cool"]
## concatenation = foldl(words, a & b)
## assert addition == 25, "Addition is (((5)+9)+11)"
## assert substraction == -15, "Substraction 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"
var result {.gensym.}: type(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 -
## (3))). Example:
##
## .. code-block:: nimrod
## .. code-block::
## let
## numbers = @[5, 9, 11]
## addition = foldr(numbers, a + b)
## substraction = foldr(numbers, a - b)
## multiplication = foldr(numbers, a * b)
## words = @["nim", "rod", "is", "cool"]
## words = @["nim", "is", "cool"]
## concatenation = foldr(words, a & b)
## assert addition == 25, "Addition is (5+(9+(11)))"
## assert substraction == 7, "Substraction 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"
var result {.gensym.}: type(sequence[0])
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
## original. Example:
##
## .. code-block:: nimrod
## .. code-block::
## let
## nums = @[1, 2, 3, 4]
## 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
## are mutating. Example:
##
## .. code-block:: nimrod
## .. code-block::
## var nums = @[1, 2, 3, 4]
## nums.mapIt(it * 3)
## assert nums[0] + nums[3] == 15
@@ -412,7 +412,7 @@ template mapIt*(varSeq, pred: expr) =
template newSeqWith*(len: int, init: expr): expr =
## creates a new sequence, calling `init` to initialize each value. Example:
##
## .. code-block:: nimrod
## .. code-block::
## var seq2D = newSeqWith(20, newSeq[bool](10))
## seq2D[0][0] = true
## seq2D[1][0] = true
@@ -503,12 +503,12 @@ when isMainModule:
addition = foldl(numbers, a + b)
substraction = foldl(numbers, a - b)
multiplication = foldl(numbers, a * b)
words = @["nim", "rod", "is", "cool"]
words = @["nim", "is", "cool"]
concatenation = foldl(words, a & b)
assert addition == 25, "Addition is (((5)+9)+11)"
assert substraction == -15, "Substraction is (((5)-9)-11)"
assert multiplication == 495, "Multiplication is (((5)*9)*11)"
assert concatenation == "nimrodiscool"
assert concatenation == "nimiscool"
block: # foldr tests
let
@@ -516,12 +516,12 @@ when isMainModule:
addition = foldr(numbers, a + b)
substraction = foldr(numbers, a - b)
multiplication = foldr(numbers, a * b)
words = @["nim", "rod", "is", "cool"]
words = @["nim", "is", "cool"]
concatenation = foldr(words, a & b)
assert addition == 25, "Addition is (5+(9+(11)))"
assert substraction == 7, "Substraction is (5-(9-(11)))"
assert multiplication == 495, "Multiplication is (5*(9*(11)))"
assert concatenation == "nimrodiscool"
assert concatenation == "nimiscool"
block: # delete tests
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
#
# See the file "copying.txt", included in this
@@ -24,17 +24,19 @@ when not defined(nimhygiene):
{.pragma: dirty.}
type
TSlotEnum = enum seEmpty, seFilled, seDeleted
TKeyValuePair[A] = tuple[slot: TSlotEnum, key: A]
TKeyValuePairSeq[A] = seq[TKeyValuePair[A]]
TSet* {.final, myShallow.}[A] = object ## \
SlotEnum = enum seEmpty, seFilled, seDeleted
KeyValuePair[A] = tuple[slot: SlotEnum, key: A]
KeyValuePairSeq[A] = seq[KeyValuePair[A]]
HashSet* {.myShallow.}[A] = object ## \
## 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.
data: TKeyValuePairSeq[A]
data: KeyValuePairSeq[A]
counter: int
{.deprecated: [TSet: HashSet].}
proc isValid*[A](s: TSet[A]): bool =
## 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
## initialized. Example:
##
## .. code-block :: nimrod
## .. code-block ::
## proc savePreferences(options: TSet[string]) =
## assert options.isValid, "Pass an initialized set!"
## # 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 ------------------------------
type
TOrderedKeyValuePair[A] = tuple[
slot: TSlotEnum, next: int, key: A]
TOrderedKeyValuePairSeq[A] = seq[TOrderedKeyValuePair[A]]
TOrderedSet* {.
final, myShallow.}[A] = object ## \
OrderedKeyValuePair[A] = tuple[
slot: SlotEnum, next: int, key: A]
OrderedKeyValuePairSeq[A] = seq[OrderedKeyValuePair[A]]
OrderedSet* {.myShallow.}[A] = object ## \
## 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.
data: TOrderedKeyValuePairSeq[A]
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
## <#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
## correctly initialized. Example:
##
## .. code-block :: nimrod
## .. code-block::
## proc saveTarotCards(cards: TOrderedSet[int]) =
## assert cards.isValid, "Pass an initialized set!"
## # Do stuff here, may crash in release builds!
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`.
##
## 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() =
## Internal micro test to validate docstrings and such.
block isValidTest:
var options: TSet[string]
var options: HashSet[string]
proc savePreferences(options: TSet[string]) =
assert options.isValid, "Pass an initialized set!"
options = initSet[string]()
options.savePreferences
block lenTest:
var values: TSet[int]
var values: HashSet[int]
assert(not values.isValid)
assert values.len == 0
assert values.card == 0
@@ -835,14 +838,14 @@ proc testModule() =
assert b == toSet(["1", "2", "3"])
block isValidTest:
var cards: TOrderedSet[string]
var cards: OrderedSet[string]
proc saveTarotCards(cards: TOrderedSet[string]) =
assert cards.isValid, "Pass an initialized set!"
cards = initOrderedSet[string]()
cards.saveTarotCards
block lenTest:
var values: TOrderedSet[int]
var values: OrderedSet[int]
assert(not values.isValid)
assert values.len == 0
assert values.card == 0
@@ -879,7 +882,7 @@ proc testModule() =
assert(a == b) # https://github.com/Araq/Nimrod/issues/1413
block initBlocks:
var a: TOrderedSet[int]
var a: OrderedSet[int]
a.init(4)
a.incl(2)
a.init

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2013 Andreas Rumpf
#
# 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
## example:
##
## .. code-block:: nimrod
## .. code-block::
## type
## Person = object
## firstName, lastName: string
@@ -61,13 +61,15 @@ import
{.pragma: myShallow.}
type
TSlotEnum = enum seEmpty, seFilled, seDeleted
TKeyValuePair[A, B] = tuple[slot: TSlotEnum, key: A, val: B]
TKeyValuePairSeq[A, B] = seq[TKeyValuePair[A, B]]
TTable* {.final, myShallow.}[A, B] = object ## generic hash table
data: TKeyValuePairSeq[A, B]
SlotEnum = enum seEmpty, seFilled, seDeleted
KeyValuePair[A, B] = tuple[slot: SlotEnum, key: A, val: B]
KeyValuePairSeq[A, B] = seq[KeyValuePair[A, B]]
Table* {.myShallow.}[A, B] = object ## generic hash table
data: KeyValuePairSeq[A, B]
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):
{.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`.
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) =
rawInsertImpl()
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)
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)
@@ -347,14 +349,16 @@ proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): PTable[C, B] =
# ------------------------------ ordered table ------------------------------
type
TOrderedKeyValuePair[A, B] = tuple[
slot: TSlotEnum, next: int, key: A, val: B]
TOrderedKeyValuePairSeq[A, B] = seq[TOrderedKeyValuePair[A, B]]
TOrderedTable* {.
final, myShallow.}[A, B] = object ## table that remembers insertion order
data: TOrderedKeyValuePairSeq[A, B]
OrderedKeyValuePair[A, B] = tuple[
slot: SlotEnum, next: int, key: A, val: B]
OrderedKeyValuePairSeq[A, B] = seq[OrderedKeyValuePair[A, B]]
OrderedTable* {.
myShallow.}[A, B] = object ## table that remembers insertion order
data: OrderedKeyValuePairSeq[A, B]
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.} =
## returns the number of keys in `t`.
@@ -608,11 +612,13 @@ proc sort*[A, B](t: POrderedTable[A, B],
# ------------------------------ count tables -------------------------------
type
TCountTable* {.final, myShallow.}[
CountTable* {.myShallow.}[
A] = object ## table that counts the number of each key
data: seq[tuple[key: A, val: 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 =
## 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
#
# See the file "copying.txt", included in this
@@ -12,18 +12,20 @@
import strutils
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.
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 g = a.int shr 8 and 0xff
var b = a.int and 0xff
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.} =
extract(a, ar, ag, ab)
@@ -38,23 +40,23 @@ proc satMinus(a, b: int): int {.inline.} =
result = a -% b
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
## component cannot overflow (255 is used as a maximum).
colorOp(satPlus)
proc `-`*(a, b: TColor): TColor =
proc `-`*(a, b: Color): Color =
## substracts two colors: This uses saturated artithmetic, so that each color
## component cannot overflow (255 is used as a maximum).
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`.
result.r = a.int shr 16 and 0xff
result.g = a.int shr 8 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
## dark) to 1.0 (full color intensity).
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
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
## 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
@@ -84,146 +86,146 @@ template mix*(a, b: TColor, fn: expr): expr =
const
colAliceBlue* = TColor(0xF0F8FF)
colAntiqueWhite* = TColor(0xFAEBD7)
colAqua* = TColor(0x00FFFF)
colAquamarine* = TColor(0x7FFFD4)
colAzure* = TColor(0xF0FFFF)
colBeige* = TColor(0xF5F5DC)
colBisque* = TColor(0xFFE4C4)
colBlack* = TColor(0x000000)
colBlanchedAlmond* = TColor(0xFFEBCD)
colBlue* = TColor(0x0000FF)
colBlueViolet* = TColor(0x8A2BE2)
colBrown* = TColor(0xA52A2A)
colBurlyWood* = TColor(0xDEB887)
colCadetBlue* = TColor(0x5F9EA0)
colChartreuse* = TColor(0x7FFF00)
colChocolate* = TColor(0xD2691E)
colCoral* = TColor(0xFF7F50)
colCornflowerBlue* = TColor(0x6495ED)
colCornsilk* = TColor(0xFFF8DC)
colCrimson* = TColor(0xDC143C)
colCyan* = TColor(0x00FFFF)
colDarkBlue* = TColor(0x00008B)
colDarkCyan* = TColor(0x008B8B)
colDarkGoldenRod* = TColor(0xB8860B)
colDarkGray* = TColor(0xA9A9A9)
colDarkGreen* = TColor(0x006400)
colDarkKhaki* = TColor(0xBDB76B)
colDarkMagenta* = TColor(0x8B008B)
colDarkOliveGreen* = TColor(0x556B2F)
colDarkorange* = TColor(0xFF8C00)
colDarkOrchid* = TColor(0x9932CC)
colDarkRed* = TColor(0x8B0000)
colDarkSalmon* = TColor(0xE9967A)
colDarkSeaGreen* = TColor(0x8FBC8F)
colDarkSlateBlue* = TColor(0x483D8B)
colDarkSlateGray* = TColor(0x2F4F4F)
colDarkTurquoise* = TColor(0x00CED1)
colDarkViolet* = TColor(0x9400D3)
colDeepPink* = TColor(0xFF1493)
colDeepSkyBlue* = TColor(0x00BFFF)
colDimGray* = TColor(0x696969)
colDodgerBlue* = TColor(0x1E90FF)
colFireBrick* = TColor(0xB22222)
colFloralWhite* = TColor(0xFFFAF0)
colForestGreen* = TColor(0x228B22)
colFuchsia* = TColor(0xFF00FF)
colGainsboro* = TColor(0xDCDCDC)
colGhostWhite* = TColor(0xF8F8FF)
colGold* = TColor(0xFFD700)
colGoldenRod* = TColor(0xDAA520)
colGray* = TColor(0x808080)
colGreen* = TColor(0x008000)
colGreenYellow* = TColor(0xADFF2F)
colHoneyDew* = TColor(0xF0FFF0)
colHotPink* = TColor(0xFF69B4)
colIndianRed* = TColor(0xCD5C5C)
colIndigo* = TColor(0x4B0082)
colIvory* = TColor(0xFFFFF0)
colKhaki* = TColor(0xF0E68C)
colLavender* = TColor(0xE6E6FA)
colLavenderBlush* = TColor(0xFFF0F5)
colLawnGreen* = TColor(0x7CFC00)
colLemonChiffon* = TColor(0xFFFACD)
colLightBlue* = TColor(0xADD8E6)
colLightCoral* = TColor(0xF08080)
colLightCyan* = TColor(0xE0FFFF)
colLightGoldenRodYellow* = TColor(0xFAFAD2)
colLightGrey* = TColor(0xD3D3D3)
colLightGreen* = TColor(0x90EE90)
colLightPink* = TColor(0xFFB6C1)
colLightSalmon* = TColor(0xFFA07A)
colLightSeaGreen* = TColor(0x20B2AA)
colLightSkyBlue* = TColor(0x87CEFA)
colLightSlateGray* = TColor(0x778899)
colLightSteelBlue* = TColor(0xB0C4DE)
colLightYellow* = TColor(0xFFFFE0)
colLime* = TColor(0x00FF00)
colLimeGreen* = TColor(0x32CD32)
colLinen* = TColor(0xFAF0E6)
colMagenta* = TColor(0xFF00FF)
colMaroon* = TColor(0x800000)
colMediumAquaMarine* = TColor(0x66CDAA)
colMediumBlue* = TColor(0x0000CD)
colMediumOrchid* = TColor(0xBA55D3)
colMediumPurple* = TColor(0x9370D8)
colMediumSeaGreen* = TColor(0x3CB371)
colMediumSlateBlue* = TColor(0x7B68EE)
colMediumSpringGreen* = TColor(0x00FA9A)
colMediumTurquoise* = TColor(0x48D1CC)
colMediumVioletRed* = TColor(0xC71585)
colMidnightBlue* = TColor(0x191970)
colMintCream* = TColor(0xF5FFFA)
colMistyRose* = TColor(0xFFE4E1)
colMoccasin* = TColor(0xFFE4B5)
colNavajoWhite* = TColor(0xFFDEAD)
colNavy* = TColor(0x000080)
colOldLace* = TColor(0xFDF5E6)
colOlive* = TColor(0x808000)
colOliveDrab* = TColor(0x6B8E23)
colOrange* = TColor(0xFFA500)
colOrangeRed* = TColor(0xFF4500)
colOrchid* = TColor(0xDA70D6)
colPaleGoldenRod* = TColor(0xEEE8AA)
colPaleGreen* = TColor(0x98FB98)
colPaleTurquoise* = TColor(0xAFEEEE)
colPaleVioletRed* = TColor(0xD87093)
colPapayaWhip* = TColor(0xFFEFD5)
colPeachPuff* = TColor(0xFFDAB9)
colPeru* = TColor(0xCD853F)
colPink* = TColor(0xFFC0CB)
colPlum* = TColor(0xDDA0DD)
colPowderBlue* = TColor(0xB0E0E6)
colPurple* = TColor(0x800080)
colRed* = TColor(0xFF0000)
colRosyBrown* = TColor(0xBC8F8F)
colRoyalBlue* = TColor(0x4169E1)
colSaddleBrown* = TColor(0x8B4513)
colSalmon* = TColor(0xFA8072)
colSandyBrown* = TColor(0xF4A460)
colSeaGreen* = TColor(0x2E8B57)
colSeaShell* = TColor(0xFFF5EE)
colSienna* = TColor(0xA0522D)
colSilver* = TColor(0xC0C0C0)
colSkyBlue* = TColor(0x87CEEB)
colSlateBlue* = TColor(0x6A5ACD)
colSlateGray* = TColor(0x708090)
colSnow* = TColor(0xFFFAFA)
colSpringGreen* = TColor(0x00FF7F)
colSteelBlue* = TColor(0x4682B4)
colTan* = TColor(0xD2B48C)
colTeal* = TColor(0x008080)
colThistle* = TColor(0xD8BFD8)
colTomato* = TColor(0xFF6347)
colTurquoise* = TColor(0x40E0D0)
colViolet* = TColor(0xEE82EE)
colWheat* = TColor(0xF5DEB3)
colWhite* = TColor(0xFFFFFF)
colWhiteSmoke* = TColor(0xF5F5F5)
colYellow* = TColor(0xFFFF00)
colYellowGreen* = TColor(0x9ACD32)
colAliceBlue* = Color(0xF0F8FF)
colAntiqueWhite* = Color(0xFAEBD7)
colAqua* = Color(0x00FFFF)
colAquamarine* = Color(0x7FFFD4)
colAzure* = Color(0xF0FFFF)
colBeige* = Color(0xF5F5DC)
colBisque* = Color(0xFFE4C4)
colBlack* = Color(0x000000)
colBlanchedAlmond* = Color(0xFFEBCD)
colBlue* = Color(0x0000FF)
colBlueViolet* = Color(0x8A2BE2)
colBrown* = Color(0xA52A2A)
colBurlyWood* = Color(0xDEB887)
colCadetBlue* = Color(0x5F9EA0)
colChartreuse* = Color(0x7FFF00)
colChocolate* = Color(0xD2691E)
colCoral* = Color(0xFF7F50)
colCornflowerBlue* = Color(0x6495ED)
colCornsilk* = Color(0xFFF8DC)
colCrimson* = Color(0xDC143C)
colCyan* = Color(0x00FFFF)
colDarkBlue* = Color(0x00008B)
colDarkCyan* = Color(0x008B8B)
colDarkGoldenRod* = Color(0xB8860B)
colDarkGray* = Color(0xA9A9A9)
colDarkGreen* = Color(0x006400)
colDarkKhaki* = Color(0xBDB76B)
colDarkMagenta* = Color(0x8B008B)
colDarkOliveGreen* = Color(0x556B2F)
colDarkorange* = Color(0xFF8C00)
colDarkOrchid* = Color(0x9932CC)
colDarkRed* = Color(0x8B0000)
colDarkSalmon* = Color(0xE9967A)
colDarkSeaGreen* = Color(0x8FBC8F)
colDarkSlateBlue* = Color(0x483D8B)
colDarkSlateGray* = Color(0x2F4F4F)
colDarkTurquoise* = Color(0x00CED1)
colDarkViolet* = Color(0x9400D3)
colDeepPink* = Color(0xFF1493)
colDeepSkyBlue* = Color(0x00BFFF)
colDimGray* = Color(0x696969)
colDodgerBlue* = Color(0x1E90FF)
colFireBrick* = Color(0xB22222)
colFloralWhite* = Color(0xFFFAF0)
colForestGreen* = Color(0x228B22)
colFuchsia* = Color(0xFF00FF)
colGainsboro* = Color(0xDCDCDC)
colGhostWhite* = Color(0xF8F8FF)
colGold* = Color(0xFFD700)
colGoldenRod* = Color(0xDAA520)
colGray* = Color(0x808080)
colGreen* = Color(0x008000)
colGreenYellow* = Color(0xADFF2F)
colHoneyDew* = Color(0xF0FFF0)
colHotPink* = Color(0xFF69B4)
colIndianRed* = Color(0xCD5C5C)
colIndigo* = Color(0x4B0082)
colIvory* = Color(0xFFFFF0)
colKhaki* = Color(0xF0E68C)
colLavender* = Color(0xE6E6FA)
colLavenderBlush* = Color(0xFFF0F5)
colLawnGreen* = Color(0x7CFC00)
colLemonChiffon* = Color(0xFFFACD)
colLightBlue* = Color(0xADD8E6)
colLightCoral* = Color(0xF08080)
colLightCyan* = Color(0xE0FFFF)
colLightGoldenRodYellow* = Color(0xFAFAD2)
colLightGrey* = Color(0xD3D3D3)
colLightGreen* = Color(0x90EE90)
colLightPink* = Color(0xFFB6C1)
colLightSalmon* = Color(0xFFA07A)
colLightSeaGreen* = Color(0x20B2AA)
colLightSkyBlue* = Color(0x87CEFA)
colLightSlateGray* = Color(0x778899)
colLightSteelBlue* = Color(0xB0C4DE)
colLightYellow* = Color(0xFFFFE0)
colLime* = Color(0x00FF00)
colLimeGreen* = Color(0x32CD32)
colLinen* = Color(0xFAF0E6)
colMagenta* = Color(0xFF00FF)
colMaroon* = Color(0x800000)
colMediumAquaMarine* = Color(0x66CDAA)
colMediumBlue* = Color(0x0000CD)
colMediumOrchid* = Color(0xBA55D3)
colMediumPurple* = Color(0x9370D8)
colMediumSeaGreen* = Color(0x3CB371)
colMediumSlateBlue* = Color(0x7B68EE)
colMediumSpringGreen* = Color(0x00FA9A)
colMediumTurquoise* = Color(0x48D1CC)
colMediumVioletRed* = Color(0xC71585)
colMidnightBlue* = Color(0x191970)
colMintCream* = Color(0xF5FFFA)
colMistyRose* = Color(0xFFE4E1)
colMoccasin* = Color(0xFFE4B5)
colNavajoWhite* = Color(0xFFDEAD)
colNavy* = Color(0x000080)
colOldLace* = Color(0xFDF5E6)
colOlive* = Color(0x808000)
colOliveDrab* = Color(0x6B8E23)
colOrange* = Color(0xFFA500)
colOrangeRed* = Color(0xFF4500)
colOrchid* = Color(0xDA70D6)
colPaleGoldenRod* = Color(0xEEE8AA)
colPaleGreen* = Color(0x98FB98)
colPaleTurquoise* = Color(0xAFEEEE)
colPaleVioletRed* = Color(0xD87093)
colPapayaWhip* = Color(0xFFEFD5)
colPeachPuff* = Color(0xFFDAB9)
colPeru* = Color(0xCD853F)
colPink* = Color(0xFFC0CB)
colPlum* = Color(0xDDA0DD)
colPowderBlue* = Color(0xB0E0E6)
colPurple* = Color(0x800080)
colRed* = Color(0xFF0000)
colRosyBrown* = Color(0xBC8F8F)
colRoyalBlue* = Color(0x4169E1)
colSaddleBrown* = Color(0x8B4513)
colSalmon* = Color(0xFA8072)
colSandyBrown* = Color(0xF4A460)
colSeaGreen* = Color(0x2E8B57)
colSeaShell* = Color(0xFFF5EE)
colSienna* = Color(0xA0522D)
colSilver* = Color(0xC0C0C0)
colSkyBlue* = Color(0x87CEEB)
colSlateBlue* = Color(0x6A5ACD)
colSlateGray* = Color(0x708090)
colSnow* = Color(0xFFFAFA)
colSpringGreen* = Color(0x00FF7F)
colSteelBlue* = Color(0x4682B4)
colTan* = Color(0xD2B48C)
colTeal* = Color(0x008080)
colThistle* = Color(0xD8BFD8)
colTomato* = Color(0xFF6347)
colTurquoise* = Color(0x40E0D0)
colViolet* = Color(0xEE82EE)
colWheat* = Color(0xF5DEB3)
colWhite* = Color(0xFFFFFF)
colWhiteSmoke* = Color(0xF5F5F5)
colYellow* = Color(0xFFFF00)
colYellowGreen* = Color(0x9ACD32)
colorNames = [
("aliceblue", colAliceBlue),
@@ -367,11 +369,11 @@ const
("yellow", colYellow),
("yellowgreen", colYellowGreen)]
proc `$`*(c: TColor): string =
proc `$`*(c: Color): string =
## converts a color into its textual representation. Example: ``#00FF00``.
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 =
var a = 0
var b = len(x) - 1
@@ -383,14 +385,14 @@ proc binaryStrSearch(x: openarray[tuple[name: string, col: TColor]],
else: return mid
result = - 1
proc parseColor*(name: string): TColor =
proc parseColor*(name: string): Color =
## parses `name` to a color value. If no valid color could be
## parsed ``EInvalidValue`` is raised.
if name[0] == '#':
result = TColor(parseHexInt(name))
result = Color(parseHexInt(name))
else:
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]
proc isColor*(name: string): bool =
@@ -403,7 +405,7 @@ proc isColor*(name: string): bool =
else:
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.
result = rawRGB(r, g, b)

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2010 Andreas Rumpf
#
# See the file "copying.txt", included in this
@@ -25,9 +25,11 @@ const
type
TComplex* = tuple[re, im: float]
Complex* = tuple[re, im: float]
## a complex number, consisting of a real and an imaginary part
{.deprecated: [TComplex: Complex].}
proc `==` *(x, y: TComplex): bool =
## Compare two complex numbers `x` and `y` for equality.
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
#
# 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
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Implements Nimrod's 'spawn'.
## Implements Nim's 'spawn'.
when not compileOption("threads"):
{.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
#
# See the file "copying.txt", included in this
@@ -11,7 +11,7 @@
import strtabs, times
proc parseCookies*(s: string): PStringTable =
proc parseCookies*(s: string): StringTableRef =
## parses cookies into a string table.
result = newStringTable(modeCaseInsensitive)
var i = 0
@@ -39,7 +39,7 @@ proc setCookie*(key, value: string, domain = "", path = "",
if path != "": result.add("; Path=" & path)
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 =
## Creates a command in the format of
## ``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)
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()))

View File

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

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
#
# 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
#
# 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
## 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 genericargs: TEventArgs
## proc handleevent(e: TEventArgs) =
## var genericargs: EventArgs
## proc handleevent(e: EventArgs) =
## echo("Handled!")
##
## # Python way
@@ -27,21 +27,24 @@
## # C/Java way
## # Declare a type
## type
## TSomeObject = object of TObject
## SomeEvent: TEventHandler
## var myobj: TSomeObject
## SomeObject = object of RootObj
## SomeEvent: EventHandler
## var myobj: SomeObject
## myobj.SomeEvent = initEventHandler("SomeEvent")
## myobj.SomeEvent.addHandler(handleevent)
## ee.emit(myobj.SomeEvent, genericargs)
type
TEventArgs* = object of TObject ## 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.
EventArgs* = object of RootObj ## Base object for event arguments that are passed to callback functions.
EventHandler* = tuple[name: string, handlers: seq[proc(e: EventArgs) {.closure.}]] ## An eventhandler for an event.
type
TEventEmitter* = object {.pure, final.} ## An object that fires events and holds event handlers for an object.
s: seq[TEventHandler]
EInvalidEvent* = object of EInvalidValue
EventEmitter* = object ## An object that fires events and holds event handlers for an object.
s: seq[EventHandler]
EventError* = object of ValueError
{.deprecated: [TEventArgs: EventArgs, TEventHandler: EventHandler,
TEventEmitter: EventEmitter, EInvalidEvent: EventError].}
proc initEventHandler*(name: string): TEventHandler =
## 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
#
# See the file "copying.txt", included in this
@@ -25,13 +25,13 @@ else:
import inotify, os, asyncio, tables
type
PFSMonitor* = ref TFSMonitor
TFSMonitor = object of TObject
FSMonitor* = ref FSMonitorObj
FSMonitorObj = object of RootObj
fd: cint
handleEvent: proc (m: PFSMonitor, ev: TMonitorEvent) {.closure.}
handleEvent: proc (m: FSMonitor, ev: MonitorEvent) {.closure.}
targets: TTable[cint, string]
TMonitorEventType* = enum ## Monitor event type
MonitorEventType* = enum ## Monitor event type
MonitorAccess, ## File was accessed.
MonitorAttrib, ## Metadata changed.
MonitorCloseWrite, ## Writtable file was closed.
@@ -45,8 +45,8 @@ type
MonitorOpen, ## File was opened.
MonitorAll ## Filter for all event types.
TMonitorEvent* = object
case kind*: TMonitorEventType ## Type of the event.
MonitorEvent* = object
case kind*: MonitorEventType ## Type of the event.
of MonitorMoveSelf, MonitorMoved:
oldPath*: string ## Old absolute location
newPath*: string ## New absolute location
@@ -58,6 +58,9 @@ type
## watched.
wd*: cint ## Watch descriptor.
{.deprecated: [PFSMonitor: FSMonitor, TFSMonitor: FSMonitorObj,
TMonitorEventType: MonitorEventType, TMonitorEvent: MonitorEvent].}
const
MaxEvents = 100
@@ -67,7 +70,7 @@ proc newMonitor*(): PFSMonitor =
result.targets = initTable[cint, string]()
result.fd = inotifyInit()
if result.fd < 0:
OSError(OSLastError())
raiseOSError(osLastError())
proc add*(monitor: PFSMonitor, target: string,
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 inotifyRmWatch(monitor.fd, wd) < 0:
OSError(OSLastError())
raiseOSError(osLastError())
proc getEvent(m: PFSMonitor, fd: cint): seq[TMonitorEvent] =
result = @[]
@@ -197,18 +200,19 @@ proc register*(d: PDispatcher, monitor: PFSMonitor,
d.register(deleg)
when isMainModule:
var disp = newDispatcher()
var monitor = newMonitor()
echo monitor.add("/home/dom/inotifytests/")
disp.register(monitor,
proc (m: PFSMonitor, ev: TMonitorEvent) =
echo("Got event: ", ev.kind)
if ev.kind == MonitorMoved:
echo("From ", ev.oldPath, " to ", ev.newPath)
echo("Name is ", ev.name)
else:
echo("Name ", ev.name, " fullname ", ev.fullName))
while true:
if not disp.poll(): break
proc main =
var disp = newDispatcher()
var monitor = newMonitor()
echo monitor.add("/home/dom/inotifytests/")
disp.register(monitor,
proc (m: PFSMonitor, ev: TMonitorEvent) =
echo("Got event: ", ev.kind)
if ev.kind == MonitorMoved:
echo("From ", ev.oldPath, " to ", ev.newPath)
echo("Name is ", ev.name)
else:
echo("Name ", ev.name, " fullname ", ev.fullName))
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
# See the file "copying.txt", included in this
# 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>`_.
##
## This module provides both a synchronous and asynchronous implementation.
## The asynchronous implementation requires you to use the ``AsyncFTPClient``
## function. You are then required to register the ``PAsyncFTPClient`` with a
## The asynchronous implementation requires you to use the ``asyncFTPClient``
## function. You are then required to register the ``AsyncFTPClient`` with a
## asyncio dispatcher using the ``register`` function. Take a look at the
## 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:
##
## .. code-block:: Nimrod
## var ftp = FTPClient("example.org", user = "user", pass = "pass")
## .. code-block:: Nim
## var ftp = ftpClient("example.org", user = "user", pass = "pass")
## ftp.connect()
## ftp.retrFile("file.ext", "file.ext")
##
## **Warning:** The API of this module is unstable, and therefore is subject
## to change.
type
TFTPClient* = object of TObject
FTPClientObj* = object of RootObj
case isAsync: bool
of false:
csock: TSocket # Command connection socket
dsock: TSocket # Data connection socket
csock: Socket # Command connection socket
dsock: Socket # Data connection socket
else:
dummyA, dummyB: pointer # workaround a Nimrod API issue
asyncCSock: PAsyncSocket
asyncDSock: PAsyncSocket
handleEvent*: proc (ftp: PAsyncFTPClient, ev: TFTPEvent){.closure,gcsafe.}
disp: PDispatcher
asyncDSockID: PDelegate
asyncCSock: AsyncSocket
asyncDSock: AsyncSocket
handleEvent*: proc (ftp: AsyncFTPClient, ev: FTPEvent){.closure,gcsafe.}
disp: Dispatcher
asyncDSockID: Delegate
user, pass: string
address: string
port: TPort
port: Port
jobInProgress: bool
job: ref TFTPJob
job: ref FTPJob
dsockConnected: bool
PFTPClient* = ref TFTPClient
FTPClient* = ref FTPClientObj
FTPJobType* = enum
JRetrText, JRetr, JStore
TFTPJob = object
FTPJob = object
prc: proc (ftp: PFTPClient, async: bool): bool {.nimcall, gcsafe.}
case typ*: FTPJobType
of JRetrText:
lines: string
of JRetr, JStore:
file: TFile
file: File
filename: string
total: biggestInt # In bytes.
progress: biggestInt # In bytes.
oneSecond: biggestInt # Bytes transferred in one second.
total: BiggestInt # In bytes.
progress: BiggestInt # In bytes.
oneSecond: BiggestInt # Bytes transferred in one second.
lastProgressReport: float # Time
toStore: string # Data left to upload (Only used with async)
else: nil
PAsyncFTPClient* = ref TAsyncFTPClient ## Async alternative to TFTPClient.
TAsyncFTPClient* = object of TFTPClient
AsyncFTPClient* = ref AsyncFTPClientObj ## Async alternative to TFTPClient.
AsyncFTPClientObj* = object of FTPClientObj
FTPEventType* = enum
EvTransferProgress, EvLines, EvRetr, EvStore
TFTPEvent* = object ## Event
FTPEvent* = object ## Event
filename*: string
case typ*: FTPEventType
of EvLines:
@@ -89,13 +88,19 @@ type
of EvRetr, EvStore: ## Retr/Store operation finished.
nil
of EvTransferProgress:
bytesTotal*: biggestInt ## Bytes total.
bytesFinished*: biggestInt ## Bytes transferred.
speed*: biggestInt ## Speed in bytes/s
bytesTotal*: BiggestInt ## Bytes total.
bytesFinished*: BiggestInt ## Bytes transferred.
speed*: BiggestInt ## Speed in bytes/s
currentJob*: FTPJobType ## The current job being performed.
EInvalidReply* = object of ESynch
EFTP* = object of ESynch
ReplyError* = object of IOError
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),
user, pass = ""): PFTPClient =

View File

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

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
@@ -8,13 +8,13 @@
#
## 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 <#!$>`_
## 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
## code:
##
## .. code-block:: nimrod
## .. code-block:: Nim
## proc hash(x: Something): THash =
## ## Computes a THash from `x`.
## var h: THash = 0
@@ -29,7 +29,7 @@
## like for example objects made up of ``strings``, you can simply hash
## together the hash value of the individual fields:
##
## .. code-block:: nimrod
## .. code-block:: Nim
## proc hash(x: Something): THash =
## ## Computes a THash from `x`.
## var h: THash = 0

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2014 Andreas Rumpf
#
# See the file "copying.txt", included in this
@@ -18,13 +18,13 @@
##
## Example:
##
## .. code-block:: nimrod
## var nim = "Nimrod"
## echo h1(a(href="http://nimrod-lang.org", nim))
## .. code-block:: Nim
## var nim = "Nim"
## echo h1(a(href="http://nim-lang.org", nim))
##
## Writes the string::
##
## <h1><a href="http://nimrod-lang.org">Nimrod</a></h1>
## <h1><a href="http://nim-lang.org">Nim</a></h1>
##
import
@@ -53,8 +53,7 @@ proc delete[T](s: var seq[T], attr: T): bool =
setLen(s, L-1)
result = true
proc xmlCheckedTag*(e: PNimrodNode, tag: string,
optAttr = "", reqAttr = "",
proc xmlCheckedTag*(e: PNimrodNode, tag: string, optAttr = "", reqAttr = "",
isLeaf = false): PNimrodNode {.compileTime.} =
## use this procedure to define a new XML tag
@@ -484,7 +483,7 @@ macro `var`*(e: expr): expr {.immediate.} =
result = xmlCheckedTag(e, "var", commonAttr)
when isMainModule:
var nim = "Nimrod"
echo h1(a(href="http://nimrod-code.org", nim))
var nim = "Nim"
echo h1(a(href="http://nim-lang.org", nim))
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
#
# 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
## document (well, if you are lucky):
##
## .. code-block:: nimrod
## .. code-block:: Nim
##
## echo loadHtml("mydirty.html")
##
@@ -29,7 +29,7 @@
## and write back the modified version. In this case we look for hyperlinks
## ending with the extension ``.rst`` and convert them to ``.html``.
##
## .. code-block:: nimrod
## .. code-block:: Nim
##
## import htmlparser
## 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
#
# See the file "copying.txt", included in this
@@ -21,7 +21,7 @@
## This example uses HTTP GET to retrieve
## ``http://google.com``
##
## .. code-block:: nimrod
## .. code-block:: Nim
## echo(getContent("http://google.com"))
##
## Using HTTP POST
@@ -31,7 +31,7 @@
## uses ``multipart/form-data`` as the ``Content-Type`` to send the HTML to
## the server.
##
## .. code-block:: nimrod
## .. code-block:: Nim
## var headers: string = "Content-Type: multipart/form-data; boundary=xyz\c\L"
## var body: string = "--xyz\c\L"
## # soap 1.2 output
@@ -54,7 +54,7 @@
## on many operating systems. httpclient will use SSL automatically if you give
## 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:
## ``nimrod c -d:ssl ...``.
## ``nim c -d:ssl ...``.
##
## Timeouts
## ========
@@ -80,34 +80,38 @@ import asyncnet, asyncdispatch
import rawsockets
type
TResponse* = tuple[
Response* = tuple[
version: string,
status: string,
headers: PStringTable,
headers: StringTableRef,
body: string]
PProxy* = ref object
Proxy* = ref object
url*: TUrl
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
## protocol
EHttpRequestErr* = object of ESynch ## Thrown in the ``getContent`` proc
## and ``postContent`` proc,
## when the server returns an error
HttpRequestError* = object of IOError ## Thrown in the ``getContent`` proc
## and ``postContent`` proc,
## 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) =
var e: ref EInvalidProtocol
var e: ref ProtocolError
new(e)
e.msg = msg
raise e
proc fileError(msg: string) =
var e: ref EIO
var e: ref IOError
new(e)
e.msg = msg
raise e
@@ -232,7 +236,7 @@ proc parseResponse(s: TSocket, getBody: bool, timeout: int): TResponse =
result.body = ""
type
THttpMethod* = enum ## the requested HttpMethod
HttpMethod* = enum ## the requested HttpMethod
httpHEAD, ## Asks for the response identical to the one that would
## correspond to a GET request, but without the response
## body.
@@ -250,6 +254,8 @@ type
httpCONNECT ## Converts the request connection to a transparent
## TCP/IP tunnel, usually used for proxies.
{.deprecated: [THttpMethod: HttpMethod].}
when not defined(ssl):
type PSSLContext = ref object
let defaultSSLContext: PSSLContext = nil
@@ -288,7 +294,7 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
add(headers, "\c\L")
var s = socket()
if s == InvalidSocket: osError(osLastError())
if s == InvalidSocket: raiseOSError(osLastError())
var port = sockets.TPort(80)
if r.scheme == "https":
when defined(ssl):
@@ -431,16 +437,18 @@ proc generateHeaders(r: TURL, httpMethod: THttpMethod,
add(result, "\c\L")
type
PAsyncHttpClient* = ref object
socket: PAsyncSocket
AsyncHttpClient* = ref object
socket: AsyncSocket
connected: bool
currentURL: TURL ## Where we are currently connected.
headers: PStringTable
headers: StringTableRef
maxRedirects: int
userAgent: string
{.deprecated: [PAsyncHttpClient: AsyncHttpClient].}
proc newAsyncHttpClient*(userAgent = defUserAgent,
maxRedirects = 5): PAsyncHttpClient =
maxRedirects = 5): AsyncHttpClient =
## Creates a new PAsyncHttpClient instance.
##
## ``userAgent`` specifies the user agent that will be used when making
@@ -453,7 +461,7 @@ proc newAsyncHttpClient*(userAgent = defUserAgent,
result.userAgent = defUserAgent
result.maxRedirects = maxRedirects
proc close*(client: PAsyncHttpClient) =
proc close*(client: AsyncHttpClient) =
## Closes any connections held by the HTTP client.
if client.connected:
client.socket.close()

View File

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

View File

@@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2012 Andreas Rumpf, Dominik Picheta
# Nim's Runtime Library
# (c) Copyright 2014 Andreas Rumpf, Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -35,7 +35,7 @@ import
hashes, strutils, lexbase, streams, unicode
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
jsonEof, ## end of file reached
jsonString, ## a string literal
@@ -65,7 +65,7 @@ type
tkColon,
tkComma
TJsonError* = enum ## enumeration that lists all errors that can occur
JsonError* = enum ## enumeration that lists all errors that can occur
errNone, ## no error
errInvalidToken, ## invalid token
errStringExpected, ## string expected
@@ -78,20 +78,23 @@ type
errEofExpected, ## EOF expected
errExprExpected ## expr expected
TParserState = enum
ParserState = enum
stateEof, stateStart, stateObject, stateArray, stateExpectArrayComma,
stateExpectObjectComma, stateExpectColon, stateExpectValue
TJsonParser* = object of TBaseLexer ## the parser object.
JsonParser* = object of TBaseLexer ## the parser object.
a: string
tok: TTokKind
kind: TJsonEventKind
err: TJsonError
state: seq[TParserState]
kind: JsonEventKind
err: JsonError
state: seq[ParserState]
filename: string
{.deprecated: [TJsonEventKind: JsonEventKind, TJsonError: JsonError,
TJsonParser: JsonParser].}
const
errorMessages: array [TJsonError, string] = [
errorMessages: array [JsonError, string] = [
"no error",
"invalid token",
"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
## for nice error messages.
lexbase.open(my, input)
@@ -125,49 +128,49 @@ proc open*(my: var TJsonParser, input: PStream, filename: string) =
my.kind = jsonError
my.a = ""
proc close*(my: var TJsonParser) {.inline.} =
proc close*(my: var JsonParser) {.inline.} =
## closes the parser `my` and its associated input stream.
lexbase.close(my)
proc str*(my: TJsonParser): string {.inline.} =
proc str*(my: JsonParser): string {.inline.} =
## returns the character data for the events: ``jsonInt``, ``jsonFloat``,
## ``jsonString``
assert(my.kind in {jsonInt, jsonFloat, jsonString})
return my.a
proc getInt*(my: TJsonParser): BiggestInt {.inline.} =
proc getInt*(my: JsonParser): BiggestInt {.inline.} =
## returns the number for the event: ``jsonInt``
assert(my.kind == jsonInt)
return parseBiggestInt(my.a)
proc getFloat*(my: TJsonParser): float {.inline.} =
proc getFloat*(my: JsonParser): float {.inline.} =
## returns the number for the event: ``jsonFloat``
assert(my.kind == jsonFloat)
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
return my.kind
proc getColumn*(my: TJsonParser): int {.inline.} =
proc getColumn*(my: JsonParser): int {.inline.} =
## get the current column the parser has arrived at.
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.
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.
result = my.filename
proc errorMsg*(my: TJsonParser): string =
proc errorMsg*(my: JsonParser): string =
## returns a helpful error message for the event ``jsonError``
assert(my.kind == jsonError)
result = "$1($2, $3) Error: $4" % [
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
## other error messages
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)
else: result = false # error
proc parseString(my: var TJsonParser): TTokKind =
proc parseString(my: var JsonParser): TTokKind =
result = tkString
var pos = my.bufpos + 1
var buf = my.buf
@@ -239,7 +242,7 @@ proc parseString(my: var TJsonParser): TTokKind =
inc(pos)
my.bufpos = pos # store back
proc skip(my: var TJsonParser) =
proc skip(my: var JsonParser) =
var pos = my.bufpos
var buf = my.buf
while true:
@@ -297,7 +300,7 @@ proc skip(my: var TJsonParser) =
break
my.bufpos = pos
proc parseNumber(my: var TJsonParser) =
proc parseNumber(my: var JsonParser) =
var pos = my.bufpos
var buf = my.buf
if buf[pos] == '-':
@@ -328,7 +331,7 @@ proc parseNumber(my: var TJsonParser) =
inc(pos)
my.bufpos = pos
proc parseName(my: var TJsonParser) =
proc parseName(my: var JsonParser) =
var pos = my.bufpos
var buf = my.buf
if buf[pos] in IdentStartChars:
@@ -337,7 +340,7 @@ proc parseName(my: var TJsonParser) =
inc(pos)
my.bufpos = pos
proc getTok(my: var TJsonParser): TTokKind =
proc getTok(my: var JsonParser): TTokKind =
setLen(my.a, 0)
skip(my) # skip whitespace, comments
case my.buf[my.bufpos]
@@ -381,7 +384,7 @@ proc getTok(my: var TJsonParser): TTokKind =
result = tkError
my.tok = result
proc next*(my: var TJsonParser) =
proc next*(my: var JsonParser) =
## retrieves the first/next event. This controls the parser.
var tk = getTok(my)
var i = my.state.len-1
@@ -529,7 +532,10 @@ type
of JArray:
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.} =
## raises an `EJsonParsingError` exception.

View File

@@ -1,6 +1,6 @@
#
#
# The Nimrod Compiler
# The Nim Compiler
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
@@ -36,6 +36,8 @@ type
lineStart: int # index of last line start in buffer
fileOpened: bool
{.deprecated: [TBaseLexer: BaseLexer].}
proc open*(L: var TBaseLexer, input: PStream, bufLen: int = 8192)
## 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
#
# See the file "copying.txt", included in this
@@ -26,7 +26,7 @@
## The following example demonstrates logging to three different handlers
## simultaneously:
##
## .. code-block:: nimrod
## .. code-block:: nim
##
## var L = newConsoleLogger()
## var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
@@ -42,7 +42,7 @@
import strutils, os, times
type
TLevel* = enum ## logging level
Level* = enum ## logging level
lvlAll, ## all levels active
lvlDebug, ## debug level (and any above) active
lvlInfo, ## info level (and any above) active
@@ -52,7 +52,7 @@ type
lvlNone ## no levels active
const
LevelNames*: array [TLevel, string] = [
LevelNames*: array [Level, string] = [
"DEBUG", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "NONE"
]
@@ -60,26 +60,29 @@ const
verboseFmtStr* = "$date $time "
type
PLogger* = ref object of PObject ## abstract logger; the base type of all loggers
levelThreshold*: TLevel ## only messages of level >= levelThreshold
Logger* = ref object of RootObj ## abstract logger; the base type of all loggers
levelThreshold*: Leve l ## only messages of level >= levelThreshold
## should be processed
fmtStr: string ## = defaultFmtStr by default, see substituteLog for $date etc.
PConsoleLogger* = ref object of PLogger ## logger that writes the messages to the
## console
ConsoleLogger* = ref object of Logger ## logger that writes the messages to the
## 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
PRollingFileLogger* = ref object of PFileLogger ## logger that writes the
## messages to a file and
## performs log rotation
RollingFileLogger* = ref object of FileLogger ## logger that writes the
## messages to a file and
## performs log rotation
maxLines: int # maximum number of lines
curLine : int
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...
{.deprecated: [TLevel: Level, PLogger: Logger, PConsoleLogger: ConsoleLogger,
PFileLogger: FileLogger, PRollingFileLogger: RollingFileLogger].}
proc substituteLog(frmt: string): string =
## converts $date to the current date
## converts $time to the current time
@@ -105,7 +108,7 @@ proc substituteLog(frmt: string): string =
of "appdir": result.add(app.splitFile.dir)
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].} =
## Override this method in custom loggers. Default implementation does
## nothing.

View File

@@ -1,20 +1,20 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2013 Andreas Rumpf
# Nim's Runtime Library
# (c) Copyright 2014 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## 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
## essentially that it does not work if the object has some other runtime
## type than its compiletime type:
##
## .. code-block:: nimrod
## .. code-block:: nim
##
## type
## TA = object
@@ -211,7 +211,7 @@ proc loadAny(p: var TJsonParser, a: TAny, t: var TTable[biggestInt, pointer]) =
raiseParseErr(p, "float expected")
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
open(p, s, "unknown file")
next(p)

View File

@@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
# Nim's Runtime Library
# (c) Copyright 2014 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -44,7 +44,7 @@ proc validEmailAddress*(s: string): bool {.noSideEffect,
"aero", "jobs", "museum": return true
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".} =
## parses `s` into an integer in the range `validRange`. If successful,
## `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
try:
discard parseutils.parseInt(s, x, 0)
except EOverflow:
except OverflowError:
discard
if x in validRange: value = x

View File

@@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
# Nim's Runtime Library
# (c) Copyright 2014 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -9,7 +9,7 @@
## 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
## <backends.html#the-javascript-target>`_.
@@ -41,8 +41,8 @@ const
## for Nimrod's ``float`` type.
type
TFloatClass* = enum ## describes the class a floating point value belongs to.
## This is the type that is returned by `classify`.
FloatClass* = enum ## describes the class a floating point value belongs to.
## This is the type that is returned by `classify`.
fcNormal, ## value is an ordinary nonzero floating point value
fcSubnormal, ## value is a subnormal (a very small) floating point value
fcZero, ## value is zero
@@ -51,10 +51,10 @@ type
fcInf, ## value is positive 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
## `TFloatClass`.
## `FloatClass`.
# JavaScript and most C compilers have no classify:
if x == 0.0:
if 1.0/x == Inf:
@@ -287,12 +287,14 @@ proc random[T](a: openArray[T]): T =
result = a[random(a.low..a.len)]
type
TRunningStat* {.pure,final.} = object ## an accumulator for statistical data
n*: int ## number of pushed data
sum*, min*, max*, mean*: float ## self-explaining
RunningStat* = object ## an accumulator for statistical data
n*: int ## number of pushed data
sum*, min*, max*, mean*: float ## self-explaining
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
inc(s.n)
# 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.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``
## and the other push operation is called.
push(s, toFloat(x))
proc variance*(s: TRunningStat): float =
proc variance*(s: RunningStat): float =
## computes the current variance of `s`
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`
result = sqrt(variance(s))

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2010 Andreas Rumpf
#
# 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
#
# See the file "copying.txt", included in this
@@ -22,7 +22,7 @@ else:
import os
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
## can be used directly to change the contents of the
## file, if it was opened with write access.
@@ -34,8 +34,9 @@ type
else:
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 =
var readonly = mode == fmRead
when defined(windows):
@@ -60,27 +61,27 @@ proc mapMem*(m: var TMemFile, mode: FileMode = fmRead,
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`.
## 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
## via ``mapMem``.
when defined(windows):
if unmapViewOfFile(p) == 0: osError(osLastError())
if unmapViewOfFile(p) == 0: raiseOSError(osLastError())
else:
if munmap(p, size) != 0: osError(osLastError())
if munmap(p, size) != 0: raiseOSError(osLastError())
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.
## `newFileSize` can only be set if the file does not exist and is opened
## with write access (e.g., with fmReadWrite). `mappedSize` and `offset`
## can be used to map only a slice of the file. Example:
##
## .. code-block:: nimrod
## .. code-block:: nim
## 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.close()
@@ -100,11 +101,11 @@ proc open*(filename: string, mode: FileMode = fmRead,
result.size = 0
when defined(windows):
template fail(errCode: TOSErrorCode, msg: expr) =
template fail(errCode: OSErrorCode, msg: expr) =
rollback()
if result.fHandle != 0: discard closeHandle(result.fHandle)
if result.mapHandle != 0: discard closeHandle(result.mapHandle)
osError(errCode)
raiseOSError(errCode)
# return false
#raise newException(EIO, msg)
@@ -169,10 +170,10 @@ proc open*(filename: string, mode: FileMode = fmRead,
else: result.size = fileSize.int
else:
template fail(errCode: TOSErrorCode, msg: expr) =
template fail(errCode: OSErrorCode, msg: expr) =
rollback()
if result.handle != 0: discard close(result.handle)
osError(errCode)
raiseOSError(errCode)
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):
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
## file system, if `f` was opened with write access.
var error = false
var lastErr: TOSErrorCode
var lastErr: OSErrorCode
when defined(windows):
if f.fHandle != INVALID_HANDLE_VALUE:
@@ -242,5 +243,5 @@ proc close*(f: var TMemFile) =
else:
f.handle = 0
if error: osError(lastErr)
if error: raiseOSError(lastErr)

View File

@@ -1,24 +1,26 @@
import unsigned
type
TMersenneTwister* = object
MersenneTwister* = object
mt: array[0..623, uint32]
index: int
proc newMersenneTwister*(seed: int): TMersenneTwister =
{.deprecated: [TMersenneTwister: MersenneTwister].}
proc newMersenneTwister*(seed: int): MersenneTwister =
result.index = 0
result.mt[0]= uint32(seed)
for i in 1..623'u32:
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:
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)
if (y mod 2'u32) != 0:
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:
generateNumbers(m)
var y = m.mt[m.index]
@@ -29,11 +31,9 @@ proc getNum*(m: var TMersenneTwister): int =
m.index = (m.index+1) mod 624
return int(y)
# Test
when isMainModule:
var mt = newMersenneTwister(2525)
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
#
# See the file "copying.txt", included in this
@@ -10,8 +10,10 @@
## This module implements a mimetypes database
import strtabs
type
TMimeDB* = object
mimes: PStringTable
MimeDB* = object
mimes: StringTableRef
{.deprecated: [TMimeDB: MimeDB].}
const mimes* = {
"ez": "application/andrew-inset",
@@ -489,20 +491,19 @@ const mimes* = {
"vrml": "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
## common mimetypes.
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``
## could not be found.
result = mimedb.mimes[ext]
if result == "":
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
## ``mimetype`` could not be found. Extensions are returned without the
## leading dot.
@@ -511,11 +512,11 @@ proc getExt*(mimedb: TMimeDB, mimetype: string, default = "txt"): string =
if m == mimetype:
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``.
mimedb.mimes[ext] = mimetype
when isMainModule:
var m = newMimetypes()
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
#
# See the file "copying.txt", included in this
@@ -11,7 +11,7 @@
{.deadCodeElim: on.}
import rawsockets, os, strutils, unsigned, parseutils, times
export TPort, `$`, `==`
export Port, `$`, `==`
const useWinVersion = defined(Windows) or defined(nimdoc)
@@ -22,25 +22,25 @@ when defined(ssl):
when defined(ssl):
type
ESSL* = object of ESynch
SSLError* = object of Exception
TSSLCVerifyMode* = enum
SSLCVerifyMode* = enum
CVerifyNone, CVerifyPeer
TSSLProtVersion* = enum
SSLProtVersion* = enum
protSSLv2, protSSLv3, protTLSv1, protSSLv23
PSSLContext* = distinct PSSLCTX
SSLContext* = distinct PSSLCTX
TSSLAcceptResult* = enum
SSLAcceptResult* = enum
AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess
const
BufferSize*: int = 4000 ## size of a buffered socket's buffer
type
TSocketImpl* = object ## socket type
fd*: TSocketHandle
SocketImpl* = object ## socket type
fd*: SocketHandle
case isBuffered*: bool # determines whether this socket is buffered.
of true:
buffer*: array[0..BufferSize, char]
@@ -57,34 +57,40 @@ type
sslPeekChar*: char
of false: nil
PSocket* = ref TSocketImpl
Socket* = ref SocketImpl
TSOBool* = enum ## Boolean socket options.
SOBool* = enum ## Boolean socket options.
OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive,
OptOOBInline, OptReuseAddr
TReadLineResult* = enum ## result for readLineAsync
ReadLineResult* = enum ## result for readLineAsync
ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone
ETimeout* = object of ESynch
TimeoutError* = object of Exception
TSocketFlags* {.pure.} = enum
SocketFlag* {.pure.} = enum
Peek,
SafeDisconn ## Ensures disconnection exceptions (ECONNRESET, EPIPE etc) are not thrown.
proc isDisconnectionError*(flags: set[TSocketFlags],
lastError: TOSErrorCode): bool =
{.deprecated: [TSocketFlags: SocketFlag, ETimeout: TimeoutError,
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
## if flags contains ``SafeDisconn``.
when useWinVersion:
TSocketFlags.SafeDisconn in flags and
SocketFlag.SafeDisconn in flags and
lastError.int32 in {WSAECONNRESET, WSAECONNABORTED, WSAENETRESET,
WSAEDISCON, ERROR_NETNAME_DELETED}
else:
TSocketFlags.SafeDisconn in flags and
SocketFlag.SafeDisconn in flags and
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.
for f in socketFlags:
case f

View File

@@ -1,13 +1,13 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# 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
## report at program exit.
@@ -64,7 +64,7 @@ when withThreads:
var
profilingLock: TLock
InitLock profilingLock
initLock profilingLock
proc hookAux(st: TStackTrace, costs: int) =
# this is quite performance sensitive!

View File

@@ -1,16 +1,17 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2013 Robert Persson
#
# See the file "copying.txt", included in this
# 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]=
## Searches `function` for a root between `xmin` and `xmax`
## 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
#
# See the file "copying.txt", included in this
# 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
## produce a globally distributed unique ID. This implementation was extracted
## from the Mongodb interface and it thus binary compatible with a Mongo OID.
@@ -18,11 +18,13 @@
import times, endians
type
Toid* {.pure, final.} = object ## an OID
Oid* = object ## an OID
time: int32 ##
fuzz: int32 ##
count: int32 ##
{.deprecated: [Toid: Oid].}
proc hexbyte*(hex: char): int =
case hex
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)
else: discard
proc parseOid*(str: cstring): TOid =
proc parseOid*(str: cstring): Oid =
## parses an OID.
var bytes = cast[cstring](addr(result.time))
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]))
inc(i)
proc oidToString*(oid: TOid, str: cstring) =
proc oidToString*(oid: Oid, str: cstring) =
const hex = "0123456789abcdef"
# work around a compiler bug:
var str = str
@@ -52,7 +54,7 @@ proc oidToString*(oid: TOid, str: cstring) =
inc(i)
str[24] = '\0'
proc `$`*(oid: TOid): string =
proc `$`*(oid: Oid): string =
result = newString(25)
oidToString(oid, result)
@@ -60,7 +62,7 @@ var
incr: int
fuzz: int32
proc genOid*(): TOid =
proc genOid*(): Oid =
## generates a new OID.
proc rand(): cint {.importc: "rand", header: "<stdlib.h>", nodecl.}
proc gettime(dummy: ptr cint): cint {.importc: "time", header: "<time.h>".}
@@ -79,12 +81,12 @@ proc genOid*(): TOid =
result.fuzz = fuzz
bigEndian32(addr result.count, addr(i))
proc generatedTime*(oid: TOid): TTime =
proc generatedTime*(oid: Oid): Time =
## returns the generated timestamp of the OID.
var tmp: int32
var dummy = oid.time
bigEndian32(addr(tmp), addr(dummy))
result = TTime(tmp)
result = Time(tmp)
when isMainModule:
let xo = genOID()

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2014 Andreas Rumpf
#
# See the file "copying.txt", included in this
@@ -29,18 +29,23 @@ else:
include "system/ansi_c"
type
FReadEnv* = object of ReadIOEffect ## effect that denotes a read
## from an environment variable
FWriteEnv* = object of WriteIOEffect ## effect that denotes a write
## to an environment variable
ReadEnvEffect* = object of ReadIOEffect ## effect that denotes a read
## from an environment variable
WriteEnvEffect* = object of WriteIOEffect ## effect that denotes a write
## to an environment variable
FReadDir* = object of ReadIOEffect ## effect that denotes a write operation to
## the directory structure
FWriteDir* = object of WriteIOEffect ## effect that denotes a write operation to
## the directory structure
ReadDirEffect* = object of ReadIOEffect ## effect that denotes a write
## operation to the directory structure
WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write operation to
## 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
doslike = defined(windows) or defined(OS2) or defined(DOS)
# DOS-like filesystem
@@ -204,7 +209,8 @@ proc osErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} =
result = $os.strerror(errno)
{.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``.
## If ``msg == ""``, the operating system's error flag
## (``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)
{.pop.}
proc `==`*(err1, err2: TOSErrorCode): bool {.borrow.}
proc `$`*(err: TOSErrorCode): string {.borrow.}
{.deprecated: [osError: raiseOSError].}
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.
##
## The error code can be retrieved using the ``OSLastError`` proc.
@@ -249,10 +257,10 @@ proc osErrorMsg*(errorCode: TOSErrorCode): string =
result = $msgbuf
if msgbuf != nil: localFree(msgbuf)
else:
if errorCode != TOSErrorCode(0'i32):
if errorCode != OSErrorCode(0'i32):
result = $os.strerror(errorCode.int32)
proc osError*(errorCode: TOSErrorCode) =
proc raiseOSError*(errorCode: OSErrorCode) =
## Raises an ``EOS`` exception. The ``errorCode`` will determine the
## message, ``OSErrorMsg`` will be used to get this message.
##
@@ -268,7 +276,7 @@ proc osError*(errorCode: TOSErrorCode) =
raise e
{.push stackTrace:off.}
proc osLastError*(): TOSErrorCode =
proc osLastError*(): OSErrorCode =
## Retrieves the last operating system error code.
##
## 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):
result = TOSErrorCode(getLastError())
else:
result = TOSErrorCode(errno)
result = OSErrorCode(errno)
{.pop.}
proc unixToNativePath*(path: string, drive=""): string {.
@@ -371,7 +379,7 @@ when defined(windows):
f.cFileName[1].int == dot and f.cFileName[2].int == 0)
proc existsFile*(filename: string): bool {.rtl, extern: "nos$1",
tags: [FReadDir].} =
tags: [ReadDirEffect].} =
## Returns true if the file exists, false otherwise.
when defined(windows):
when useWinUnicode:
@@ -384,7 +392,7 @@ proc existsFile*(filename: string): bool {.rtl, extern: "nos$1",
var res: TStat
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
## is returned.
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)
proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
tags: [FReadDir].} =
tags: [ReadDirEffect].} =
## Returns true iff the symlink `link` exists. Will return true
## regardless of whether the link points to a directory or file.
when defined(windows):
@@ -701,7 +709,7 @@ proc extractFilename*(path: string): string {.
result = splitPath(path).tail
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.
when defined(windows):
const bufsize = 3072'i32
@@ -800,7 +808,7 @@ when defined(Windows):
)
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
## file or directory. Raises an exception if any of the files does not
## exist or information about it can not be obtained.
@@ -883,7 +891,7 @@ type
fpOthersRead ## read access for others
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
## an error. On Windows, only the ``readonly`` flag is checked, every other
## permission is available in any case.
@@ -915,7 +923,7 @@ proc getFilePermissions*(filename: string): set[TFilePermission] {.
result = {fpUserExec..fpOthersRead}
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
## an error. On Windows, only the ``readonly`` flag is changed, depending on
## ``fpUserWrite``.
@@ -1014,7 +1022,7 @@ when defined(Windows):
template setFileAttributes(file, attrs: expr): expr {.immediate.} =
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
## if the file never existed in the first place.
## On Windows, ignores the read-only attribute.
@@ -1076,7 +1084,7 @@ when defined(windows):
while true:
var eend = strEnd(e)
add(environment, $e)
e = cast[WideCString](cast[ByteAddress](eend)+2)
e = cast[WideCString](cast[TAddress](eend)+2)
if eend[1].int == 0: break
discard freeEnvironmentStringsW(env)
else:
@@ -1130,7 +1138,7 @@ proc findEnvVar(key: string): int =
if startsWith(environment[i], temp): return i
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`.
##
## 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("")
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.
## Returns true if it exists, false otherwise.
if c_getenv(key) != nil: return true
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`.
## If an error occurs, `EInvalidEnvVar` is raised.
@@ -1175,7 +1183,7 @@ proc putEnv*(key, val: string) {.tags: [FWriteEnv].} =
else:
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
## of the tuple is the name of the current variable stored, in the second
## its value.
@@ -1185,7 +1193,7 @@ iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [FReadEnv].} =
yield (TaintedString(substr(environment[i], 0, 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
## the `glob`:idx: call.
##
@@ -1225,7 +1233,7 @@ type
pcLinkToDir ## path refers to a symbolic link to a directory
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
## `dir`. The component type and full path for each item is returned.
## Walking is not recursive.
@@ -1278,7 +1286,7 @@ iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] {.
discard closedir(d)
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
## full path for each file is returned.
## **Warning**:
@@ -1318,7 +1326,7 @@ proc rawRemoveDir(dir: string) =
if rmdir(dir) != 0'i32 and errno != ENOENT: osError(osLastError())
proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [
FWriteDir, FReadDir].} =
WriteDirEffect, ReadDirEffect].} =
## Removes the directory `dir` including all subdirectories and files
## in `dir` (recursively).
##
@@ -1345,7 +1353,7 @@ proc rawCreateDir(dir: string) =
if res == 0'i32 and getLastError() != 183'i32:
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`.
##
## The directory may contain several subdirectories that do not exist yet.
@@ -1568,7 +1576,7 @@ proc copyDirWithPermissions*(source, dest: string,
proc inclFilePermissions*(filename: string,
permissions: set[TFilePermission]) {.
rtl, extern: "nos$1", tags: [FReadDir, FWriteDir].} =
rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect].} =
## a convenience procedure for:
##
## .. code-block:: nimrod
@@ -1577,14 +1585,14 @@ proc inclFilePermissions*(filename: string,
proc exclFilePermissions*(filename: string,
permissions: set[TFilePermission]) {.
rtl, extern: "nos$1", tags: [FReadDir, FWriteDir].} =
rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect].} =
## a convenience procedure for:
##
## .. code-block:: nimrod
## 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.
##
## 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")) & "\\"
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.
when defined(windows): return string(getEnv("APPDATA")) & "\\"
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
## save temporary files in.
when defined(windows): return string(getEnv("TEMP")) & "\\"
@@ -1605,7 +1613,7 @@ proc getTempDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} =
when defined(nimdoc):
# 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
## application.
##
@@ -1625,7 +1633,7 @@ when defined(nimdoc):
## 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.
##
## `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)
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
## in directories listed in the ``PATH`` environment variable.
## 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")
## echo configFile
## # --> C:\Users\amber\appname.cfg
if len(path) > 1 and path[0] == '~' and (path[1] == '/' or path[1] == '\\'):
result = getHomeDir() / path[2..len(path)-1]
else:
@@ -1961,7 +1968,7 @@ proc getFileInfo*(handle: FileHandle): FileInfo =
rawToFormalFileInfo(rawInfo, result)
proc getFileInfo*(file: File): FileInfo =
result = getFileInfo(file.fileHandle())
result = getFileInfo(file.getFileHandle())
proc getFileInfo*(path: string, followSymlink = true): FileInfo =
## 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
#
# See the file "copying.txt", included in this
@@ -24,7 +24,7 @@ when defined(linux):
import linux
type
TProcess = object of RootObj
ProcessObj = object of RootObj
when defined(windows):
fProcessHandle: THandle
inHandle, outHandle, errHandle: FileHandle
@@ -35,9 +35,9 @@ type
id: TPid
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
poUsePath, ## Asks system to search for executable using PATH environment
## variable.
@@ -47,6 +47,9 @@ type
poStdErrToStdOut, ## merge stdout and stderr to the stdout stream
poParentStreams ## use the parent's streams
{.deprecated: [TProcess: ProcessObj, PProcess: Process,
TProcessOption: ProcessOption].}
const poUseShell* {.deprecated.} = poUsePath
## Deprecated alias for poUsePath.
@@ -125,7 +128,7 @@ proc startProcess*(command: string,
args: openArray[string] = [],
env: PStringTable = nil,
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
## process's working directory. If ``workingDir == ""`` the current directory
## 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] = {
poStdErrToStdOut, poUsePath}): PProcess {.
tags: [ExecIOEffect, FReadEnv], deprecated.} =
tags: [ExecIOEffect, ReadEnvEffect], deprecated.} =
## Deprecated - use `startProcess` directly.
result = startProcess(command=command, options=options + {poEvalCommand})
@@ -233,7 +236,7 @@ proc countProcessors*(): int {.rtl, extern: "nosp$1".} =
proc execProcesses*(cmds: openArray[string],
options = {poStdErrToStdOut, poParentStreams},
n = countProcessors()): int {.rtl, extern: "nosp$1",
tags: [ExecIOEffect, TimeEffect, FReadEnv]} =
tags: [ExecIOEffect, TimeEffect, ReadEnvEffect]} =
## executes the commands `cmds` in parallel. Creates `n` processes
## that execute in parallel. The highest return value of all processes
## is returned.
@@ -584,12 +587,12 @@ elif not defined(useNimRtl):
when not defined(useFork):
proc startProcessAuxSpawn(data: TStartProcessData): TPid {.
tags: [ExecIOEffect, FReadEnv], gcsafe.}
tags: [ExecIOEffect, ReadEnvEffect], gcsafe.}
proc startProcessAuxFork(data: TStartProcessData): TPid {.
tags: [ExecIOEffect, FReadEnv], gcsafe.}
tags: [ExecIOEffect, ReadEnvEffect], gcsafe.}
{.push stacktrace: off, profiler: off.}
proc startProcessAfterFork(data: ptr TStartProcessData) {.
tags: [ExecIOEffect, FReadEnv], cdecl, gcsafe.}
tags: [ExecIOEffect, ReadEnvEffect], cdecl, gcsafe.}
{.pop.}
proc startProcess(command: string,

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2010 Andreas Rumpf
#
# See the file "copying.txt", included in this
@@ -20,24 +20,24 @@
## The file ``examples/parsecfgex.nim`` demonstrates how to use the
## configuration file parser:
##
## .. code-block:: nimrod
## .. code-block:: nim
## :file: examples/parsecfgex.nim
import
import
hashes, strutils, lexbase, streams
include "system/inclrtl"
type
TCfgEventKind* = enum ## enumeration of all events that may occur when parsing
type
CfgEventKind* = enum ## enumeration of all events that may occur when parsing
cfgEof, ## end of file reached
cfgSectionStart, ## a ``[section]`` has been parsed
cfgKeyValuePair, ## a ``key=value`` pair has been detected
cfgOption, ## a ``--key=value`` command line option
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
of cfgEof: nil
of cfgSectionStart:
@@ -53,28 +53,30 @@ type
msg*: string ## contains the error message. No exceptions
## are thrown if a parse error occurs.
TTokKind = enum
TokKind = enum
tkInvalid, tkEof,
tkSymbol, tkEquals, tkColon, tkBracketLe, tkBracketRi, tkDashDash
TToken {.final.} = object # a token
kind: TTokKind # the type of the token
Token = object # a token
kind: TokKind # the type of the token
literal: string # the parsed (string) literal
TCfgParser* = object of TBaseLexer ## the parser object.
tok: TToken
CfgParser* = object of BaseLexer ## the parser object.
tok: Token
filename: string
{.deprecated: [TCfgEventKind: CfgEventKind, TCfgEvent: CfgEvent,
TTokKind: TokKind, TToken: Token, TCfgParser: CfgParser].}
# implementation
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,
lineOffset = 0) {.
rtl, extern: "npc$1".} =
proc open*(c: var CfgParser, input: PStream, filename: string,
lineOffset = 0) {.rtl, extern: "npc$1".} =
## initializes the parser with an input stream. `Filename` is only used
## for nice error messages. `lineOffset` can be used to influence the line
## number information in the generated error messages.
@@ -85,23 +87,23 @@ proc open*(c: var TCfgParser, input: PStream, filename: string,
inc(c.lineNumber, lineOffset)
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.
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.
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.
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.
result = c.filename
proc handleHexChar(c: var TCfgParser, xi: var int) =
proc handleHexChar(c: var CfgParser, xi: var int) =
case c.buf[c.bufpos]
of '0'..'9':
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:
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'}:
xi = (xi * 10) + (ord(c.buf[c.bufpos]) - ord('0'))
inc(c.bufpos)
proc getEscapedChar(c: var TCfgParser, tok: var TToken) =
proc getEscapedChar(c: var CfgParser, tok: var TToken) =
inc(c.bufpos) # skip '\'
case c.buf[c.bufpos]
of 'n', 'N':
@@ -169,13 +171,13 @@ proc getEscapedChar(c: var TCfgParser, tok: var TToken) =
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]
of '\c': result = lexbase.handleCR(c, pos)
of '\L': result = lexbase.handleLF(c, 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 buf = c.buf # put `buf` in a register
tok.kind = tkSymbol
@@ -221,7 +223,7 @@ proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
inc(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 buf = c.buf
while true:
@@ -231,7 +233,7 @@ proc getSymbol(c: var TCfgParser, tok: var TToken) =
c.bufpos = pos
tok.kind = tkSymbol
proc skip(c: var TCfgParser) =
proc skip(c: var CfgParser) =
var pos = c.bufpos
var buf = c.buf
while true:
@@ -247,7 +249,7 @@ proc skip(c: var TCfgParser) =
break # EndOfFile also leaves the loop
c.bufpos = pos
proc rawGetTok(c: var TCfgParser, tok: var TToken) =
proc rawGetTok(c: var CfgParser, tok: var Token) =
tok.kind = tkInvalid
setLen(tok.literal, 0)
skip(c)
@@ -286,19 +288,19 @@ proc rawGetTok(c: var TCfgParser, tok: var TToken) =
tok.literal = "[EOF]"
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
## column information.
result = `%`("$1($2, $3) Error: $4",
[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
## column information.
result = `%`("$1($2, $3) Warning: $4",
[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
## an entry is ignored.
case e.kind
@@ -309,7 +311,7 @@ proc ignoreMsg*(c: TCfgParser, e: TCfgEvent): string {.rtl, extern: "npc$1".} =
of cfgError: result = e.msg
of cfgEof: result = ""
proc getKeyValPair(c: var TCfgParser, kind: TCfgEventKind): TCfgEvent =
proc getKeyValPair(c: var CfgParser, kind: CfgEventKind): CfgEvent =
if c.tok.kind == tkSymbol:
result.kind = kind
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)
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.
case c.tok.kind
of tkEof:

View File

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

View File

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

View File

@@ -1,13 +1,13 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
# Nim's Runtime Library
# (c) Copyright 2014 Andreas Rumpf
#
# See the file "copying.txt", included in this
# 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
## lower-level features.
##
@@ -25,22 +25,24 @@ import
os, strutils
type
TCmdLineKind* = enum ## the detected command line token
CmdLineKind* = enum ## the detected command line token
cmdEnd, ## end of command line reached
cmdArgument, ## argument detected
cmdLongOption, ## a long option ``--option`` detected
cmdShortOption ## a short option ``-c`` detected
TOptParser* =
OptParser* =
object of TObject ## this object implements the command line parser
cmd: seq[string]
pos: int
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
## or the argument, ``value`` is not "" if
## 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
## argument 0 - program name.
## If cmdline == nil default to current command line arguments.
@@ -54,7 +56,7 @@ proc initOptParser*(cmdline: seq[string]): TOptParser {.rtl.} =
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
## and calls initOptParser(openarray[string])
## Do not use.
@@ -64,13 +66,13 @@ proc initOptParser*(cmdline: string): TOptParser {.rtl, deprecated.} =
return initOptParser(cmdline.split)
when not defined(createNimRtl):
proc initOptParser*(): TOptParser =
proc initOptParser*(): OptParser =
## Initializes option parser from current command line arguments.
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 [':', '=']:
if splitchar in token:
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.next()
proc next(p: var TOptParser) =
proc next(p: var OptParser) =
if p.remainingShortOptions.len != 0:
p.kind = cmdShortOption
p.key = TaintedString(p.remainingShortOptions[0..0])
@@ -111,20 +113,22 @@ proc next(p: var TOptParser) =
p.key = token
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.
## Do not use - does not correctly handle whitespace.
return p.cmd[p.pos..p.cmd.len-1].join(" ")
type
TGetoptResult* = tuple[kind: TCmdLineKind, key, val: TaintedString]
GetoptResult* = tuple[kind: CmdLineKind, key, val: TaintedString]
{.deprecated: [TGetoptResult: GetoptResult].}
when declared(paramCount):
iterator getopt*(): TGetoptResult =
iterator getopt*(): GetoptResult =
## 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
## filename = ""
## 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
#
# See the file "copying.txt", included in this
@@ -10,13 +10,13 @@
## The ``parsesql`` module implements a high performance SQL file
## parser. It parses PostgreSQL syntax and the SQL ANSI standard.
import
import
hashes, strutils, lexbase, streams
# ------------------- scanner -------------------------------------------------
type
TTokKind = enum ## enumeration of all SQL tokens
TokKind = enum ## enumeration of all SQL tokens
tkInvalid, ## invalid token
tkEof, ## end of file reached
tkIdentifier, ## abc
@@ -38,33 +38,35 @@ type
tkBracketRi, ## ']'
tkDot ## '.'
TToken {.final.} = object # a token
kind: TTokKind # the type of the token
Token = object # a token
kind: TokKind # the type of the token
literal: string # the parsed (string) literal
TSqlLexer* = object of TBaseLexer ## the parser object.
SqlLexer* = object of BaseLexer ## the parser object.
filename: string
{.deprecated: [TToken: Token, TSqlLexer: SqlLexer].}
const
tokKindToStr: array[TTokKind, string] = [
tokKindToStr: array[TokKind, string] = [
"invalid", "[EOF]", "identifier", "quoted identifier", "string constant",
"escape string constant", "dollar quoted constant", "bit string constant",
"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)
L.filename = filename
proc close(L: var TSqlLexer) =
proc close(L: var SqlLexer) =
lexbase.close(L)
proc getColumn(L: TSqlLexer): int =
proc getColumn(L: SqlLexer): int =
## get the current column the parser has arrived at.
result = getColNumber(L, L.bufPos)
proc getLine(L: TSqlLexer): int =
proc getLine(L: SqlLexer): int =
result = L.linenumber
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
proc HandleCRLF(c: var TSqlLexer, pos: int): int =
proc handleCRLF(c: var TSqlLexer, pos: int): int =
case c.buf[pos]
of '\c': result = lexbase.HandleCR(c, pos)
of '\L': result = lexbase.HandleLF(c, pos)
of '\c': result = lexbase.handleCR(c, pos)
of '\L': result = lexbase.handleLF(c, pos)
else: result = pos
proc skip(c: var TSqlLexer) =
@@ -163,7 +165,7 @@ proc skip(c: var TSqlLexer) =
case buf[pos]
of '\0': break
of '\c', '\L':
pos = HandleCRLF(c, pos)
pos = handleCRLF(c, pos)
buf = c.buf
of '*':
if buf[pos+1] == '/':
@@ -181,13 +183,13 @@ proc skip(c: var TSqlLexer) =
else: inc(pos)
else: break
of '\c', '\L':
pos = HandleCRLF(c, pos)
pos = handleCRLF(c, pos)
buf = c.buf
else:
break # EndOfFile also leaves the loop
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 buf = c.buf
tok.kind = kind
@@ -465,7 +467,7 @@ proc errorStr(L: TSqlLexer, msg: string): string =
# :: left PostgreSQL-style typecast
# [ ] left array element selection
# - right unary minus
# ^ left exponentiation
# ^ left exponentiation
# * / % left multiplication, division, modulo
# + - left addition, subtraction
# IS IS TRUE, IS FALSE, IS UNKNOWN, IS NULL
@@ -483,7 +485,7 @@ proc errorStr(L: TSqlLexer, msg: string): string =
# OR left logical disjunction
type
TSqlNodeKind* = enum ## kind of SQL abstract syntax tree
SqlNodeKind* = enum ## kind of SQL abstract syntax tree
nkNone,
nkIdent,
nkStringLit,
@@ -536,79 +538,82 @@ type
nkEnumDef
type
EInvalidSql* = object of EInvalidValue ## Invalid SQL encountered
PSqlNode* = ref TSqlNode ## an SQL abstract syntax tree node
TSqlNode* = object ## an SQL abstract syntax tree node
case kind*: TSqlNodeKind ## kind of syntax tree
SqlParseError* = object of ValueError ## Invalid SQL encountered
SqlNode* = ref SqlNodeObj ## an SQL abstract syntax tree node
SqlNodeObj* = object ## an SQL abstract syntax tree node
case kind*: SqlNodeKind ## kind of syntax tree
of nkIdent, nkStringLit, nkBitStringLit, nkHexStringLit,
nkIntegerLit, nkNumericLit:
strVal*: string ## AST leaf: the identifier, numeric literal
## string literal, etc.
else:
sons*: seq[PSqlNode] ## the node's children
sons*: seq[SqlNode] ## the node's children
TSqlParser* = object of TSqlLexer ## SQL parser object
tok: TToken
SqlParser* = object of SqlLexer ## SQL parser object
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)
result.kind = k
proc newNode(k: TSqlNodeKind, s: string): PSqlNode =
proc newNode(k: SqlNodeKind, s: string): SqlNode =
new(result)
result.kind = k
result.strVal = s
proc len*(n: PSqlNode): int =
proc len*(n: SqlNode): int =
if isNil(n.sons): result = 0
else: result = n.sons.len
proc add*(father, n: PSqlNode) =
proc add*(father, n: SqlNode) =
if isNil(father.sons): father.sons = @[]
add(father.sons, n)
proc getTok(p: var TSqlParser) =
proc getTok(p: var SqlParser) =
getTok(p, p.tok)
proc sqlError(p: TSqlParser, msg: string) =
var e: ref EInvalidSql
proc sqlError(p: SqlParser, msg: string) =
var e: ref SqlParseError
new(e)
e.msg = errorStr(p, msg)
raise e
proc isKeyw(p: TSqlParser, keyw: string): bool =
proc isKeyw(p: SqlParser, keyw: string): bool =
result = p.tok.kind == tkIdentifier and
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
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:
getTok(p)
proc expectIdent(p: TSqlParser) =
proc expectIdent(p: SqlParser) =
if p.tok.kind != tkIdentifier and p.tok.kind != tkQuotedIdentifier:
sqlError(p, "identifier expected")
proc expect(p: TSqlParser, kind: TTokKind) =
proc expect(p: SqlParser, kind: TokKind) =
if p.tok.kind != kind:
sqlError(p, tokKindToStr[kind] & " expected")
proc eat(p: var TSqlParser, kind: TTokKind) =
proc eat(p: var SqlParser, kind: TokKind) =
if p.tok.kind == kind:
getTok(p)
else:
sqlError(p, tokKindToStr[kind] & " expected")
proc eat(p: var TSqlParser, keyw: string) =
proc eat(p: var SqlParser, keyw: string) =
if isKeyw(p, keyw):
getTok(p)
else:
sqlError(p, keyw.toUpper() & " expected")
proc parseDataType(p: var TSqlParser): PSqlNode =
proc parseDataType(p: var SqlParser): SqlNode =
if isKeyw(p, "enum"):
result = newNode(nkEnumDef)
getTok(p)
@@ -636,7 +641,7 @@ proc parseDataType(p: var TSqlParser): PSqlNode =
getTok(p)
eat(p, tkParRi)
proc getPrecedence(p: TSqlParser): int =
proc getPrecedence(p: SqlParser): int =
if isOpr(p, "*") or isOpr(p, "/") or isOpr(p, "%"):
result = 6
elif isOpr(p, "+") or isOpr(p, "-"):
@@ -655,9 +660,9 @@ proc getPrecedence(p: TSqlParser): int =
else:
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
of tkIdentifier, tkQuotedIdentifier:
result = newNode(nkIdent, p.tok.literal)
@@ -685,7 +690,7 @@ proc identOrLiteral(p: var TSqlParser): PSqlNode =
sqlError(p, "expression expected")
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"):
result = newNode(nkPrefix)
result.add(newNode(nkIdent, p.tok.literal))
@@ -723,9 +728,9 @@ proc primary(p: var TSqlParser): PSqlNode =
getTok(p)
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
v2, node, opNode: PSqlNode
v2, node, opNode: SqlNode
v = primary(p) # expand while operators have priorities higher than 'limit'
var opPred = getPrecedence(p)
result = opPred
@@ -740,14 +745,14 @@ proc lowestExprAux(p: var TSqlParser, v: var PSqlNode, limit: int): int =
v = node
opPred = getPrecedence(p)
proc parseExpr(p: var TSqlParser): PSqlNode =
proc parseExpr(p: var SqlParser): SqlNode =
discard lowestExprAux(p, result, - 1)
proc parseTableName(p: var TSqlParser): PSqlNode =
proc parseTableName(p: var SqlParser): SqlNode =
expectIdent(p)
result = primary(p)
proc parseColumnReference(p: var TSqlParser): PSqlNode =
proc parseColumnReference(p: var SqlParser): SqlNode =
result = parseTableName(p)
if p.tok.kind == tkParLe:
getTok(p)
@@ -760,12 +765,12 @@ proc parseColumnReference(p: var TSqlParser): PSqlNode =
result.add(parseTableName(p))
eat(p, tkParRi)
proc parseCheck(p: var TSqlParser): PSqlNode =
proc parseCheck(p: var SqlParser): SqlNode =
getTok(p)
result = newNode(nkCheck)
result.add(parseExpr(p))
proc parseConstraint(p: var TSqlParser): PSqlNode =
proc parseConstraint(p: var SqlParser): SqlNode =
getTok(p)
result = newNode(nkConstraint)
expectIdent(p)
@@ -774,7 +779,7 @@ proc parseConstraint(p: var TSqlParser): PSqlNode =
eat(p, "check")
result.add(parseExpr(p))
proc parseColumnConstraints(p: var TSqlParser, result: PSqlNode) =
proc parseColumnConstraints(p: var SqlParser, result: SqlNode) =
while true:
if isKeyw(p, "default"):
getTok(p)
@@ -806,7 +811,7 @@ proc parseColumnConstraints(p: var TSqlParser, result: PSqlNode) =
else:
break
proc parseColumnDef(p: var TSqlParser): PSqlNode =
proc parseColumnDef(p: var SqlParser): SqlNode =
expectIdent(p)
result = newNode(nkColumnDef)
result.add(newNode(nkIdent, p.tok.literal))
@@ -814,7 +819,7 @@ proc parseColumnDef(p: var TSqlParser): PSqlNode =
result.add(parseDataType(p))
parseColumnConstraints(p, result)
proc parseIfNotExists(p: var TSqlParser, k: TSqlNodeKind): PSqlNode =
proc parseIfNotExists(p: var SqlParser, k: TSqlNodeKind): SqlNode =
getTok(p)
if isKeyw(p, "if"):
getTok(p)
@@ -824,7 +829,7 @@ proc parseIfNotExists(p: var TSqlParser, k: TSqlNodeKind): PSqlNode =
else:
result = newNode(k)
proc parseParIdentList(p: var TSqlParser, father: PSqlNode) =
proc parseParIdentList(p: var SqlParser, father: SqlNode) =
eat(p, tkParLe)
while true:
expectIdent(p)
@@ -834,7 +839,7 @@ proc parseParIdentList(p: var TSqlParser, father: PSqlNode) =
getTok(p)
eat(p, tkParRi)
proc parseTableConstraint(p: var TSqlParser): PSqlNode =
proc parseTableConstraint(p: var SqlParser): SqlNode =
if isKeyw(p, "primary"):
getTok(p)
eat(p, "key")
@@ -861,7 +866,7 @@ proc parseTableConstraint(p: var TSqlParser): PSqlNode =
else:
sqlError(p, "column definition expected")
proc parseTableDef(p: var TSqlParser): PSqlNode =
proc parseTableDef(p: var SqlParser): SqlNode =
result = parseIfNotExists(p, nkCreateTable)
expectIdent(p)
result.add(newNode(nkIdent, p.tok.literal))
@@ -876,7 +881,7 @@ proc parseTableDef(p: var TSqlParser): PSqlNode =
if p.tok.kind != tkComma: break
eat(p, tkParRi)
proc parseTypeDef(p: var TSqlParser): PSqlNode =
proc parseTypeDef(p: var SqlParser): SqlNode =
result = parseIfNotExists(p, nkCreateType)
expectIdent(p)
result.add(newNode(nkIdent, p.tok.literal))
@@ -884,12 +889,12 @@ proc parseTypeDef(p: var TSqlParser): PSqlNode =
eat(p, "as")
result.add(parseDataType(p))
proc parseWhere(p: var TSqlParser): PSqlNode =
proc parseWhere(p: var SqlParser): SqlNode =
getTok(p)
result = newNode(nkWhere)
result.add(parseExpr(p))
proc parseIndexDef(p: var TSqlParser): PSqlNode =
proc parseIndexDef(p: var SqlParser): SqlNode =
result = parseIfNotExists(p, nkCreateIndex)
if isKeyw(p, "primary"):
getTok(p)
@@ -914,7 +919,7 @@ proc parseIndexDef(p: var TSqlParser): PSqlNode =
getTok(p)
eat(p, tkParRi)
proc parseInsert(p: var TSqlParser): PSqlNode =
proc parseInsert(p: var SqlParser): SqlNode =
getTok(p)
eat(p, "into")
expectIdent(p)
@@ -941,7 +946,7 @@ proc parseInsert(p: var TSqlParser): PSqlNode =
result.add(n)
eat(p, tkParRi)
proc parseUpdate(p: var TSqlParser): PSqlNode =
proc parseUpdate(p: var SqlParser): SqlNode =
getTok(p)
result = newNode(nkUpdate)
result.add(primary(p))
@@ -962,7 +967,7 @@ proc parseUpdate(p: var TSqlParser): PSqlNode =
else:
result.add(nil)
proc parseDelete(p: var TSqlParser): PSqlNode =
proc parseDelete(p: var SqlParser): SqlNode =
getTok(p)
result = newNode(nkDelete)
eat(p, "from")
@@ -972,7 +977,7 @@ proc parseDelete(p: var TSqlParser): PSqlNode =
else:
result.add(nil)
proc parseSelect(p: var TSqlParser): PSqlNode =
proc parseSelect(p: var SqlParser): SqlNode =
getTok(p)
if isKeyw(p, "distinct"):
getTok(p)
@@ -1041,7 +1046,7 @@ proc parseSelect(p: var TSqlParser): PSqlNode =
getTok(p)
result.add(n)
proc parseStmt(p: var TSqlParser): PSqlNode =
proc parseStmt(p: var SqlParser): SqlNode =
if isKeyw(p, "create"):
getTok(p)
optKeyw(p, "cached")
@@ -1071,7 +1076,7 @@ proc parseStmt(p: var TSqlParser): PSqlNode =
else:
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.
## `filename` is only used for error messages.
open(TSqlLexer(p), input, filename)
@@ -1079,7 +1084,7 @@ proc open(p: var TSqlParser, input: PStream, filename: string) =
p.tok.literal = ""
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.
## Syntax errors raise an `EInvalidSql` exception.
result = newNode(nkStmtList)
@@ -1090,24 +1095,24 @@ proc parse(p: var TSqlParser): PSqlNode =
if result.len == 1:
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.
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.
## `filename` is only used for error messages.
## Syntax errors raise an `EInvalidSql` exception.
var p: TSqlParser
var p: SqlParser
open(p, input, filename)
try:
result = parse(p)
finally:
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 = ")",
sep = ", ") =
if n.len > 0:
@@ -1117,7 +1122,7 @@ proc rs(n: PSqlNode, s: var string, indent: int,
ra(n.sons[i], s, indent)
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
case n.kind
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):
# writeln x, y, z
proc renderSQL*(n: PSqlNode): string =
proc renderSQL*(n: SqlNode): string =
## Converts an SQL abstract syntax tree to its string representation.
result = ""
ra(n, result, 0)

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2014 Dominik Picheta
#
# See the file "copying.txt", included in this
@@ -15,12 +15,14 @@
import strutils
type
TUrl* = tuple[ ## represents a *Uniform Resource Locator* (URL)
## any optional component is "" if it does not exist
Url* = tuple[ ## represents a *Uniform Resource Locator* (URL)
## any optional component is "" if it does not exist
scheme, username, password,
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 scheme, username, password: string = ""
@@ -86,7 +88,7 @@ proc parseUrl*(url: string): TUrl {.deprecated.} =
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.
result = ""
if u.scheme.len > 0:

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
#
# 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
## overflow. Example:
##
## .. code-block:: nimrod
## .. code-block:: nim
## var value = 0
## discard parseHex("0x38", value)
## assert value == 56
@@ -332,7 +332,7 @@ proc parseFloat*(s: string, number: var float, start = 0): int {.
number = bf
type
TInterpolatedKind* = enum ## describes for `interpolatedFragments`
InterpolatedKind* = enum ## describes for `interpolatedFragments`
## which part of the interpolated string is
## yielded; for example in "str$$$var${expr}"
ikStr, ## ``str`` part of the interpolated string
@@ -340,7 +340,9 @@ type
ikVar, ## ``var`` 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] =
## Tokenizes the string `s` into substrings for interpolation purposes.
##
@@ -360,7 +362,7 @@ iterator interpolatedFragments*(s: string): tuple[kind: TInterpolatedKind,
## (ikString, " ")
## (ikDollar, "$")
var i = 0
var kind: TInterpolatedKind
var kind: InterpolatedKind
while true:
var j = i
if s[j] == '$':

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2014 Dominik Picheta
#
# 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,
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
export
@@ -40,22 +40,22 @@ export
MSG_PEEK
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
## here are unsupported.
AF_UNIX, ## for local socket (using a file). Unsupported on Windows.
AF_INET = 2, ## for network protocol IPv4 or
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_DGRAM = 2, ## datagram service or Datagram Sockets
SOCK_RAW = 3, ## raw protocols atop the network layer.
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_UDP = 17, ## User datagram protocol.
IPPROTO_IP, ## Internet protocol. Unsupported on Windows.
@@ -63,19 +63,22 @@ type
IPPROTO_RAW, ## Raw IP Packets 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
aliases*: seq[string]
port*: TPort
port*: Port
proto*: string
Thostent* {.pure, final.} = object ## information about a given host
Hostent* = object ## information about a given host
name*: string
aliases*: seq[string]
addrtype*: TDomain
addrtype*: Domain
length*: int
addrList*: seq[string]
{.deprecated: [TPort: Port, TDomain: Domain, TType: SockType,
TProtocol: Protocol, TServent: Servent, THostent: Hostent].}
when useWinVersion:
let
osInvalidSocket* = winlean.INVALID_SOCKET
@@ -86,37 +89,37 @@ when useWinVersion:
FIONBIO* = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or
(102 shl 8) or 126
proc ioctlsocket*(s: TSocketHandle, cmd: clong,
proc ioctlsocket*(s: SocketHandle, cmd: clong,
argptr: ptr clong): cint {.
stdcall, importc: "ioctlsocket", dynlib: "ws2_32.dll".}
else:
let
osInvalidSocket* = posix.INVALID_SOCKET
proc `==`*(a, b: TPort): bool {.borrow.}
proc `==`*(a, b: Port): bool {.borrow.}
## ``==`` for ports.
proc `$`*(p: TPort): string {.borrow.}
proc `$`*(p: Port): string {.borrow.}
## 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``.
proc toInt*(typ: TType): cint
proc toInt*(typ: SockType): 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``.
when not useWinVersion:
proc toInt(domain: TDomain): cint =
proc toInt(domain: Domain): cint =
case domain
of AF_UNIX: result = posix.AF_UNIX
of AF_INET: result = posix.AF_INET
of AF_INET6: result = posix.AF_INET6
else: discard
proc toInt(typ: TType): cint =
proc toInt(typ: SockType): cint =
case typ
of SOCK_STREAM: result = posix.SOCK_STREAM
of SOCK_DGRAM: result = posix.SOCK_DGRAM
@@ -124,7 +127,7 @@ when not useWinVersion:
of SOCK_RAW: result = posix.SOCK_RAW
else: discard
proc toInt(p: TProtocol): cint =
proc toInt(p: Protocol): cint =
case p
of IPPROTO_TCP: result = posix.IPPROTO_TCP
of IPPROTO_UDP: result = posix.IPPROTO_UDP
@@ -135,22 +138,22 @@ when not useWinVersion:
else: discard
else:
proc toInt(domain: TDomain): cint =
proc toInt(domain: Domain): cint =
result = toU16(ord(domain))
proc toInt(typ: TType): cint =
proc toInt(typ: SockType): cint =
result = cint(ord(typ))
proc toInt(p: TProtocol): cint =
proc toInt(p: Protocol): cint =
result = cint(ord(p))
proc newRawSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
protocol: TProtocol = IPPROTO_TCP): TSocketHandle =
proc newRawSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP): SocketHandle =
## Creates a new socket; returns `InvalidSocket` if an error occurs.
socket(toInt(domain), toInt(typ), toInt(protocol))
proc close*(socket: TSocketHandle) =
proc close*(socket: SocketHandle) =
## closes a socket.
when useWinVersion:
discard winlean.closeSocket(socket)
@@ -159,10 +162,10 @@ proc close*(socket: TSocketHandle) =
# 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
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)
proc listen*(socket: TSocketHandle, backlog = SOMAXCONN): cint {.tags: [FReadIO].} =
proc listen*(socket: SocketHandle, backlog = SOMAXCONN): cint {.tags: [FReadIO].} =
## Marks ``socket`` as accepting connections.
## ``Backlog`` specifies the maximum length of the
## queue of pending connections.
@@ -171,12 +174,12 @@ proc listen*(socket: TSocketHandle, backlog = SOMAXCONN): cint {.tags: [FReadIO]
else:
result = posix.listen(socket, cint(backlog))
proc getAddrInfo*(address: string, port: TPort, af: TDomain = AF_INET, typ: TType = SOCK_STREAM,
prot: TProtocol = IPPROTO_TCP): ptr TAddrInfo =
proc getAddrInfo*(address: string, port: Port, af: Domain = AF_INET, typ: SockType = SOCK_STREAM,
prot: Protocol = IPPROTO_TCP): ptr AddrInfo =
##
##
## **Warning**: The resulting ``ptr TAddrInfo`` must be freed using ``dealloc``!
var hints: TAddrInfo
var hints: AddrInfo
result = nil
hints.ai_family = toInt(af)
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)
if gaiResult != 0'i32:
when useWinVersion:
osError(osLastError())
raiseOSError(osLastError())
else:
raise newException(EOS, $gai_strerror(gaiResult))
proc dealloc*(ai: ptr TAddrInfo) =
proc dealloc*(ai: ptr AddrInfo) =
freeaddrinfo(ai)
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.
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
## which the service name specified by ``name`` matches the s_name 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.")
result.name = $s.s_name
result.aliases = cstringArrayToSeq(s.s_aliases)
result.port = TPort(s.s_port)
result.port = Port(s.s_port)
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
## which the port specified by ``port`` matches the s_port member and the
## 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.")
result.name = $s.s_name
result.aliases = cstringArrayToSeq(s.s_aliases)
result.port = TPort(s.s_port)
result.port = Port(s.s_port)
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.
var myaddr: TInAddr
var myaddr: InAddr
myaddr.s_addr = inet_addr(ip)
when useWinVersion:
var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
cint(rawsockets.AF_INET))
if s == nil: osError(osLastError())
if s == nil: raiseOSError(osLastError())
else:
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).TSocklen,
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).Socklen,
cint(posix.AF_INET))
if s == nil:
raise newException(EOS, $hstrerror(h_errno))
result.name = $s.h_name
result.aliases = cstringArrayToSeq(s.h_aliases)
when useWinVersion:
result.addrtype = TDomain(s.h_addrtype)
when useWinVersion:
result.addrtype = Domain(s.h_addrtype)
else:
if s.h_addrtype == posix.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.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.
when useWinVersion:
var s = winlean.gethostbyname(name)
else:
var s = posix.gethostbyname(name)
if s == nil: osError(osLastError())
if s == nil: raiseOSError(osLastError())
result.name = $s.h_name
result.aliases = cstringArrayToSeq(s.h_aliases)
when useWinVersion:
result.addrtype = TDomain(s.h_addrtype)
when useWinVersion:
result.addrtype = Domain(s.h_addrtype)
else:
if s.h_addrtype == posix.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.length = int(s.h_length)
proc getSockName*(socket: TSocketHandle): TPort =
proc getSockName*(socket: SocketHandle): Port =
## returns the socket's associated port number.
var name: Tsockaddr_in
var name: Sockaddr_in
when useWinVersion:
name.sin_family = int16(ord(AF_INET))
else:
name.sin_family = posix.AF_INET
#name.sin_port = htons(cint16(port))
#name.sin_addr.s_addr = htonl(INADDR_ANY)
var namelen = sizeof(name).TSocklen
if getsockname(socket, cast[ptr TSockAddr](addr(name)),
var namelen = sizeof(name).Socklen
if getsockname(socket, cast[ptr SockAddr](addr(name)),
addr(namelen)) == -1'i32:
osError(osLastError())
result = TPort(rawsockets.ntohs(name.sin_port))
raiseOSError(osLastError())
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].} =
## getsockopt for integer options.
var res: cint
var size = sizeof(res).TSocklen
var size = sizeof(res).Socklen
if getsockopt(socket, cint(level), cint(optname),
addr(res), addr(size)) < 0'i32:
osError(osLastError())
raiseOSError(osLastError())
result = int(res)
proc setSockOptInt*(socket: TSocketHandle, level, optname, optval: int) {.
proc setSockOptInt*(socket: SocketHandle, level, optname, optval: int) {.
tags: [FWriteIO].} =
## setsockopt for integer options.
var value = cint(optval)
if setsockopt(socket, cint(level), cint(optname), addr(value),
sizeof(value).TSocklen) < 0'i32:
osError(osLastError())
sizeof(value).Socklen) < 0'i32:
raiseOSError(osLastError())
proc setBlocking*(s: TSocketHandle, blocking: bool) =
proc setBlocking*(s: SocketHandle, blocking: bool) =
## Sets blocking mode on socket.
##
## Raises EOS on error.
when useWinVersion:
var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
if ioctlsocket(s, FIONBIO, addr(mode)) == -1:
osError(osLastError())
raiseOSError(osLastError())
else: # BSD sockets
var x: int = fcntl(s, F_GETFL, 0)
if x == -1:
osError(osLastError())
raiseOSError(osLastError())
else:
var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
if fcntl(s, F_SETFL, mode) == -1:
osError(osLastError())
raiseOSError(osLastError())
proc timeValFromMilliseconds(timeout = 500): Ttimeval =
proc timeValFromMilliseconds(timeout = 500): Timeval =
if timeout != -1:
var seconds = timeout div 1000
result.tv_sec = seconds.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)
for i in items(s):
m = max(m, int(i))
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 L = s.len
while i < L:
@@ -375,7 +378,7 @@ proc pruneSocketSet(s: var seq[TSocketHandle], fd: var TFdSet) =
inc(i)
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
## sockets that are ready to be read from, written to, or which have errors.
## If there are none; 0 is returned.
@@ -396,7 +399,7 @@ proc select*(readfds: var seq[TSocketHandle], timeout = 500): int =
pruneSocketSet(readfds, (rd))
proc selectWrite*(writefds: var seq[TSocketHandle],
proc selectWrite*(writefds: var seq[SocketHandle],
timeout = 500): int {.tags: [FReadIO].} =
## 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
@@ -405,7 +408,7 @@ proc selectWrite*(writefds: var seq[TSocketHandle],
##
## ``timeout`` is specified in miliseconds and ``-1`` can be specified for
## an unlimited time.
var tv {.noInit.}: Ttimeval = timeValFromMilliseconds(timeout)
var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
var wr: TFdSet
var m = 0
@@ -419,5 +422,5 @@ proc selectWrite*(writefds: var seq[TSocketHandle],
pruneSocketSet(writefds, (wr))
when defined(Windows):
var wsa: TWSADATA
if WSAStartup(0x0101'i16, addr wsa) != 0: osError(osLastError())
var wsa: WSADATA
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
#
# See the file "copying.txt", included in this
@@ -20,36 +20,40 @@ const
redisNil* = "\0\0"
type
PPipeline = ref object
Pipeline = ref object
enabled: bool
buffer: string
expected: int ## number of replies expected if pipelined
type
TSendMode = enum
SendMode = enum
normal, pipelined, multiple
type
TRedis* {.pure, final.} = object
Redis* = object
socket: TSocket
connected: bool
pipeline: PPipeline
pipeline: Pipeline
TRedisStatus* = string
TRedisInteger* = biggestInt
TRedisString* = string ## Bulk reply
TRedisList* = seq[TRedisString] ## Multi-bulk reply
RedisStatus* = string
RedisInteger* = BiggestInt
RedisString* = string ## Bulk reply
RedisList* = seq[RedisString] ## Multi-bulk reply
EInvalidReply* = object of ESynch ## Invalid reply from redis
ERedis* = object of ESynch ## Error in redis
ReplyError* = object of IOError ## Invalid reply from 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)
result.buffer = ""
result.enabled = false
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.
result.socket = socket(buffered = false)
if result.socket == InvalidSocket:
@@ -58,24 +62,24 @@ proc open*(host = "localhost", port = 6379.TPort): TRedis =
result.pipeline = newPipeline()
proc raiseInvalidReply(expected, got: char) =
raise newException(EInvalidReply,
raise newException(ReplyError,
"Expected '$1' at the beginning of a status reply got '$2'" %
[$expected, $got])
proc raiseNoOK(status: string, pipelineEnabled:bool) =
proc raiseNoOK(status: string, pipelineEnabled: bool) =
if pipelineEnabled and not (status == "QUEUED" or status == "PIPELINED"):
raise newException(EInvalidReply, "Expected \"QUEUED\" or \"PIPELINED\" got \"$1\"" % status)
elif not pipelineEnabled and status != "OK":
raise newException(EInvalidReply, "Expected \"OK\" got \"$1\"" % status)
template readSocket(r: TRedis, dummyVal:expr): stmt =
var line {.inject.} :TaintedString = ""
template readSocket(r: Redis, dummyVal:expr): stmt =
var line {.inject.}: TaintedString = ""
if r.pipeline.enabled:
return dummyVal
else:
readLine(r.socket, line)
proc parseStatus(r: TRedis, line: string = ""): TRedisStatus =
proc parseStatus(r: Redis, line: string = ""): RedisStatus =
if r.pipeline.enabled:
return "PIPELINED"

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2011 Philippe Lhoste
#
# 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
#
# See the file "copying.txt", included in this
@@ -30,13 +30,15 @@ var
cacheEnabled = false
type
PRope* = ref TRope ## empty rope is represented by nil
TRope {.acyclic, final, pure.} = object
left, right: PRope
Rope* = ref RopeObj ## empty rope is represented by nil
RopeObj {.acyclic.} = object
left, right: Rope
length: int
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.
# 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
# pointers.
proc len*(a: PRope): int {.rtl, extern: "nro$1".} =
proc len*(a: Rope): int {.rtl, extern: "nro$1".} =
## the rope's length
if a == nil: result = 0
else: result = a.length
proc newRope(): PRope = new(result)
proc newRope(data: string): PRope =
proc newRope(): Rope = new(result)
proc newRope(data: string): Rope =
new(result)
result.length = len(data)
result.data = data
var
cache {.threadvar.}: PRope # the root of the cache tree
N {.threadvar.}: PRope # dummy rope needed for splay algorithm
cache {.threadvar.}: Rope # the root of the cache tree
N {.threadvar.}: Rope # dummy rope needed for splay algorithm
when countCacheMisses:
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 t = tree
N.left = nil
@@ -102,7 +104,7 @@ proc splay(s: string, tree: PRope, cmpres: var int): PRope =
t.right = N.left
result = t
proc insertInCache(s: string, tree: PRope): PRope =
proc insertInCache(s: string, tree: Rope): Rope =
var t = tree
if t == nil:
result = newRope(s)
@@ -128,7 +130,7 @@ proc insertInCache(s: string, tree: PRope): PRope =
result.left = t
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.
if s.len == 0:
result = nil
@@ -138,11 +140,11 @@ proc rope*(s: string): PRope {.rtl, extern: "nro$1Str".} =
else:
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.
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.
result = rope($f)
@@ -156,7 +158,7 @@ proc disableCache*() {.rtl, extern: "nro$1".} =
cache = nil
cacheEnabled = false
proc `&`*(a, b: PRope): PRope {.rtl, extern: "nroConcRopeRope".} =
proc `&`*(a, b: Rope): Rope {.rtl, extern: "nroConcRopeRope".} =
## the concatenation operator for ropes.
if a == nil:
result = b
@@ -176,27 +178,27 @@ proc `&`*(a, b: PRope): PRope {.rtl, extern: "nroConcRopeRope".} =
result.left = a
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.
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.
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.
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`.
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`.
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
## expensive! Worst-case: O(n). If ``i >= r.len``, ``\0`` is returned.
var x = r
@@ -213,7 +215,7 @@ proc `[]`*(r: PRope, i: int): char {.rtl, extern: "nroCharAt".} =
x = x.right
dec(j, x.len)
iterator leaves*(r: PRope): string =
iterator leaves*(r: Rope): string =
## iterates over any leaf string in the rope `r`.
if r != nil:
var stack = @[r]
@@ -226,16 +228,16 @@ iterator leaves*(r: PRope): string =
assert(it.data != nil)
yield it.data
iterator items*(r: PRope): char =
iterator items*(r: Rope): char =
## iterates over any character in the rope `r`.
for s in leaves(r):
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.
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.
result = newString(r.len)
setLen(result, 0)
@@ -245,11 +247,11 @@ when false:
# 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
# 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)
result.length = -idx
proc compileFrmt(frmt: string): PRope =
proc compileFrmt(frmt: string): Rope =
var i = 0
var length = len(frmt)
result = nil
@@ -289,7 +291,7 @@ when false:
if i - 1 >= start:
add(result, substr(frmt, start, i-1))
proc `%`*(frmt: string, args: openarray[PRope]): PRope {.
proc `%`*(frmt: string, args: openarray[Rope]): Rope {.
rtl, extern: "nroFormat".} =
## `%` substitution operator for ropes. Does not support the ``$identifier``
## nor ``${identifier}`` notations.
@@ -332,12 +334,12 @@ proc `%`*(frmt: string, args: openarray[PRope]): PRope {.
if i - 1 >= start:
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".} =
## shortcut for ``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`.
var bufSize = 1024 # reasonable start value
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?
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
## exist, false is returned.
var bin: TFile

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2013 Andreas Rumpf, Dominik Picheta
#
# See the file "copying.txt", included in this
@@ -9,13 +9,13 @@
## This module implements helper procs for SCGI applications. Example:
##
## .. code-block:: Nimrod
## .. code-block:: Nim
##
## import strtabs, sockets, scgi
##
## var counter = 0
## proc handleRequest(client: TSocket, input: string,
## headers: PStringTable): bool {.procvar.} =
## proc handleRequest(client: Socket, input: string,
## headers: StringTableRef): bool {.procvar.} =
## inc(counter)
## client.writeStatusOkTextContent()
## client.send("Hello for the $#th time." % $counter & "\c\L")
@@ -31,11 +31,11 @@ include "system/inclrtl"
import sockets, strutils, os, strtabs, asyncio
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.} =
## raises an EScgi exception with message `msg`.
var e: ref EScgi
proc raiseScgiError*(msg: string) {.noreturn.} =
## raises an ScgiError exception with message `msg`.
var e: ref ScgiError
new(e)
e.msg = msg
raise e
@@ -45,7 +45,7 @@ proc parseWord(inp: string, outp: var string, start: int): int =
while inp[result] != '\0': inc(result)
outp = substr(inp, start, result-1)
proc parseHeaders(s: string, L: int): PStringTable =
proc parseHeaders(s: string, L: int): StringTableRef =
result = newStringTable()
var i = 0
while i < L:
@@ -54,73 +54,77 @@ proc parseHeaders(s: string, L: int): PStringTable =
i = parseWord(s, val, i)+1
result[key] = val
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
if recv(s, addr(c), sizeof(c)) == sizeof(c):
result = c
type
TScgiState* = object of TObject ## SCGI state object
server: TSocket
ScgiState* = object of RootObj ## SCGI state object
server: Socket
bufLen: int
client*: TSocket ## the client socket to send data to
headers*: PStringTable ## the parsed headers
client*: Socket ## the client socket to send data to
headers*: StringTableRef ## the parsed headers
input*: string ## the input buffer
# Async
TClientMode = enum
ClientMode = enum
ClientReadChar, ClientReadHeaders, ClientReadContent
PAsyncClient = ref object
c: PAsyncSocket
mode: TClientMode
AsyncClient = ref object
c: AsyncSocket
mode: ClientMode
dataLen: int
headers: PStringTable ## the parsed headers
input: string ## the input buffer
TAsyncScgiState = object
handleRequest: proc (client: PAsyncSocket,
input: string, headers: PStringTable) {.closure,gcsafe.}
asyncServer: PAsyncSocket
disp: PDispatcher
PAsyncScgiState* = ref TAsyncScgiState
proc recvBuffer(s: var TScgiState, L: int) =
AsyncScgiStateObj = object
handleRequest: proc (client: AsyncSocket,
input: string,
headers: StringTableRef) {.closure, gcsafe.}
asyncServer: AsyncSocket
disp: Dispatcher
AsyncScgiState* = ref AsyncScgiStateObj
{.deprected: [EScgi: ScgiError, TScgiState: ScgiState,
PAsyncScgiState: AsyncScgiState, scgiError: raiseScgiError].}
proc recvBuffer(s: var ScgiState, L: int) =
if L > s.bufLen:
s.bufLen = L
s.input = newString(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)
proc open*(s: var TScgiState, port = TPort(4000), address = "127.0.0.1",
reuseAddr = False) =
proc open*(s: var ScgiState, port = Port(4000), address = "127.0.0.1",
reuseAddr = false) =
## opens a connection.
s.bufLen = 4000
s.input = newString(s.buflen) # will be reused
s.server = socket()
if s.server == InvalidSocket: osError(osLastError())
if s.server == InvalidSocket: raiseOSError(osLastError())
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)
if reuseAddr:
s.server.setSockOpt(OptReuseAddr, True)
bindAddr(s.server, port, address)
listen(s.server)
proc close*(s: var TScgiState) =
proc close*(s: var ScgiState) =
## closes the connection.
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
## 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]
if select(rsocks, timeout) == 1 and rsocks.len == 1:
new(s.client)
@@ -132,17 +136,17 @@ proc next*(s: var TScgistate, timeout: int = -1): bool =
s.client.close()
return false
if d notin strutils.digits:
if d != ':': scgiError("':' after length expected")
if d != ':': raiseScgiError("':' after length expected")
break
L = L * 10 + ord(d) - ord('0')
recvBuffer(s, L+1)
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"])
recvBuffer(s, L)
return True
proc writeStatusOkTextContent*(c: TSocket, contentType = "text/html") =
proc writeStatusOkTextContent*(c: Socket, contentType = "text/html") =
## sends the following string to the socket `c`::
##
## 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" &
"Content-Type: $1\r\L\r\L" % contentType)
proc run*(handleRequest: proc (client: TSocket, input: string,
headers: PStringTable): bool {.nimcall,gcsafe.},
port = TPort(4000)) =
proc run*(handleRequest: proc (client: Socket, input: string,
headers: StringTableRef): bool {.nimcall,gcsafe.},
port = Port(4000)) =
## encapsulates the SCGI object and main loop.
var s: TScgiState
var s: ScgiState
s.open(port)
var stop = false
while not stop:
@@ -166,11 +170,11 @@ proc run*(handleRequest: proc (client: TSocket, input: string,
# -- AsyncIO start
proc recvBufferAsync(client: PAsyncClient, L: int): TReadLineResult =
proc recvBufferAsync(client: AsyncClient, L: int): ReadLineResult =
result = ReadPartialLine
var data = ""
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)
if ret == 0 and data == "":
client.c.close()
@@ -181,16 +185,16 @@ proc recvBufferAsync(client: PAsyncClient, L: int): TReadLineResult =
if ret == L:
return ReadFullLine
proc checkCloseSocket(client: PAsyncClient) =
proc checkCloseSocket(client: AsyncClient) =
if not client.c.isClosed:
if client.c.isSendDataBuffered:
client.c.setHandleWrite do (s: PAsyncSocket):
client.c.setHandleWrite do (s: AsyncSocket):
if not s.isClosed and not s.isSendDataBuffered:
s.close()
s.delHandleWrite()
else: client.c.close()
proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) =
proc handleClientRead(client: AsyncClient, s: AsyncScgiState) =
case client.mode
of ClientReadChar:
while true:
@@ -203,7 +207,7 @@ proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) =
if ret == -1:
return # No more data available
if d[0] notin strutils.digits:
if d[0] != ':': scgiError("':' after length expected")
if d[0] != ':': raiseScgiError("':' after length expected")
break
client.dataLen = client.dataLen * 10 + ord(d[0]) - ord('0')
client.mode = ClientReadHeaders
@@ -213,7 +217,7 @@ proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) =
case ret
of ReadFullLine:
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
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)
checkCloseSocket(client)
proc handleAccept(sock: PAsyncSocket, s: PAsyncScgiState) =
var client: PAsyncSocket
proc handleAccept(sock: AsyncSocket, s: AsyncScgiState) =
var client: AsyncSocket
new(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: "")
client.handleRead =
proc (sock: PAsyncSocket) =
proc (sock: AsyncSocket) =
handleClientRead(asyncClient, s)
s.disp.register(client)
proc open*(handleRequest: proc (client: PAsyncSocket,
input: string, headers: PStringTable) {.
proc open*(handleRequest: proc (client: AsyncSocket,
input: string, headers: StringTableRef) {.
closure, gcsafe.},
port = TPort(4000), address = "127.0.0.1",
reuseAddr = false): PAsyncScgiState =
## Creates an ``PAsyncScgiState`` object which serves as a SCGI server.
port = Port(4000), address = "127.0.0.1",
reuseAddr = false): AsyncScgiState =
## Creates an ``AsyncScgiState`` object which serves as a SCGI server.
##
## After the execution of ``handleRequest`` the client socket will be closed
## automatically unless it has already been closed.
var cres: PAsyncScgiState
var cres: AsyncScgiState
new(cres)
cres.asyncServer = AsyncSocket()
cres.asyncServer.handleAccept = proc (s: PAsyncSocket) = handleAccept(s, cres)
cres.asyncServer.handleAccept = proc (s: AsyncSocket) = handleAccept(s, cres)
if reuseAddr:
cres.asyncServer.setSockOpt(OptReuseAddr, True)
cres.asyncServer.setSockOpt(OptReuseAddr, true)
bindAddr(cres.asyncServer, port, address)
listen(cres.asyncServer)
cres.handleRequest = handleRequest
result = cres
proc register*(d: PDispatcher, s: PAsyncScgiState): PDelegate {.discardable.} =
proc register*(d: Dispatcher, s: AsyncScgiState): Delegate {.discardable.} =
## Registers ``s`` with dispatcher ``d``.
result = d.register(s.asyncServer)
s.disp = d
proc close*(s: PAsyncScgiState) =
## Closes the ``PAsyncScgiState``.
proc close*(s: AsyncScgiState) =
## Closes the ``AsyncScgiState``.
s.asyncServer.close()
when false:
var counter = 0
proc handleRequest(client: TSocket, input: string,
headers: PStringTable): bool {.procvar.} =
proc handleRequest(client: Socket, input: string,
headers: StringTableRef): bool {.procvar.} =
inc(counter)
client.writeStatusOkTextContent()
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
#
# See the file "copying.txt", included in this
@@ -18,57 +18,57 @@ elif defined(windows):
else:
import posix
proc hash*(x: TSocketHandle): THash {.borrow.}
proc `$`*(x: TSocketHandle): string {.borrow.}
proc hash*(x: SocketHandle): THash {.borrow.}
proc `$`*(x: SocketHandle): string {.borrow.}
type
TEvent* = enum
Event* = enum
EvRead, EvWrite
PSelectorKey* = ref object
fd*: TSocketHandle
events*: set[TEvent] ## The events which ``fd`` listens for.
data*: PObject ## User object.
SelectorKey* = ref object
fd*: SocketHandle
events*: set[Event] ## The events which ``fd`` listens for.
data*: RootRef ## User object.
TReadyInfo* = tuple[key: PSelectorKey, events: set[TEvent]]
ReadyInfo* = tuple[key: SelectorKey, events: set[Event]]
when defined(nimdoc):
type
PSelector* = ref object
Selector* = ref object
## An object which holds file descripters to be checked for read/write
## status.
fds: TTable[TSocketHandle, PSelectorKey]
fds: Table[SocketHandle, SelectorKey]
proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent],
data: PObject): PSelectorKey {.discardable.} =
proc register*(s: Selector, fd: SocketHandle, events: set[Event],
data: RootRef): SelectorKey {.discardable.} =
## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent
## ``events``.
proc update*(s: PSelector, fd: TSocketHandle,
events: set[TEvent]): PSelectorKey {.discardable.} =
proc update*(s: Selector, fd: SocketHandle,
events: set[Event]): SelectorKey {.discardable.} =
## 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
## for which the ``fd`` was bound. This is contrary to the ``events`` field
## of the ``TReadyInfo`` tuple which determines which events are ready
## on the ``fd``.
proc contains*(s: PSelector, fd: TSocketHandle): bool =
proc contains*(s: Selector, fd: SocketHandle): bool =
## 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``.
elif defined(linux):
type
PSelector* = ref object
Selector* = ref object
epollFD: cint
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:
result.events = EPOLLIN
if EvWrite in events:
@@ -76,22 +76,22 @@ elif defined(linux):
result.events = result.events or EPOLLRDHUP
result.data.fd = fd.cint
proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent],
data: PObject): PSelectorKey {.discardable.} =
proc register*(s: Selector, fd: SocketHandle, events: set[Event],
data: RootRef): SelectorKey {.discardable.} =
## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent
## ``events``.
var event = createEventStruct(events, fd)
if events != {}:
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
result = key
proc update*(s: PSelector, fd: TSocketHandle,
events: set[TEvent]): PSelectorKey {.discardable.} =
proc update*(s: Selector, fd: SocketHandle,
events: set[Event]): SelectorKey {.discardable.} =
## Updates the events which ``fd`` wants notifications for.
if s.fds[fd].events != events:
if events == {}:
@@ -101,7 +101,7 @@ elif defined(linux):
# because its got fds which are waiting for no events and
# are therefore constantly ready. (leading to 100% CPU usage).
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
OSError(OSLastError())
raiseOSError(osLastError())
s.fds[fd].events = events
else:
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
# be re-registered.
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
OSError(OSLastError())
raiseOSError(osLastError())
else:
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
OSError(OSLastError())
raiseOSError(osLastError())
s.fds[fd].events = events
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:
let err = OSLastError()
let err = osLastError()
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]
s.fds.del(fd)
proc close*(s: PSelector) =
if s.epollFD.close() != 0: OSError(OSLastError())
proc close*(s: Selector) =
if s.epollFD.close() != 0: raiseOSError(osLastError())
dealloc(addr s.events) # TODO: Test this
proc epollHasFd(s: PSelector, fd: TSocketHandle): bool =
proc epollHasFd(s: Selector, fd: SocketHandle): bool =
result = true
var event = createEventStruct(s.fds[fd].events, fd)
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
let err = osLastError()
if err.cint in {ENOENT, EBADF}:
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
## for which the ``fd`` was bound. This is contrary to the ``events`` field
@@ -146,12 +146,12 @@ elif defined(linux):
## on the ``fd``.
result = @[]
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 @[]
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 EPOLLOUT) != 0: evSet = evSet + {EvWrite}
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)
proc newSelector*(): PSelector =
proc newSelector*(): Selector =
new result
result.epollFD = epoll_create(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:
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.
if s.fds.hasKey(fd):
# Ensure the underlying epoll instance still contains this fd.
@@ -179,46 +179,46 @@ elif defined(linux):
else:
return false
proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey =
proc `[]`*(s: Selector, fd: SocketHandle): SelectorKey =
## Retrieves the selector key for ``fd``.
return s.fds[fd]
elif not defined(nimdoc):
# TODO: kqueue for bsd/mac os x.
type
PSelector* = ref object
fds: TTable[TSocketHandle, PSelectorKey]
Selector* = ref object
fds: Table[SocketHandle, SelectorKey]
proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent],
data: PObject): PSelectorKey {.discardable.} =
proc register*(s: Selector, fd: SocketHandle, events: set[Event],
data: RootRef): SelectorKey {.discardable.} =
if s.fds.hasKey(fd):
raise newException(EInvalidValue, "File descriptor already exists.")
var sk = PSelectorKey(fd: fd, events: events, data: data)
raise newException(ValueError, "File descriptor already exists.")
var sk = SelectorKey(fd: fd, events: events, data: data)
s.fds[fd] = sk
result = sk
proc update*(s: PSelector, fd: TSocketHandle,
events: set[TEvent]): PSelectorKey {.discardable.} =
proc update*(s: Selector, fd: SocketHandle,
events: set[Event]): SelectorKey {.discardable.} =
## Updates the events which ``fd`` wants notifications for.
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
result = s.fds[fd]
proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} =
proc unregister*(s: Selector, fd: SocketHandle): SelectorKey {.discardable.} =
result = s.fds[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:
var seconds = timeout div 1000
result.tv_sec = seconds.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) =
FD_ZERO(rd); FD_ZERO(wr)
for k, v in pairs(fds):
@@ -229,22 +229,22 @@ elif not defined(nimdoc):
m = max(m, int(k))
FD_SET(k, wr)
proc getReadyFDs(rd, wr: var TFdSet, fds: TTable[TSocketHandle, PSelectorKey]):
seq[TReadyInfo] =
proc getReadyFDs(rd, wr: var FdSet, fds: Table[SocketHandle, SelectorKey]):
seq[ReadyInfo] =
result = @[]
for k, v in pairs(fds):
var events: set[TEvent] = {}
var events: set[Event] = {}
if FD_ISSET(k, rd) != 0'i32:
events = events + {EvRead}
if FD_ISSET(k, wr) != 0'i32:
events = events + {EvWrite}
result.add((v, events))
proc select(fds: TTable[TSocketHandle, PSelectorKey], timeout = 500):
seq[TReadyInfo] =
var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout)
proc select(fds: Table[SocketHandle, SelectorKey], timeout = 500):
seq[ReadyInfo] =
var tv {.noInit.}: TimeVal = timeValFromMilliseconds(timeout)
var rd, wr: TFdSet
var rd, wr: FdSet
var m = 0
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))
if retCode < 0:
OSError(OSLastError())
raiseOSError(osLastError())
elif retCode == 0:
return @[]
else:
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)
proc newSelector*(): PSelector =
proc newSelector*(): Selector =
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)
proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey =
proc `[]`*(s: Selector, fd: SocketHandle): SelectorKey =
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
## than checking if the file descriptor is in the selector because it
## 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.
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):
# Select()
import sockets
type
PSockWrapper = ref object of PObject
sock: TSocket
SockWrapper = ref object of RootObj
sock: Socket
var sock = socket()
if sock == sockets.InvalidSocket: osError(osLastError())
if sock == sockets.InvalidSocket: raiseOSError(osLastError())
#sock.setBlocking(false)
sock.connect("irc.freenode.net", TPort(6667))
sock.connect("irc.freenode.net", Port(6667))
var selector = newSelector()
var data = PSockWrapper(sock: sock)
var data = SockWrapper(sock: sock)
let key = selector.register(sock.getFD, {EvWrite}, data)
var i = 0
while true:

View File

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

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2013 Andreas Rumpf, Dominik Picheta
#
# 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
## support buffered sockets (mainly the recvFrom function).
##
## Most procedures raise EOS on error, but some may return ``-1`` or a boolean
## ``false``.
## Most procedures raise OSError on error, but some may return ``-1`` or a
## boolean ``false``.
##
## 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
@@ -47,25 +47,29 @@ else:
when defined(ssl):
type
ESSL* = object of ESynch
SSLError* = object of Exception
TSSLCVerifyMode* = enum
SSLCVerifyMode* = enum
CVerifyNone, CVerifyPeer
TSSLProtVersion* = enum
SSLProtVersion* = enum
protSSLv2, protSSLv3, protTLSv1, protSSLv23
PSSLContext* = distinct PSSLCTX
SSLContext* = distinct PSSLCTX
TSSLAcceptResult* = enum
SSLAcceptResult* = enum
AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess
{.deprecated: [ESSL: SSLError, TSSLCVerifyMode: SSLCVerifyMode,
TSSLProtVersion: SSLProtVersion, PSSLContext: SSLContext,
TSSLAcceptResult: SSLAcceptResult].}
const
BufferSize*: int = 4000 ## size of a buffered socket's buffer
type
TSocketImpl = object ## socket type
fd: TSocketHandle
fd: SocketHandle
case isBuffered: bool # determines whether this socket is buffered.
of true:
buffer: array[0..BufferSize, char]
@@ -76,31 +80,31 @@ type
case isSsl: bool
of true:
sslHandle: PSSL
sslContext: PSSLContext
sslContext: SSLContext
sslNoHandshake: bool # True if needs handshake.
sslHasPeekChar: bool
sslPeekChar: char
of false: nil
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
## here are unsupported.
AF_UNIX, ## for local socket (using a file). Unsupported on Windows.
AF_INET = 2, ## for network protocol IPv4 or
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_DGRAM = 2, ## datagram service or Datagram Sockets
SOCK_RAW = 3, ## raw protocols atop the network layer.
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_UDP = 17, ## User datagram protocol.
IPPROTO_IP, ## Internet protocol. Unsupported on Windows.
@@ -108,33 +112,39 @@ type
IPPROTO_RAW, ## Raw IP Packets 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
aliases*: seq[string]
port*: TPort
port*: Port
proto*: string
Thostent* {.pure, final.} = object ## information about a given host
Hostent* = object ## information about a given host
name*: string
aliases*: seq[string]
addrtype*: TDomain
addrtype*: Domain
length*: int
addrList*: seq[string]
TSOBool* = enum ## Boolean socket options.
SOBool* = enum ## Boolean socket options.
OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive,
OptOOBInline, OptReuseAddr
TRecvLineResult* = enum ## result for recvLineAsync
RecvLineResult* = enum ## result for recvLineAsync
RecvFullLine, RecvPartialLine, RecvDisconnected, RecvFail
TReadLineResult* = enum ## result for readLineAsync
ReadLineResult* = enum ## result for readLineAsync
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
invalidSocket*: TSocket = nil ## invalid socket
invalidSocket*: Socket = nil ## invalid socket
when defined(windows):
let
@@ -143,7 +153,7 @@ else:
let
osInvalidSocket = posix.INVALID_SOCKET
proc newTSocket(fd: TSocketHandle, isBuff: bool): TSocket =
proc newTSocket(fd: SocketHandle, isBuff: bool): Socket =
if fd == osInvalidSocket:
return nil
new(result)
@@ -153,10 +163,10 @@ proc newTSocket(fd: TSocketHandle, isBuff: bool): TSocket =
result.currPos = 0
result.nonblocking = false
proc `==`*(a, b: TPort): bool {.borrow.}
proc `==`*(a, b: Port): bool {.borrow.}
## ``==`` for ports.
proc `$`*(p: TPort): string {.borrow.}
proc `$`*(p: Port): string {.borrow.}
## returns the port number as a string
proc ntohl*(x: int32): int32 =
@@ -189,14 +199,14 @@ proc htons*(x: int16): int16 =
result = sockets.ntohs(x)
when defined(Posix):
proc toInt(domain: TDomain): cint =
proc toInt(domain: Domain): cint =
case domain
of AF_UNIX: result = posix.AF_UNIX
of AF_INET: result = posix.AF_INET
of AF_INET6: result = posix.AF_INET6
else: discard
proc toInt(typ: TType): cint =
proc toInt(typ: SockType): cint =
case typ
of SOCK_STREAM: result = posix.SOCK_STREAM
of SOCK_DGRAM: result = posix.SOCK_DGRAM
@@ -204,7 +214,7 @@ when defined(Posix):
of SOCK_RAW: result = posix.SOCK_RAW
else: discard
proc toInt(p: TProtocol): cint =
proc toInt(p: Protocol): cint =
case p
of IPPROTO_TCP: result = posix.IPPROTO_TCP
of IPPROTO_UDP: result = posix.IPPROTO_UDP
@@ -215,13 +225,13 @@ when defined(Posix):
else: discard
else:
proc toInt(domain: TDomain): cint =
proc toInt(domain: Domain): cint =
result = toU16(ord(domain))
proc toInt(typ: TType): cint =
proc toInt(typ: SockType): cint =
result = cint(ord(typ))
proc toInt(p: TProtocol): cint =
proc toInt(p: Protocol): cint =
result = cint(ord(p))
proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
@@ -336,7 +346,7 @@ when defined(ssl):
if SSLSetFd(socket.sslHandle, socket.fd) != 1:
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.
##
## 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):
if lastError.int32 == WSAEWOULDBLOCK:
return
else: osError(lastError)
else: raiseOSError(lastError)
else:
if lastError.int32 == EAGAIN or lastError.int32 == EWOULDBLOCK:
return
else: osError(lastError)
else: osError(lastError)
else: raiseOSError(lastError)
else: raiseOSError(lastError)
proc listen*(socket: TSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
## Marks ``socket`` as accepting connections.
## ``Backlog`` specifies the maximum length of the
## 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.} =
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)
if gaiResult != 0'i32:
when defined(windows):
osError(osLastError())
raiseOSError(osLastError())
else:
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)
if bindSocket(socket.fd, cast[ptr TSockAddr](addr(name)),
sizeof(name).TSocklen) < 0'i32:
osError(osLastError())
raiseOSError(osLastError())
else:
var hints: Taddrinfo
var aiList: ptr Taddrinfo = nil
@@ -449,7 +459,7 @@ proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {.
hints.ai_protocol = toInt(IPPROTO_TCP)
gaiNim(address, port, hints, aiList)
if bindSocket(socket.fd, aiList.ai_addr, aiList.ai_addrlen.TSocklen) < 0'i32:
osError(osLastError())
raiseOSError(osLastError())
proc getSockName*(socket: TSocket): TPort =
## returns the socket's associated port number.
@@ -463,7 +473,7 @@ proc getSockName*(socket: TSocket): TPort =
var namelen = sizeof(name).TSocklen
if getsockname(socket.fd, cast[ptr TSockAddr](addr(name)),
addr(namelen)) == -1'i32:
osError(osLastError())
raiseOSError(osLastError())
result = TPort(sockets.ntohs(name.sin_port))
template acceptAddrPlain(noClientRet, successRet: expr,
@@ -484,7 +494,7 @@ template acceptAddrPlain(noClientRet, successRet: expr,
return
else:
return noClientRet
else: osError(err)
else: raiseOSError(err)
else:
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
client = invalidSocket
@@ -493,7 +503,7 @@ template acceptAddrPlain(noClientRet, successRet: expr,
return
else:
return noClientRet
else: osError(err)
else: raiseOSError(err)
else:
client.fd = sock
client.isBuffered = server.isBuffered
@@ -680,7 +690,7 @@ proc getHostByAddr*(ip: string): Thostent {.tags: [ReadIOEffect].} =
when defined(windows):
var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
cint(sockets.AF_INET))
if s == nil: osError(osLastError())
if s == nil: raiseOSError(osLastError())
else:
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).TSocklen,
cint(posix.AF_INET))
@@ -707,7 +717,7 @@ proc getHostByName*(name: string): Thostent {.tags: [ReadIOEffect].} =
var s = winlean.gethostbyname(name)
else:
var s = posix.gethostbyname(name)
if s == nil: osError(osLastError())
if s == nil: raiseOSError(osLastError())
result.name = $s.h_name
result.aliases = cstringArrayToSeq(s.h_aliases)
when defined(windows):
@@ -729,7 +739,7 @@ proc getSockOptInt*(socket: TSocket, level, optname: int): int {.
var size = sizeof(res).TSocklen
if getsockopt(socket.fd, cint(level), cint(optname),
addr(res), addr(size)) < 0'i32:
osError(osLastError())
raiseOSError(osLastError())
result = int(res)
proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {.
@@ -738,7 +748,7 @@ proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {.
var value = cint(optval)
if setsockopt(socket.fd, cint(level), cint(optname), addr(value),
sizeof(value).TSocklen) < 0'i32:
osError(osLastError())
raiseOSError(osLastError())
proc toCInt(opt: TSOBool): cint =
case opt
@@ -757,7 +767,7 @@ proc getSockOpt*(socket: TSocket, opt: TSOBool, level = SOL_SOCKET): bool {.
var size = sizeof(res).TSocklen
if getsockopt(socket.fd, cint(level), toCInt(opt),
addr(res), addr(size)) < 0'i32:
osError(osLastError())
raiseOSError(osLastError())
result = res != 0
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)
if setsockopt(socket.fd, cint(level), toCInt(opt), addr(valuei),
sizeof(valuei).TSocklen) < 0'i32:
osError(osLastError())
raiseOSError(osLastError())
proc connect*(socket: TSocket, address: string, port = TPort(0),
af: TDomain = AF_INET) {.tags: [ReadIOEffect].} =
@@ -784,7 +794,7 @@ proc connect*(socket: TSocket, address: string, port = TPort(0),
gaiNim(address, port, hints, aiList)
# try all possibilities:
var success = false
var lastError: TOSErrorCode
var lastError: OSErrorCode
var it = aiList
while it != nil:
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
freeaddrinfo(aiList)
if not success: osError(lastError)
if not success: raiseOSError(lastError)
when defined(ssl):
if socket.isSSL:
@@ -847,7 +857,7 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0),
gaiNim(name, port, hints, aiList)
# try all possibilities:
var success = false
var lastError: TOSErrorCode
var lastError: OSErrorCode
var it = aiList
while it != nil:
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
freeaddrinfo(aiList)
if not success: osError(lastError)
if not success: raiseOSError(lastError)
when defined(ssl):
if socket.isSSL:
socket.sslNoHandshake = true
@@ -1133,7 +1143,7 @@ proc waitFor(socket: TSocket, waited: var float, timeout, size: int,
result = min(result, size)
else:
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):
if socket.isSSL:
@@ -1147,9 +1157,9 @@ proc waitFor(socket: TSocket, waited: var float, timeout, size: int,
var s = @[socket]
var startTime = epochTime()
let selRet = select(s, timeout - int(waited * 1000.0))
if selRet < 0: osError(osLastError())
if selRet < 0: raiseOSError(osLastError())
if selRet != 1:
raise newException(ETimeout, "Call to '" & funcName & "' timed out.")
raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
waited += (epochTime() - startTime)
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)
if result < 0:
data.setLen(0)
socket.socketError(result)
socket.raiseSocketError(result)
data.setLen(result)
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)
if result < 0:
data.setLen(0)
socket.socketError(async = true)
socket.raiseSocketError(async = true)
result = -1
data.setLen(result)
@@ -1296,14 +1306,14 @@ proc readLine*(socket: TSocket, line: var TaintedString, timeout = -1) {.
var c: char
discard waitFor(socket, waited, timeout, 1, "readLine")
var n = recv(socket, addr(c), 1)
if n < 0: socket.socketError()
if n < 0: socket.raiseSocketError()
elif n == 0: return
if c == '\r':
discard waitFor(socket, waited, timeout, 1, "readLine")
n = peekChar(socket, c)
if n > 0 and c == '\L':
discard recv(socket, addr(c), 1)
elif n <= 0: socket.socketError()
elif n <= 0: socket.raiseSocketError()
addNLIfEmpty()
return
elif c == '\L':
@@ -1357,7 +1367,7 @@ proc readLineAsync*(socket: TSocket,
setLen(line.string, 0)
template errorOrNone =
socket.socketError(async = true)
socket.raiseSocketError(async = true)
return ReadNone
while true:
@@ -1390,7 +1400,7 @@ proc recv*(socket: TSocket): TaintedString {.tags: [ReadIOEffect], deprecated.}
var pos = 0
while true:
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)
if bytesRead != bufSize-1: break
# increase capacity:
@@ -1463,11 +1473,11 @@ proc recvAsync*(socket: TSocket, s: var TaintedString): bool {.
when defined(windows):
if err.int32 == WSAEWOULDBLOCK:
return false
else: osError(err)
else: raiseOSError(err)
else:
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
return false
else: osError(err)
else: raiseOSError(err)
setLen(s.string, pos + bytesRead)
if bytesRead != bufSize-1: break
@@ -1516,11 +1526,11 @@ proc recvFromAsync*(socket: TSocket, data: var string, length: int,
when defined(windows):
if err.int32 == WSAEWOULDBLOCK:
return false
else: osError(err)
else: raiseOSError(err)
else:
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
return false
else: osError(err)
else: raiseOSError(err)
proc skip*(socket: TSocket) {.tags: [ReadIOEffect], deprecated.} =
## 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:
SSLError()
osError(osLastError())
raiseOSError(osLastError())
if sent != data.len:
raise newException(OSError, "Could not send all data.")
@@ -1607,11 +1617,11 @@ proc sendAsync*(socket: TSocket, data: string): int {.tags: [WriteIOEffect].} =
when defined(windows):
if err.int32 == WSAEINPROGRESS:
return 0
else: osError(err)
else: raiseOSError(err)
else:
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
return 0
else: osError(err)
else: raiseOSError(err)
proc trySend*(socket: TSocket, data: string): bool {.tags: [WriteIOEffect].} =
@@ -1667,15 +1677,15 @@ proc setBlocking(s: TSocket, blocking: bool) =
when defined(Windows):
var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
if ioctlsocket(s.fd, FIONBIO, addr(mode)) == -1:
osError(osLastError())
raiseOSError(osLastError())
else: # BSD sockets
var x: int = fcntl(s.fd, F_GETFL, 0)
if x == -1:
osError(osLastError())
raiseOSError(osLastError())
else:
var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
if fcntl(s.fd, F_SETFL, mode) == -1:
osError(osLastError())
raiseOSError(osLastError())
s.nonblocking = not blocking
discard """ proc setReuseAddr*(s: TSocket) =
@@ -1715,6 +1725,6 @@ proc isBlocking*(socket: TSocket): bool = not socket.nonblocking
when defined(Windows):
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
#
# See the file "copying.txt", included in this
@@ -8,8 +8,8 @@
#
## This module provides a stream interface and two implementations thereof:
## the `PFileStream` and the `PStringStream` which implement the stream
## interface for Nimrod file objects (`TFile`) and strings. Other modules
## the `FileStream` and the `StringStream` which implement the stream
## interface for Nimrod file objects (`File`) and strings. Other modules
## may provide other implementations for this standard stream interface.
include "system/inclrtl"
@@ -19,81 +19,83 @@ proc newEIO(msg: string): ref IOError =
result.msg = msg
type
PStream* = ref TStream
TStream* = object of RootObj ## Stream interface that supports
## writing or reading. Note that these fields
## here shouldn't be used directly. They are
## accessible so that a stream implementation
## can override them.
closeImpl*: proc (s: PStream) {.nimcall, tags: [], gcsafe.}
atEndImpl*: proc (s: PStream): bool {.nimcall, tags: [], gcsafe.}
setPositionImpl*: proc (s: PStream, pos: int) {.nimcall, tags: [], gcsafe.}
getPositionImpl*: proc (s: PStream): int {.nimcall, tags: [], gcsafe.}
readDataImpl*: proc (s: PStream, buffer: pointer,
Stream* = ref StreamObj
StreamObj* = object of RootObj ## Stream interface that supports
## writing or reading. Note that these fields
## here shouldn't be used directly. They are
## accessible so that a stream implementation
## can override them.
closeImpl*: proc (s: Stream) {.nimcall, tags: [], gcsafe.}
atEndImpl*: proc (s: Stream): bool {.nimcall, tags: [], gcsafe.}
setPositionImpl*: proc (s: Stream, pos: int) {.nimcall, tags: [], gcsafe.}
getPositionImpl*: proc (s: Stream): int {.nimcall, tags: [], gcsafe.}
readDataImpl*: proc (s: Stream, buffer: pointer,
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.}
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.
if not isNil(s.flushImpl): s.flushImpl(s)
proc close*(s: PStream) =
proc close*(s: Stream) =
## closes the stream `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`.
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
## been read.
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
## been read.
result = s.atEndImpl(s)
proc setPosition*(s: PStream, pos: int) =
proc setPosition*(s: Stream, pos: int) =
## sets the position `pos` of the stream `s`.
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`.
s.setPositionImpl(s, pos)
proc getPosition*(s: PStream): int =
proc getPosition*(s: Stream): int =
## retrieves the current position in the stream `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`.
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.
result = s.readDataImpl(s, buffer, bufLen)
proc readData*(s, unused: PStream, buffer: pointer,
proc readData*(s, unused: Stream, buffer: pointer,
bufLen: int): int {.deprecated.} =
## low level proc that reads data into an untyped `buffer` of `bufLen` size.
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
## to the stream `s`.
s.writeDataImpl(s, buffer, bufLen)
proc writeData*(s, unused: PStream, buffer: pointer,
proc writeData*(s, unused: Stream, buffer: pointer,
bufLen: int) {.deprecated.} =
## low level proc that writes an untyped `buffer` of `bufLen` size
## to the stream `s`.
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:
##
## .. code-block:: Nimrod
@@ -103,63 +105,63 @@ proc write*[T](s: PStream, x: T) =
shallowCopy(y, x)
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
## terminating zero is written.
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
## by a new line. No length field or terminating zero is written.
for str in args: write(s, str)
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`.
if readData(s, addr(result), sizeof(T)) != sizeof(T):
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.
## Returns '\0' as an EOF marker.
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.
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.
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.
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.
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.
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.
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.
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
## an error occured.
result = newString(length).TaintedString
var L = readData(s, addr(string(result)[0]), length)
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
## ``nil``! May throw an IO exception.
## 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)
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
## `EIO` if an error occured.
result = TaintedString""
@@ -194,42 +196,44 @@ proc readLine*(s: PStream): TaintedString =
result.string.add(c)
type
PStringStream* = ref TStringStream ## a stream that encapsulates a string
TStringStream* = object of TStream
StringStream* = ref StringStreamObj ## a stream that encapsulates a string
StringStreamObj* = object of StreamObj
data*: string
pos: int
proc ssAtEnd(s: PStream): bool =
var s = PStringStream(s)
{.deprecated: [PStringStream: StringStream, TStringStream: StringStreamObj].}
proc ssAtEnd(s: Stream): bool =
var s = StringStream(s)
return s.pos >= s.data.len
proc ssSetPosition(s: PStream, pos: int) =
var s = PStringStream(s)
proc ssSetPosition(s: Stream, pos: int) =
var s = StringStream(s)
s.pos = clamp(pos, 0, s.data.high)
proc ssGetPosition(s: PStream): int =
var s = PStringStream(s)
proc ssGetPosition(s: Stream): int =
var s = StringStream(s)
return s.pos
proc ssReadData(s: PStream, buffer: pointer, bufLen: int): int =
var s = PStringStream(s)
proc ssReadData(s: Stream, buffer: pointer, bufLen: int): int =
var s = StringStream(s)
result = min(bufLen, s.data.len - s.pos)
if result > 0:
copyMem(buffer, addr(s.data[s.pos]), result)
inc(s.pos, result)
proc ssWriteData(s: PStream, buffer: pointer, bufLen: int) =
var s = PStringStream(s)
proc ssWriteData(s: Stream, buffer: pointer, bufLen: int) =
var s = StringStream(s)
if bufLen > 0:
setLen(s.data, s.data.len + bufLen)
copyMem(addr(s.data[s.pos]), buffer, bufLen)
inc(s.pos, bufLen)
proc ssClose(s: PStream) =
var s = PStringStream(s)
proc ssClose(s: Stream) =
var s = StringStream(s)
s.data = nil
proc newStringStream*(s: string = ""): PStringStream =
proc newStringStream*(s: string = ""): StringStream =
## creates a new stream from the string `s`.
new(result)
result.data = s
@@ -244,27 +248,28 @@ proc newStringStream*(s: string = ""): PStringStream =
when not defined(js):
type
PFileStream* = ref TFileStream ## a stream that encapsulates a `TFile`
TFileStream* = object of TStream
FileStream* = ref FileStreamObj ## a stream that encapsulates a `TFile`
FileStreamObj* = object of Stream
f: File
{.deprecated: [PFileStream: FileStream, TFileStream: FileStreamObj].}
proc fsClose(s: PStream) =
if PFileStream(s).f != nil:
close(PFileStream(s).f)
PFileStream(s).f = nil
proc fsFlush(s: PStream) = flushFile(PFileStream(s).f)
proc fsAtEnd(s: PStream): bool = return endOfFile(PFileStream(s).f)
proc fsSetPosition(s: PStream, pos: int) = setFilePos(PFileStream(s).f, pos)
proc fsGetPosition(s: PStream): int = return int(getFilePos(PFileStream(s).f))
proc fsClose(s: Stream) =
if FileStream(s).f != nil:
close(FileStream(s).f)
FileStream(s).f = nil
proc fsFlush(s: Stream) = flushFile(FileStream(s).f)
proc fsAtEnd(s: Stream): bool = return endOfFile(FileStream(s).f)
proc fsSetPosition(s: Stream, pos: int) = setFilePos(FileStream(s).f, pos)
proc fsGetPosition(s: Stream): int = return int(getFilePos(FileStream(s).f))
proc fsReadData(s: PStream, buffer: pointer, bufLen: int): int =
result = readBuffer(PFileStream(s).f, buffer, bufLen)
proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int =
result = readBuffer(FileStream(s).f, buffer, bufLen)
proc fsWriteData(s: PStream, buffer: pointer, bufLen: int) =
if writeBuffer(PFileStream(s).f, buffer, bufLen) != bufLen:
proc fsWriteData(s: Stream, buffer: pointer, bufLen: int) =
if writeBuffer(FileStream(s).f, buffer, bufLen) != bufLen:
raise newEIO("cannot write to stream")
proc newFileStream*(f: File): PFileStream =
proc newFileStream*(f: File): FileStream =
## creates a new stream from the file `f`.
new(result)
result.f = f
@@ -276,7 +281,7 @@ when not defined(js):
result.writeDataImpl = fsWriteData
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`.
## If the file cannot be opened, nil is returned. See the `system
## <system.html>`_ module for a list of available TFileMode enums.
@@ -288,45 +293,47 @@ when true:
discard
else:
type
TFileHandle* = cint ## Operating system file handle
PFileHandleStream* = ref TFileHandleStream
TFileHandleStream* = object of TStream
handle*: TFileHandle
FileHandleStream* = ref FileHandleStreamObj
FileHandleStreamObj* = object of Stream
handle*: FileHandle
pos: int
proc newEOS(msg: string): ref EOS =
{.deprecated: [PFileHandleStream: FileHandleStream,
TFileHandleStream: FileHandleStreamObj].}
proc newEOS(msg: string): ref OSError =
new(result)
result.msg = msg
proc hsGetPosition(s: PFileHandleStream): int =
proc hsGetPosition(s: FileHandleStream): int =
return s.pos
when defined(windows):
# do not import windows as this increases compile times:
nil
discard
else:
import posix
proc hsSetPosition(s: PFileHandleStream, pos: int) =
proc hsSetPosition(s: FileHandleStream, pos: int) =
discard lseek(s.handle, pos, SEEK_SET)
proc hsClose(s: PFileHandleStream) = discard close(s.handle)
proc hsAtEnd(s: PFileHandleStream): bool =
proc hsClose(s: FileHandleStream) = discard close(s.handle)
proc hsAtEnd(s: FileHandleStream): bool =
var pos = hsGetPosition(s)
var theEnd = lseek(s.handle, 0, SEEK_END)
result = pos >= theEnd
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)
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:
raise newEIO("cannot write to stream")
inc(s.pos, bufLen)
proc newFileHandleStream*(handle: TFileHandle): PFileHandleStream =
proc newFileHandleStream*(handle: FileHandle): FileHandleStream =
new(result)
result.handle = handle
result.pos = 0
@@ -338,9 +345,9 @@ else:
result.writeData = hsWriteData
proc newFileHandleStream*(filename: string,
mode: TFileMode): PFileHandleStream =
when defined(windows):
nil
mode: FileMode): FileHandleStream =
when defined(windows):
discard
else:
var flags: cint
case mode

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
@@ -18,20 +18,23 @@ import
include "system/inclrtl"
type
TStringTableMode* = enum ## describes the tables operation mode
StringTableMode* = enum ## describes the tables operation mode
modeCaseSensitive, ## the table is case sensitive
modeCaseInsensitive, ## the table is case insensitive
modeStyleInsensitive ## the table is style insensitive
TKeyValuePair = tuple[key, val: string]
TKeyValuePairSeq = seq[TKeyValuePair]
TStringTable* = object of RootObj
KeyValuePair = tuple[key, val: string]
KeyValuePairSeq = seq[KeyValuePair]
StringTableObj* = object of RootObj
counter: int
data: TKeyValuePairSeq
mode: TStringTableMode
data: KeyValuePairSeq
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`.
result = t.counter
@@ -54,7 +57,7 @@ iterator values*(t: PStringTable): string =
yield t.data[h].val
type
TFormatFlag* = enum ## flags for the `%` operator
FormatFlag* = enum ## flags for the `%` operator
useEnvironment, ## use environment variable if the ``$key``
## is not found in the table
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
## in the table (or in the environment)
{.deprecated: [TFormatFlag: FormatFlag].}
# implementation
const

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
@@ -23,7 +23,7 @@ import parseutils
include "system/inclrtl"
type
TCharSet* = set[char] # for compatibility with Nim
TCharSet* {.deprecated.} = set[char] # for compatibility with Nim
const
Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'}
@@ -54,7 +54,7 @@ const
## make the `find() proc <#find,string,set[char],int>`_ find **invalid**
## characters in strings. Example:
##
## .. code-block:: nimrod
## .. code-block:: nim
## let invalid = AllChars - Digits
## doAssert "01234".find(invalid) == -1
## 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.
## The following example:
##
## .. code-block:: nimrod
## .. code-block:: nim
## for word in split(" this is an example "):
## writeln(stdout, word)
##
@@ -219,13 +219,13 @@ iterator split*(s: string, seps: set[char] = Whitespace): string =
##
## And the following code:
##
## .. code-block:: nimrod
## .. code-block:: nim
## for word in split(";;this;is;an;;example;;;", {';'}):
## writeln(stdout, word)
##
## ...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 separators = {' ', '-', ':', 'T'}
## 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
## separator, returning a string for each found character. The code:
##
## .. code-block:: nimrod
## .. code-block:: nim
## for word in split(";;this;is;an;;example;;;", ';'):
## writeln(stdout, word)
##
@@ -308,13 +308,13 @@ iterator splitLines*(s: string): string =
##
## Example:
##
## .. code-block:: nimrod
## .. code-block:: nim
## for line in splitLines("\nthis\nis\nan\n\nexample\n"):
## writeln(stdout, line)
##
## Results in:
##
## .. code-block:: nimrod
## .. code-block:: nim
## ""
## "this"
## "is"
@@ -417,7 +417,7 @@ proc parseInt*(s: string): int {.noSideEffect, procvar,
rtl, extern: "nsuParseInt".} =
## 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)
if L != s.len or L == 0:
raise newException(ValueError, "invalid integer: " & s)
@@ -426,7 +426,7 @@ proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect, procvar,
rtl, extern: "nsuParseBiggestInt".} =
## 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)
if L != s.len or L == 0:
raise newException(ValueError, "invalid integer: " & s)
@@ -434,7 +434,7 @@ proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect, procvar,
proc parseFloat*(s: string): float {.noSideEffect, procvar,
rtl, extern: "nsuParseFloat".} =
## 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).
var L = parseutils.parseFloat(s, result, 0)
if L != s.len or L == 0:
@@ -444,7 +444,7 @@ proc parseHexInt*(s: string): int {.noSideEffect, procvar,
rtl, extern: "nsuParseHexInt".} =
## 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
## within `s` are ignored.
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
## returns `true`. If ``s`` is one of the following values: ``n, no, false,
## 0, off``, then returns `false`. If ``s`` is something else a
## ``EInvalidValue`` exception is raised.
## ``ValueError`` exception is raised.
case normalize(s)
of "y", "yes", "true", "1", "on": result = true
of "n", "no", "false", "0", "off": result = false
@@ -480,7 +480,7 @@ proc parseBool*(s: string): bool =
proc parseEnum*[T: enum](s: string): 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.
for e in low(T)..high(T):
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
## the character `c`. You can use this proc to left align strings. Example:
##
## .. code-block:: nimrod
## .. code-block:: nim
## let
## width = 15
## 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
## proc <#repeatChar>`_. Example:
##
## .. code-block:: nimrod
## .. code-block:: nim
## assert align("abc", 4) == " abc"
## assert align("a", 0) == "a"
## 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`.
## Examples:
##
## .. code-block:: nimrod
## .. code-block:: nim
## for word in tokenize(" this is an example "):
## writeln(stdout, word)
##
## Results in:
##
## .. code-block:: nimrod
## .. code-block:: nim
## (" ", true)
## ("this", false)
## (" ", true)
@@ -676,7 +676,7 @@ proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect,
##
## A shorthand for:
##
## .. code-block:: nimrod
## .. code-block:: nim
## if dest.len > startLen: add(dest, sep)
##
## 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
## an array of integers:
##
## .. code-block:: nimrod
## .. code-block:: nim
## var arr = "["
## for x in items([2, 3, 5, 7, 11]):
## addSep(arr, startLen=len("["))
@@ -692,7 +692,7 @@ proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect,
## add(arr, "]")
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`.
for c in items(s):
if c notin theSet: return false
@@ -739,14 +739,14 @@ proc join*(a: openArray[string]): string {.
result = ""
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)
for i in 0..0xff: a[chr(i)] = m+1
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:
var
m = len(sub)
@@ -762,7 +762,7 @@ proc findAux(s, sub: string, start: int, a: TSkipTable): int =
return -1
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`.
##
## 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
proc replace*(s, sub: string, by = ""): string {.noSideEffect,
rtl, extern: "nsuReplaceStr", operator: 1.} =
rtl, extern: "nsuReplaceStr".} =
## Replaces `sub` in `s` by the string `by`.
var a {.noinit.}: TSkipTable
var a {.noinit.}: SkipTable
result = ""
preprocessSub(sub, a)
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
## replaced.
const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
var a {.noinit.}: TSkipTable
var a {.noinit.}: SkipTable
result = ""
preprocessSub(sub, a)
var i = 0
@@ -899,7 +899,7 @@ proc parseOctInt*(s: string): int {.noSideEffect,
rtl, extern: "nsuParseOctInt".} =
## 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
## `s` are ignored.
var i = 0
@@ -999,7 +999,7 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
## operations.
##
## 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)
var i = 0
if s[0 .. prefix.len-1] != prefix:
@@ -1138,12 +1138,14 @@ proc c_sprintf(buf, frmt: cstring) {.header: "<stdio.h>", importc: "sprintf",
varargs, noSideEffect.}
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
ffDecimal, ## use decimal floating point notation
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 {.
noSideEffect, operator: 2, rtl, extern: "nsu$1".} =
## 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)
result = $buf
proc formatFloat*(f: float, format: TFloatFormat = ffDefault,
proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
precision: range[0..32] = 16): string {.
noSideEffect, operator: 2, rtl, extern: "nsu$1".} =
## 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 =
## Rounds and formats `bytes`. Examples:
##
## .. code-block:: nimrod
## .. code-block:: nim
##
## formatSize(1'i64 shl 31 + 300'i64) == "2.204GB"
## formatSize(4096) == "4KB"
@@ -1280,12 +1282,12 @@ proc `%` *(formatstr: string, a: openArray[string]): string {.noSideEffect,
##
## This is best explained by an example:
##
## .. code-block:: nimrod
## .. code-block:: nim
## "$1 eats $2." % ["The cat", "fish"]
##
## Results in:
##
## .. code-block:: nimrod
## .. code-block:: nim
## "The cat eats fish."
##
## 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
## variable:
##
## .. code-block:: nimrod
## .. code-block:: nim
## "$# eats $#." % ["The cat", "fish"]
##
## 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.
## An example:
##
## .. code-block:: nimrod
## .. code-block:: nim
## "$animal eats $food." % ["animal", "The cat", "food", "fish"]
##
## Results in:
##
## .. code-block:: nimrod
## .. code-block:: nim
## "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.
result = newStringOfCap(formatstr.len + a.len shl 4)
addf(result, formatstr, a)
@@ -1350,7 +1352,7 @@ when isMainModule:
doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz "
doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc"
type TMyEnum = enum enA, enB, enC, enuD, enE
doAssert parseEnum[TMyEnum]("enu_D") == enuD
type MyEnum = enum enA, enB, enC, enuD, enE
doAssert parseEnum[MyEnum]("enu_D") == enuD
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
#
# See the file "copying.txt", included in this
# 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
##
@@ -28,11 +28,13 @@ proc findNormalized(x: string, inArray: openarray[string]): int =
return -1
type
EInvalidSubex* = object of EInvalidValue ## exception that is raised for
## an invalid subex
SubexError* = object of ValueError ## exception that is raised for
## an invalid subex
{.deprecated: [EInvalidSubex: SubexError].}
proc raiseInvalidFormat(msg: string) {.noinline.} =
raise newException(EInvalidSubex, "invalid format string: " & msg)
raise newException(SubexError, "invalid format string: " & msg)
type
TFormatParser = object {.pure, final.}
@@ -291,14 +293,16 @@ proc scanDollar(p: var TFormatParser, a: openarray[string], s: var string) =
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
## 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".} =
## The same as ``add(s, formatstr % a)``, but more efficient.
var p: TFormatParser
@@ -312,7 +316,7 @@ proc addf*(s: var string, formatstr: TSubex, a: varargs[string, `$`]) {.
emitChar(p, s, p.f[i])
inc(i)
proc `%` *(formatstr: TSubex, a: openarray[string]): string {.noSideEffect,
proc `%` *(formatstr: Subex, a: openarray[string]): string {.noSideEffect,
rtl, extern: "nfrmtFormatOpenArray".} =
## The `substitution`:idx: operator performs string substitutions in
## `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)
addf(result, formatstr, a)
proc `%` *(formatstr: TSubex, a: string): string {.noSideEffect,
proc `%` *(formatstr: Subex, a: string): string {.noSideEffect,
rtl, extern: "nfrmtFormatSingleElem".} =
## This is the same as ``formatstr % [a]``.
result = newStringOfCap(formatstr.string.len + a.len)
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".} =
## The `substitution`:idx: operator performs string substitutions in
## `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
#
# See the file "copying.txt", included in this
@@ -27,11 +27,11 @@ when defined(windows):
var hTemp = GetStdHandle(STD_OUTPUT_HANDLE)
if DuplicateHandle(GetCurrentProcess(), hTemp, GetCurrentProcess(),
addr(conHandle), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
osError(osLastError())
raiseOSError(osLastError())
proc getCursorPos(): tuple [x,y: int] =
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))
proc getAttributes(): int16 =
@@ -51,7 +51,7 @@ proc setCursorPos*(x, y: int) =
var c: TCOORD
c.x = int16(x)
c.y = int16(y)
if SetConsoleCursorPosition(conHandle, c) == 0: osError(osLastError())
if SetConsoleCursorPosition(conHandle, c) == 0: raiseOSError(osLastError())
else:
stdout.write("\e[" & $y & ';' & $x & 'f')
@@ -61,10 +61,10 @@ proc setCursorXPos*(x: int) =
when defined(windows):
var scrbuf: TCONSOLESCREENBUFFERINFO
var hStdout = conHandle
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: osError(osLastError())
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: raiseOSError(osLastError())
var origin = scrbuf.dwCursorPosition
origin.x = int16(x)
if SetConsoleCursorPosition(conHandle, origin) == 0: osError(osLastError())
if SetConsoleCursorPosition(conHandle, origin) == 0: raiseOSError(osLastError())
else:
stdout.write("\e[" & $x & 'G')
@@ -75,10 +75,10 @@ when defined(windows):
when defined(windows):
var scrbuf: TCONSOLESCREENBUFFERINFO
var hStdout = conHandle
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: osError(osLastError())
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: raiseOSError(osLastError())
var origin = scrbuf.dwCursorPosition
origin.y = int16(y)
if SetConsoleCursorPosition(conHandle, origin) == 0: osError(osLastError())
if SetConsoleCursorPosition(conHandle, origin) == 0: raiseOSError(osLastError())
else:
discard
@@ -155,18 +155,18 @@ proc eraseLine* =
var scrbuf: TCONSOLESCREENBUFFERINFO
var numwrote: DWORD
var hStdout = conHandle
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: osError(osLastError())
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: raiseOSError(osLastError())
var origin = scrbuf.dwCursorPosition
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 wt = scrbuf.dwSize.X - origin.X
if FillConsoleOutputCharacter(hStdout,' ', ht*wt,
origin, addr(numwrote)) == 0:
osError(osLastError())
raiseOSError(osLastError())
if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, ht * wt,
scrbuf.dwCursorPosition, addr(numwrote)) == 0:
osError(osLastError())
raiseOSError(osLastError())
else:
stdout.write("\e[2K")
setCursorXPos(0)
@@ -178,14 +178,14 @@ proc eraseScreen* =
var numwrote: DWORD
var origin: TCOORD # is inititalized to 0, 0
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,
origin, addr(numwrote)) == 0:
osError(osLastError())
raiseOSError(osLastError())
if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes,
scrbuf.dwSize.X * scrbuf.dwSize.Y,
origin, addr(numwrote)) == 0:
osError(osLastError())
raiseOSError(osLastError())
setCursorXPos(0)
else:
stdout.write("\e[2J")
@@ -199,7 +199,7 @@ proc resetAttributes* {.noconv.} =
stdout.write("\e[0m")
type
TStyle* = enum ## different styles for text output
Style* = enum ## different styles for text output
styleBright = 1, ## bright text
styleDim, ## dim text
styleUnknown, ## unknown
@@ -208,6 +208,8 @@ type
styleReverse = 7, ## unknown
styleHidden ## hidden text
{.deprecated: [TStyle: Style].}
when not defined(windows):
var
# XXX: These better be thread-local
@@ -244,7 +246,7 @@ proc writeStyled*(txt: string, style: set[TStyle] = {styleBright}) =
stdout.write("\e[" & $ord(gBG) & 'm')
type
TForegroundColor* = enum ## terminal's foreground colors
ForegroundColor* = enum ## terminal's foreground colors
fgBlack = 30, ## black
fgRed, ## red
fgGreen, ## green
@@ -254,7 +256,7 @@ type
fgCyan, ## cyan
fgWhite ## white
TBackgroundColor* = enum ## terminal's background colors
BackgroundColor* = enum ## terminal's background colors
bgBlack = 40, ## black
bgRed, ## red
bgGreen, ## green
@@ -264,13 +266,16 @@ type
bgCyan, ## cyan
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
when defined(windows):
var old = getAttributes() and not 0x0007
if bright:
old = old or FOREGROUND_INTENSITY
const lookup: array [TForegroundColor, int] = [
const lookup: array [ForegroundColor, int] = [
0,
(FOREGROUND_RED),
(FOREGROUND_GREEN),
@@ -285,13 +290,13 @@ proc setForegroundColor*(fg: TForegroundColor, bright=false) =
if bright: inc(gFG, 60)
stdout.write("\e[" & $gFG & 'm')
proc setBackgroundColor*(bg: TBackgroundColor, bright=false) =
proc setBackgroundColor*(bg: BackgroundColor, bright=false) =
## sets the terminal's background color
when defined(windows):
var old = getAttributes() and not 0x0070
if bright:
old = old or BACKGROUND_INTENSITY
const lookup: array [TBackgroundColor, int] = [
const lookup: array [BackgroundColor, int] = [
0,
(BACKGROUND_RED),
(BACKGROUND_GREEN),
@@ -306,22 +311,22 @@ proc setBackgroundColor*(bg: TBackgroundColor, bright=false) =
if bright: inc(gBG, 60)
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.
when defined(posix):
proc isatty(fildes: TFileHandle): cint {.
proc isatty(fildes: FileHandle): cint {.
importc: "isatty", header: "<unistd.h>".}
else:
proc isatty(fildes: TFileHandle): cint {.
proc isatty(fildes: FileHandle): cint {.
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(style: TStyle) = setStyle({style})
proc styledEchoProcessArg(style: set[TStyle]) = setStyle style
proc styledEchoProcessArg(color: TForegroundColor) = setForegroundColor color
proc styledEchoProcessArg(color: TBackgroundColor) = setBackgroundColor color
proc styledEchoProcessArg(s: string) = write stdout, s
proc styledEchoProcessArg(style: Style) = setStyle({style})
proc styledEchoProcessArg(style: set[Style]) = setStyle style
proc styledEchoProcessArg(color: ForegroundColor) = setForegroundColor color
proc styledEchoProcessArg(color: BackgroundColor) = setBackgroundColor color
macro styledEcho*(m: varargs[expr]): stmt =
## to be documented.

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
@@ -21,9 +21,9 @@ import
include "system/inclrtl"
type
TMonth* = enum ## represents a month
Month* = enum ## represents a month
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
var
@@ -32,12 +32,12 @@ var
when defined(posix) and not defined(JS):
type
TTimeImpl {.importc: "time_t", header: "<sys/time.h>".} = int
TTime* = distinct TTimeImpl ## distinct type that represents a time
## measured as number of seconds since the epoch
TimeImpl {.importc: "time_t", header: "<sys/time.h>".} = int
Time* = distinct TimeImpl ## distinct type that represents a time
## measured as number of seconds since the epoch
Ttimeval {.importc: "struct timeval", header: "<sys/select.h>",
final, pure.} = object ## struct timeval
Timeval {.importc: "struct timeval",
header: "<sys/select.h>".} = object ## struct timeval
tv_sec: int ## Seconds.
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.
# 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>".}
elif defined(windows):
@@ -53,16 +53,16 @@ elif defined(windows):
when defined(vcc):
# 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:
type TTimeImpl {.importc: "time_t", header: "<time.h>".} = int32
type TimeImpl {.importc: "time_t", header: "<time.h>".} = int32
type
TTime* = distinct TTimeImpl
Time* = distinct TimeImpl
elif defined(JS):
type
TTime* {.final, importc.} = object
Time* {.importc.} = object
getDay: proc (): int {.tags: [], raises: [], gcsafe.}
getFullYear: proc (): int {.tags: [], raises: [], gcsafe.}
getHours: proc (): int {.tags: [], raises: [], gcsafe.}
@@ -82,7 +82,7 @@ elif defined(JS):
getUTCSeconds: proc (): int {.tags: [], raises: [], gcsafe.}
getUTCDay: 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.}
setFullYear: 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.}
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,
## normally in the range 0 to 59, but can
## be up to 61 to allow for leap seconds.
@@ -112,9 +112,9 @@ type
hour*: range[0..23] ## The number of hours past midnight,
## in the range 0 to 23.
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.
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,
## in the range 0 to 365.
## Always 0 if the target is JS.
@@ -127,7 +127,7 @@ type
## I make some assumptions about the data in here. Either
## everything should be positive or everything negative. Zero is
## 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
seconds*: int ## The number of seconds
minutes*: int ## The number of minutes
@@ -136,55 +136,58 @@ type
months*: int ## The number of months
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
## elapsed since 1970) with integer precission. Use epochTime for higher
## 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,
## 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,
## 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
## calendar time representation. The function ignores the specified
## contents of the structure members `weekday` and `yearday` and recomputes
## 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
## 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
## returns a time object.
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.
proc `$` *(timeInfo: TTimeInfo): string {.tags: [], raises: [], gcsafe.}
## converts a `TTimeInfo` object to a string representation.
proc `$` *(time: TTime): string {.tags: [], raises: [], gcsafe.}
proc `$` *(timeInfo: TimeInfo): string {.tags: [], raises: [], gcsafe.}
## converts a `TimeInfo` object to a string representation.
proc `$` *(time: Time): string {.tags: [], raises: [], gcsafe.}
## converts a calendar time to a string representation.
proc `-`*(a, b: TTime): int64 {.
proc `-`*(a, b: Time): int64 {.
rtl, extern: "ntDiffTime", tags: [], raises: [].}
## 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: [].} =
## returns true iff ``a < b``, that is iff a happened before b.
result = a - b < 0
proc `<=` * (a, b: TTime): bool {.
proc `<=` * (a, b: Time): bool {.
rtl, extern: "ntLeTime", tags: [], raises: [].}=
## returns true iff ``a <= b``.
result = a - b <= 0
proc `==`*(a, b: TTime): bool {.
proc `==`*(a, b: Time): bool {.
rtl, extern: "ntEqTime", tags: [], raises: [].} =
## returns true if ``a == b``, that is if both times represent the same value
result = a - b == 0
@@ -203,8 +206,8 @@ proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], gcsafe.}
## version 0.8.10.** Use ``epochTime`` or ``cpuTime`` instead.
proc initInterval*(miliseconds, seconds, minutes, hours, days, months,
years: int = 0): TTimeInterval =
## creates a new ``TTimeInterval``.
years: int = 0): TimeInterval =
## creates a new ``TimeInterval``.
result.miliseconds = miliseconds
result.seconds = seconds
result.minutes = minutes
@@ -234,7 +237,7 @@ proc getDaysInMonth*(month: TMonth, year: int): int =
of mApr, mJun, mSep, mNov: result = 30
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
## all the fields
@@ -257,7 +260,7 @@ proc toSeconds(a: TTimeInfo, interval: TTimeInterval): float =
result += float(newinterv.seconds)
result += newinterv.miliseconds / 1000
proc `+`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo =
proc `+`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
## adds ``interval`` time.
##
## **Note:** This has been only briefly tested and it may not be
@@ -269,7 +272,7 @@ proc `+`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo =
else:
result = getLocalTime(fromSeconds(t + secs))
proc `-`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo =
proc `-`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
## subtracts ``interval`` time.
##
## **Note:** This has been only briefly tested, it is inaccurate especially
@@ -314,38 +317,36 @@ when not defined(JS):
yearday {.importc: "tm_yday".},
isdst {.importc: "tm_isdst".}: cint
PTimeInfo = ptr StructTM
PTime = ptr TTime
TimeInfoPtr = ptr StructTM
Clock {.importc: "clock_t".} = distinct int
TClock {.importc: "clock_t".} = distinct int
proc localtime(timer: PTime): PTimeInfo {.
proc localtime(timer: ptr Time): TimeInfoPtr {.
importc: "localtime", header: "<time.h>", tags: [].}
proc gmtime(timer: PTime): PTimeInfo {.
proc gmtime(timer: ptr Time): TimeInfoPtr {.
importc: "gmtime", header: "<time.h>", tags: [].}
proc timec(timer: PTime): TTime {.
proc timec(timer: ptr Time): Time {.
importc: "time", header: "<time.h>", tags: [].}
proc mktime(t: StructTM): TTime {.
proc mktime(t: StructTM): Time {.
importc: "mktime", header: "<time.h>", tags: [].}
proc asctime(tblock: StructTM): cstring {.
importc: "asctime", header: "<time.h>", tags: [].}
proc ctime(time: PTime): cstring {.
proc ctime(time: ptr Time): cstring {.
importc: "ctime", header: "<time.h>", tags: [].}
# strftime(s: CString, maxsize: int, fmt: CString, t: tm): int {.
# importc: "strftime", header: "<time.h>".}
proc clock(): TClock {.importc: "clock", header: "<time.h>", tags: [TimeEffect].}
proc difftime(a, b: TTime): float {.importc: "difftime", header: "<time.h>",
proc getClock(): Clock {.importc: "clock", header: "<time.h>", tags: [TimeEffect].}
proc difftime(a, b: Time): float {.importc: "difftime", header: "<time.h>",
tags: [].}
var
clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl.}: int
# our own procs on top of that:
proc tmToTimeInfo(tm: StructTM, local: bool): TTimeInfo =
proc tmToTimeInfo(tm: StructTM, local: bool): TimeInfo =
const
weekDays: array [0..6, TWeekDay] = [
weekDays: array [0..6, WeekDay] = [
dSun, dMon, dTue, dWed, dThu, dFri, dSat]
TTimeInfo(second: int(tm.second),
TimeInfo(second: int(tm.second),
minute: int(tm.minute),
hour: int(tm.hour),
monthday: int(tm.monthday),
@@ -364,9 +365,9 @@ when not defined(JS):
timezone: if local: getTimezone() else: 0
)
proc timeInfoToTM(t: TTimeInfo): StructTM =
proc timeInfoToTM(t: TimeInfo): StructTM =
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.minute = t.minute
result.hour = t.hour
@@ -378,36 +379,36 @@ when not defined(JS):
result.isdst = if t.isDST: 1 else: 0
when not defined(useNimRtl):
proc `-` (a, b: TTime): int64 =
proc `-` (a, b: Time): int64 =
return toBiggestInt(difftime(a, b))
proc getStartMilsecs(): int =
#echo "clocks per sec: ", clocksPerSec, "clock: ", int(clock())
#return clock() div (clocksPerSec div 1000)
#echo "clocks per sec: ", clocksPerSec, "clock: ", int(getClock())
#return getClock() div (clocksPerSec div 1000)
when defined(macosx):
result = toInt(toFloat(int(clock())) / (toFloat(clocksPerSec) / 1000.0))
result = toInt(toFloat(int(getClock())) / (toFloat(clocksPerSec) / 1000.0))
else:
result = int(clock()) div (clocksPerSec div 1000)
result = int(getClock()) div (clocksPerSec div 1000)
when false:
var a: Ttimeval
var a: Timeval
posix_gettimeofday(a)
result = a.tv_sec * 1000'i64 + a.tv_usec div 1000'i64
#echo "result: ", result
proc getTime(): TTime = return timec(nil)
proc getLocalTime(t: TTime): TTimeInfo =
proc getTime(): Time = return timec(nil)
proc getLocalTime(t: Time): TimeInfo =
var a = t
result = tmToTimeInfo(localtime(addr(a))[], true)
# copying is needed anyway to provide reentrancity; thus
# the conversion is not expensive
proc getGMTime(t: TTime): TTimeInfo =
proc getGMTime(t: Time): TimeInfo =
var a = t
result = tmToTimeInfo(gmtime(addr(a))[], false)
# copying is needed anyway to provide reentrancity; thus
# 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,
# because the header of mktime is broken in my version of libc
return mktime(timeInfoToTM(cTimeInfo))
@@ -419,12 +420,12 @@ when not defined(JS):
add(result, p[i])
inc(i)
proc `$`(timeInfo: TTimeInfo): string =
proc `$`(timeInfo: TimeInfo): string =
# BUGFIX: asctime returns a newline at the end!
var p = asctime(timeInfoToTM(timeInfo))
result = toStringTillNL(p)
proc `$`(time: TTime): string =
proc `$`(time: Time): string =
# BUGFIX: ctime returns a newline at the end!
var a = time
return toStringTillNL(ctime(addr(a)))
@@ -433,13 +434,13 @@ when not defined(JS):
epochDiff = 116444736000000000'i64
rateDiff = 10000000'i64 # 100 nsecs
proc unixTimeToWinTime*(t: TTime): int64 =
## converts a UNIX `TTime` (``time_t``) to a Windows file time
proc unixTimeToWinTime*(t: Time): int64 =
## converts a UNIX `Time` (``time_t``) to a Windows file time
result = int64(t) * rateDiff + epochDiff
proc winTimeToUnixTime*(t: int64): TTime =
## converts a Windows time to a UNIX `TTime` (``time_t``)
result = TTime((t - epochDiff) div rateDiff)
proc winTimeToUnixTime*(t: int64): Time =
## converts a Windows time to a UNIX `Time` (``time_t``)
result = Time((t - epochDiff) div rateDiff)
proc getTzname(): tuple[nonDST, DST: string] =
return ($tzname[0], $tzname[1])
@@ -447,9 +448,9 @@ when not defined(JS):
proc getTimezone(): int =
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):
proc epochTime(): float =
@@ -468,15 +469,15 @@ when not defined(JS):
{.error: "unknown OS".}
proc cpuTime(): float =
result = toFloat(int(clock())) / toFloat(clocksPerSec)
result = toFloat(int(getClock())) / toFloat(clocksPerSec)
elif defined(JS):
proc newDate(): TTime {.importc: "new Date".}
proc internGetTime(): TTime {.importc: "new Date", tags: [].}
proc newDate(): Time {.importc: "new Date".}
proc internGetTime(): Time {.importc: "new Date", tags: [].}
proc newDate(value: float): TTime {.importc: "new Date".}
proc newDate(value: string): TTime {.importc: "new Date".}
proc getTime(): TTime =
proc newDate(value: float): Time {.importc: "new Date".}
proc newDate(value: string): Time {.importc: "new Date".}
proc getTime(): Time =
# Warning: This is something different in JS.
return newDate()
@@ -484,7 +485,7 @@ elif defined(JS):
weekDays: array [0..6, TWeekDay] = [
dSun, dMon, dTue, dWed, dThu, dFri, dSat]
proc getLocalTime(t: TTime): TTimeInfo =
proc getLocalTime(t: Time): TimeInfo =
result.second = t.getSeconds()
result.minute = t.getMinutes()
result.hour = t.getHours()
@@ -494,7 +495,7 @@ elif defined(JS):
result.weekday = weekDays[t.getDay()]
result.yearday = 0
proc getGMTime(t: TTime): TTimeInfo =
proc getGMTime(t: Time): TimeInfo =
result.second = t.getUTCSeconds()
result.minute = t.getUTCMinutes()
result.hour = t.getUTCHours()
@@ -504,7 +505,7 @@ elif defined(JS):
result.weekday = weekDays[t.getUTCDay()]
result.yearday = 0
proc timeInfoToTime*(timeInfo: TTimeInfo): TTime =
proc timeInfoToTime*(timeInfo: TimeInfo): Time =
result = internGetTime()
result.setSeconds(timeInfo.second)
result.setMinutes(timeInfo.minute)
@@ -513,10 +514,10 @@ elif defined(JS):
result.setFullYear(timeInfo.year)
result.setDate(timeInfo.monthday)
proc `$`(timeInfo: TTimeInfo): string = return $(TimeInfoToTIme(timeInfo))
proc `$`(time: TTime): string = return $time.toLocaleString()
proc `$`(timeInfo: TimeInfo): string = return $(TimeInfoToTIme(timeInfo))
proc `$`(time: Time): string = return $time.toLocaleString()
proc `-` (a, b: TTime): int64 =
proc `-` (a, b: Time): int64 =
return a.getTime() - b.getTime()
var
@@ -526,11 +527,11 @@ elif defined(JS):
## get the miliseconds from the start of the program
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()
@@ -546,20 +547,20 @@ proc getClockStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} =
result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) &
':' & intToStr(ti.second, 2)
proc `$`*(day: TWeekDay): string =
## stingify operator for ``TWeekDay``.
const lookup: array[TWeekDay, string] = ["Monday", "Tuesday", "Wednesday",
proc `$`*(day: WeekDay): string =
## stingify operator for ``WeekDay``.
const lookup: array[WeekDay, string] = ["Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday", "Sunday"]
return lookup[day]
proc `$`*(m: TMonth): string =
proc `$`*(m: Month): string =
## 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",
"November", "December"]
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.
##
## 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)
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
## specifiers are available:
##
@@ -718,7 +719,7 @@ proc format*(info: TTimeInfo, f: string): string =
while true:
case f[i]
of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
format_token(info, currentF, result)
formatToken(info, currentF, result)
currentF = ""
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]:
currentF.add(f[i])
else:
format_token(info, currentF, result)
formatToken(info, currentF, result)
dec(i) # Move position back to re-process the character separately.
currentF = ""
@@ -764,7 +765,7 @@ when isMainModule:
" 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"
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
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") ==

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2012 Nimrod Contributors
#
# 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
#
# See the file "copying.txt", included in this
@@ -14,13 +14,15 @@
include "system/inclrtl"
type
IRune = int # underlying type of TRune
TRune* = distinct IRune ## type that can hold any Unicode character
TRune16* = distinct int16 ## 16 bit Unicode character
RuneImpl = int # underlying type of Rune
Rune* = distinct RuneImpl ## type that can hold any 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: TRune): 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: Rune): 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)
@@ -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.
bind ones
if ord(s[i]) <=% 127:
result = TRune(ord(s[i]))
result = Rune(ord(s[i]))
when doInc: inc(i)
elif ord(s[i]) shr 5 == 0b110:
# assert(ord(s[i+1]) shr 6 == 0b10)
result = TRune((ord(s[i]) and (ones(5))) shl 6 or
(ord(s[i+1]) and ones(6)))
result = Rune((ord(s[i]) and (ones(5))) shl 6 or
(ord(s[i+1]) and ones(6)))
when doInc: inc(i, 2)
elif ord(s[i]) shr 4 == 0b1110:
# assert(ord(s[i+1]) shr 6 == 0b10)
# assert(ord(s[i+2]) shr 6 == 0b10)
result = TRune((ord(s[i]) and ones(4)) shl 12 or
result = Rune((ord(s[i]) and ones(4)) shl 12 or
(ord(s[i+1]) and ones(6)) shl 6 or
(ord(s[i+2]) and ones(6)))
when doInc: inc(i, 3)
@@ -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+2]) 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+2]) and ones(6)) shl 6 or
(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+3]) 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+2]) and ones(6)) shl 12 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+4]) 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+2]) and ones(6)) shl 18 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)))
when doInc: inc(i, 6)
else:
result = TRune(ord(s[i]))
result = Rune(ord(s[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`
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
var i = IRune(c)
var i = RuneImpl(c)
if i <=% 127:
result = newString(1)
result[0] = chr(i)
@@ -132,11 +134,11 @@ proc toUTF8*(c: TRune): string {.rtl, extern: "nuc$1".} =
result = newString(1)
result[0] = chr(i)
proc `$`*(rune: TRune): string =
proc `$`*(rune: Rune): string =
## converts a rune to a string
rune.toUTF8
proc `$`*(runes: seq[TRune]): string =
proc `$`*(runes: seq[Rune]): string =
## converts a sequence of runes to a string
result = ""
for rune in runes: result.add(rune.toUTF8)
@@ -1100,7 +1102,7 @@ const
0x01f1, 501, #
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 t = 0
while n > 1:
@@ -1115,41 +1117,41 @@ proc binarySearch(c: IRune, tab: openArray[IRune], len, stride: int): int =
return t
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.
## If possible, prefer `toLower` over `toUpper`.
var c = IRune(c)
var c = RuneImpl(c)
var p = binarySearch(c, tolowerRanges, len(tolowerRanges) div 3, 3)
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)
if p >= 0 and c == tolowerSinglets[p]:
return TRune(c + tolowerSinglets[p+1] - 500)
return TRune(c)
return Rune(c + tolowerSinglets[p+1] - 500)
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.
## If possible, prefer `toLower` over `toUpper`.
var c = IRune(c)
var c = RuneImpl(c)
var p = binarySearch(c, toupperRanges, len(toupperRanges) div 3, 3)
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)
if p >= 0 and c == toupperSinglets[p]:
return TRune(c + toupperSinglets[p+1] - 500)
return TRune(c)
return Rune(c + toupperSinglets[p+1] - 500)
return Rune(c)
proc toTitle*(c: TRune): TRune {.rtl, extern: "nuc$1", procvar.} =
var c = IRune(c)
proc toTitle*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
var c = RuneImpl(c)
var p = binarySearch(c, toTitleSinglets, len(toTitleSinglets) div 2, 2)
if p >= 0 and c == toTitleSinglets[p]:
return TRune(c + toTitleSinglets[p+1] - 500)
return TRune(c)
return Rune(c + toTitleSinglets[p+1] - 500)
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
## If possible, prefer `isLower` over `isUpper`.
var c = IRune(c)
var c = RuneImpl(c)
# Note: toUpperRanges is correct here!
var p = binarySearch(c, toupperRanges, len(toupperRanges) div 3, 3)
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]:
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
## If possible, prefer `isLower` over `isUpper`.
var c = IRune(c)
var c = RuneImpl(c)
# Note: toLowerRanges is correct here!
var p = binarySearch(c, tolowerRanges, len(tolowerRanges) div 3, 3)
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]:
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)
if isUpper(c) or isLower(c):
return true
var c = IRune(c)
var c = RuneImpl(c)
var p = binarySearch(c, alphaRanges, len(alphaRanges) div 2, 2)
if p >= 0 and c >= alphaRanges[p] and c <= alphaRanges[p+1]:
return true
@@ -1182,21 +1184,21 @@ proc isAlpha*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
if p >= 0 and c == alphaSinglets[p]:
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)
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
var c = IRune(c)
var c = RuneImpl(c)
var p = binarySearch(c, spaceRanges, len(spaceRanges) div 2, 2)
if p >= 0 and c >= spaceRanges[p] and c <= spaceRanges[p+1]:
return true
iterator runes*(s: string): TRune =
iterator runes*(s: string): Rune =
## iterates over any unicode character of the string `s`.
var
i = 0
result: TRune
result: Rune
while i < len(s):
fastRuneAt(s, i, result, true)
yield result
@@ -1209,12 +1211,12 @@ proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1", procvar.} =
## | > 0 iff a > b
var i = 0
var j = 0
var ar, br: TRune
var ar, br: Rune
while i < a.len and j < b.len:
# slow path:
fastRuneAt(a, i, ar)
fastRuneAt(b, j, br)
result = IRune(toLower(ar)) - IRune(toLower(br))
result = RuneImpl(toLower(ar)) - RuneImpl(toLower(br))
if result != 0: return
result = a.len - b.len

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
#
#
# Nimrod's Runtime Library
# Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
@@ -12,33 +12,36 @@
import macros, strtabs
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
xnElement, ## an element with 0 or more children
xnCData, ## a CDATA node
xnEntity, ## an entity (like ``&thing;``)
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
case k: TXmlNodeKind # private, use the kind() proc to read this field.
XmlNodeObj {.acyclic.} = object
case k: XmlNodeKind # private, use the kind() proc to read this field.
of xnText, xnComment, xnCData, xnEntity:
fText: string
of xnElement:
fTag: string
s: seq[PXmlNode]
fAttr: PXmlAttributes
s: seq[XmlNode]
fAttr: XmlAttributes
fClientData: int ## for other clients
proc newXmlNode(kind: TXmlNodeKind): PXmlNode =
## creates a new ``PXmlNode``.
{.deprecated: [PXmlNode: XmlNode, TXmlNodeKind: XmlNodeKind, PXmlAttributes:
XmlAttributes, TXmlNode: XmlNodeObj].}
proc newXmlNode(kind: XmlNodeKind): XmlNode =
## creates a new ``XmlNode``.
new(result)
result.k = kind
proc newElement*(tag: string): PXmlNode =
proc newElement*(tag: string): XmlNode =
## creates a new ``PXmlNode`` of kind ``xnText`` with the given `tag`.
result = newXmlNode(xnElement)
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`
## sequence, which can't be nil or the proc will crash. Usage example:
##
## .. code-block:: nimrod
## .. code-block::
## var
## html: PXmlNode
## tags: seq[PXmlNode] = @[]
## html: XmlNode
## tags: seq[XmlNode] = @[]
##
## html = buildHtml()
## 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] =
## Shortcut version to assign in let blocks. Example:
##
## .. code-block:: nimrod
## var html: PXmlNode
## .. code-block::
## var html: XmlNode
##
## html = buildHtml(html)
## 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
## platform specific programming.
{.deprecated: [fileHandle: getFileHandle].}
proc cstringArrayToSeq*(a: cstringArray, len: int): seq[string] =
## converts a ``cstringArray`` to a ``seq[string]``. `a` is supposed to be
## of length ``len``.

View File

@@ -49,7 +49,6 @@ Bugs
- bug: type conversions concerning proc types are weird
- compilation of niminst takes way too long. looks like a regression
- 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 ...