big rename
This commit is contained in:
parent
15a7bcc89f
commit
11b6958755
98 changed files with 2491 additions and 2341 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# The Nimrod Compiler
|
# The Nim Compiler
|
||||||
# (c) Copyright 2013 Andreas Rumpf
|
# (c) Copyright 2013 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# The Nimrod Compiler
|
# The Nim Compiler
|
||||||
# (c) Copyright 2014 Andreas Rumpf
|
# (c) Copyright 2014 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# Configuration file for the Nimrod Compiler.
|
# Configuration file for the Nim Compiler.
|
||||||
# (c) 2013 Andreas Rumpf
|
# (c) 2014 Andreas Rumpf
|
||||||
|
|
||||||
# Feel free to edit the default values as you need.
|
# Feel free to edit the default values as you need.
|
||||||
|
|
||||||
|
|
@ -13,6 +13,8 @@ cc = gcc
|
||||||
arm.linux.gcc.exe = "arm-linux-gcc"
|
arm.linux.gcc.exe = "arm-linux-gcc"
|
||||||
arm.linux.gcc.linkerexe = "arm-linux-gcc"
|
arm.linux.gcc.linkerexe = "arm-linux-gcc"
|
||||||
|
|
||||||
|
cs:partial
|
||||||
|
|
||||||
path="$lib/core"
|
path="$lib/core"
|
||||||
path="$lib/pure"
|
path="$lib/pure"
|
||||||
path="$lib/pure/collections"
|
path="$lib/pure/collections"
|
||||||
|
|
|
||||||
465
doc/manual.txt
465
doc/manual.txt
File diff suppressed because it is too large
Load diff
|
|
@ -180,7 +180,7 @@ So it is not necessary to write ``peg" 'abc' "`` in the above example.
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
|
|
||||||
Check if `s` matches Nimrod's "while" keyword:
|
Check if `s` matches Nim's "while" keyword:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nimrod
|
||||||
s =~ peg" y'while'"
|
s =~ peg" y'while'"
|
||||||
|
|
@ -212,7 +212,7 @@ example ``*`` should not be greedy, so ``\[.*?\]`` should be used instead.
|
||||||
|
|
||||||
PEG construction
|
PEG construction
|
||||||
----------------
|
----------------
|
||||||
There are two ways to construct a PEG in Nimrod code:
|
There are two ways to construct a PEG in Nim code:
|
||||||
(1) Parsing a string into an AST which consists of `TPeg` nodes with the
|
(1) Parsing a string into an AST which consists of `TPeg` nodes with the
|
||||||
`peg` proc.
|
`peg` proc.
|
||||||
(2) Constructing the AST directly with proc calls. This method does not
|
(2) Constructing the AST directly with proc calls. This method does not
|
||||||
|
|
|
||||||
258
doc/tut1.txt
258
doc/tut1.txt
|
|
@ -1,9 +1,9 @@
|
||||||
========================
|
=====================
|
||||||
Nimrod Tutorial (Part I)
|
Nim Tutorial (Part I)
|
||||||
========================
|
=====================
|
||||||
|
|
||||||
:Author: Andreas Rumpf
|
:Author: Andreas Rumpf
|
||||||
:Version: |nimrodversion|
|
:Version: |nimversion|
|
||||||
|
|
||||||
.. contents::
|
.. contents::
|
||||||
|
|
||||||
|
|
@ -16,7 +16,7 @@ Introduction
|
||||||
</p></blockquote>
|
</p></blockquote>
|
||||||
|
|
||||||
|
|
||||||
This document is a tutorial for the programming language *Nimrod*.
|
This document is a tutorial for the programming language *Nim*.
|
||||||
This tutorial assumes that you are familiar with basic programming concepts
|
This tutorial assumes that you are familiar with basic programming concepts
|
||||||
like variables, types or statements but is kept very basic. The `manual
|
like variables, types or statements but is kept very basic. The `manual
|
||||||
<manual.html>`_ contains many more examples of the advanced language features.
|
<manual.html>`_ contains many more examples of the advanced language features.
|
||||||
|
|
@ -28,7 +28,7 @@ The first program
|
||||||
|
|
||||||
We start the tour with a modified "hello world" program:
|
We start the tour with a modified "hello world" program:
|
||||||
|
|
||||||
.. code-block:: Nimrod
|
.. code-block:: Nim
|
||||||
# This is a comment
|
# This is a comment
|
||||||
echo("What's your name? ")
|
echo("What's your name? ")
|
||||||
var name: string = readLine(stdin)
|
var name: string = readLine(stdin)
|
||||||
|
|
@ -37,30 +37,30 @@ We start the tour with a modified "hello world" program:
|
||||||
|
|
||||||
Save this code to the file "greetings.nim". Now compile and run it::
|
Save this code to the file "greetings.nim". Now compile and run it::
|
||||||
|
|
||||||
nimrod compile --run greetings.nim
|
nim compile --run greetings.nim
|
||||||
|
|
||||||
With the ``--run`` `switch <nimrodc.html#command-line-switches>`_ Nimrod
|
With the ``--run`` `switch <nimc.html#command-line-switches>`_ Nim
|
||||||
executes the file automatically after compilation. You can give your program
|
executes the file automatically after compilation. You can give your program
|
||||||
command line arguments by appending them after the filename::
|
command line arguments by appending them after the filename::
|
||||||
|
|
||||||
nimrod compile --run greetings.nim arg1 arg2
|
nim compile --run greetings.nim arg1 arg2
|
||||||
|
|
||||||
Commonly used commands and switches have abbreviations, so you can also use::
|
Commonly used commands and switches have abbreviations, so you can also use::
|
||||||
|
|
||||||
nimrod c -r greetings.nim
|
nim c -r greetings.nim
|
||||||
|
|
||||||
To compile a release version use::
|
To compile a release version use::
|
||||||
|
|
||||||
nimrod c -d:release greetings.nim
|
nim c -d:release greetings.nim
|
||||||
|
|
||||||
By default the Nimrod compiler generates a large amount of runtime checks
|
By default the Nim compiler generates a large amount of runtime checks
|
||||||
aiming for your debugging pleasure. With ``-d:release`` these checks are
|
aiming for your debugging pleasure. With ``-d:release`` these checks are
|
||||||
`turned off and optimizations are turned on
|
`turned off and optimizations are turned on
|
||||||
<nimrodc.html#compile-time-symbols>`_.
|
<nimc.html#compile-time-symbols>`_.
|
||||||
|
|
||||||
Though it should be pretty obvious what the program does, I will explain the
|
Though it should be pretty obvious what the program does, I will explain the
|
||||||
syntax: statements which are not indented are executed when the program
|
syntax: statements which are not indented are executed when the program
|
||||||
starts. Indentation is Nimrod's way of grouping statements. Indentation is
|
starts. Indentation is Nim's way of grouping statements. Indentation is
|
||||||
done with spaces only, tabulators are not allowed.
|
done with spaces only, tabulators are not allowed.
|
||||||
|
|
||||||
String literals are enclosed in double quotes. The ``var`` statement declares
|
String literals are enclosed in double quotes. The ``var`` statement declares
|
||||||
|
|
@ -70,11 +70,11 @@ compiler knows that `readLine <system.html#readLine,TFile>`_ returns a string,
|
||||||
you can leave out the type in the declaration (this is called `local type
|
you can leave out the type in the declaration (this is called `local type
|
||||||
inference`:idx:). So this will work too:
|
inference`:idx:). So this will work too:
|
||||||
|
|
||||||
.. code-block:: Nimrod
|
.. code-block:: Nim
|
||||||
var name = readLine(stdin)
|
var name = readLine(stdin)
|
||||||
|
|
||||||
Note that this is basically the only form of type inference that exists in
|
Note that this is basically the only form of type inference that exists in
|
||||||
Nimrod: it is a good compromise between brevity and readability.
|
Nim: it is a good compromise between brevity and readability.
|
||||||
|
|
||||||
The "hello world" program contains several identifiers that are already known
|
The "hello world" program contains several identifiers that are already known
|
||||||
to the compiler: ``echo``, `readLine <system.html#readLine,TFile>`_, etc.
|
to the compiler: ``echo``, `readLine <system.html#readLine,TFile>`_, etc.
|
||||||
|
|
@ -85,8 +85,8 @@ imported by any other module.
|
||||||
Lexical elements
|
Lexical elements
|
||||||
================
|
================
|
||||||
|
|
||||||
Let us look at Nimrod's lexical elements in more detail: like other
|
Let us look at Nim's lexical elements in more detail: like other
|
||||||
programming languages Nimrod consists of (string) literals, identifiers,
|
programming languages Nim consists of (string) literals, identifiers,
|
||||||
keywords, comments, operators, and other punctuation marks.
|
keywords, comments, operators, and other punctuation marks.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -97,7 +97,7 @@ String literals are enclosed in double quotes; character literals in single
|
||||||
quotes. Special characters are escaped with ``\``: ``\n`` means newline, ``\t``
|
quotes. Special characters are escaped with ``\``: ``\n`` means newline, ``\t``
|
||||||
means tabulator, etc. There are also *raw* string literals:
|
means tabulator, etc. There are also *raw* string literals:
|
||||||
|
|
||||||
.. code-block:: Nimrod
|
.. code-block:: Nim
|
||||||
r"C:\program files\nim"
|
r"C:\program files\nim"
|
||||||
|
|
||||||
In raw literals the backslash is not an escape character.
|
In raw literals the backslash is not an escape character.
|
||||||
|
|
@ -115,7 +115,7 @@ Comments start anywhere outside a string or character literal with the
|
||||||
hash character ``#``. Documentation comments start with ``##``. Multiline
|
hash character ``#``. Documentation comments start with ``##``. Multiline
|
||||||
comments need to be aligned at the same column:
|
comments need to be aligned at the same column:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
|
|
||||||
i = 0 # This is a single comment over multiple lines belonging to the
|
i = 0 # This is a single comment over multiple lines belonging to the
|
||||||
# assignment statement.
|
# assignment statement.
|
||||||
|
|
@ -128,7 +128,7 @@ comments need to be aligned at the same column:
|
||||||
The alignment requirement does not hold if the preceding comment piece ends in
|
The alignment requirement does not hold if the preceding comment piece ends in
|
||||||
a backslash:
|
a backslash:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
TMyObject {.final, pure, acyclic.} = object # comment continues: \
|
TMyObject {.final, pure, acyclic.} = object # comment continues: \
|
||||||
# we have lots of space here to comment 'TMyObject'.
|
# we have lots of space here to comment 'TMyObject'.
|
||||||
|
|
@ -151,15 +151,15 @@ the syntax, watch their indentation:
|
||||||
**Note**: To comment out a large piece of code, it is often better to use a
|
**Note**: To comment out a large piece of code, it is often better to use a
|
||||||
``when false:`` statement.
|
``when false:`` statement.
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
when false:
|
when false:
|
||||||
brokenCode()
|
brokenCode()
|
||||||
|
|
||||||
Another option is to use the `discard statement`_ together with *long string
|
Another option is to use the `discard statement`_ together with *long string
|
||||||
literals* to create block comments:
|
literals* to create block comments:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
discard """ You can have any Nimrod code text commented
|
discard """ You can have any Nim code text commented
|
||||||
out inside this with no indentation restrictions.
|
out inside this with no indentation restrictions.
|
||||||
yes("May I ask a pointless question?") """
|
yes("May I ask a pointless question?") """
|
||||||
|
|
||||||
|
|
@ -204,7 +204,7 @@ to a storage location:
|
||||||
|
|
||||||
``=`` is the *assignment operator*. The assignment operator cannot be
|
``=`` is the *assignment operator*. The assignment operator cannot be
|
||||||
overloaded, overwritten or forbidden, but this might change in a future version
|
overloaded, overwritten or forbidden, but this might change in a future version
|
||||||
of Nimrod. You can declare multiple variables with a single assignment
|
of Nim. You can declare multiple variables with a single assignment
|
||||||
statement and all the variables will have the same value:
|
statement and all the variables will have the same value:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block::
|
||||||
|
|
@ -229,7 +229,7 @@ Constants are symbols which are bound to a value. The constant's value
|
||||||
cannot change. The compiler must be able to evaluate the expression in a
|
cannot change. The compiler must be able to evaluate the expression in a
|
||||||
constant declaration at compile time:
|
constant declaration at compile time:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
const x = "abc" # the constant x contains the string "abc"
|
const x = "abc" # the constant x contains the string "abc"
|
||||||
|
|
||||||
Indentation can be used after the ``const`` keyword to list a whole section of
|
Indentation can be used after the ``const`` keyword to list a whole section of
|
||||||
|
|
@ -277,7 +277,7 @@ If statement
|
||||||
|
|
||||||
The if statement is one way to branch the control flow:
|
The if statement is one way to branch the control flow:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
let name = readLine(stdin)
|
let name = readLine(stdin)
|
||||||
if name == "":
|
if name == "":
|
||||||
echo("Poor soul, you lost your name?")
|
echo("Poor soul, you lost your name?")
|
||||||
|
|
@ -298,7 +298,7 @@ Case statement
|
||||||
Another way to branch is provided by the case statement. A case statement is
|
Another way to branch is provided by the case statement. A case statement is
|
||||||
a multi-branch:
|
a multi-branch:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
let name = readLine(stdin)
|
let name = readLine(stdin)
|
||||||
case name
|
case name
|
||||||
of "":
|
of "":
|
||||||
|
|
@ -317,7 +317,7 @@ The case statement can deal with integers, other ordinal types and strings.
|
||||||
(What an ordinal type is will be explained soon.)
|
(What an ordinal type is will be explained soon.)
|
||||||
For integers or other ordinal types value ranges are also possible:
|
For integers or other ordinal types value ranges are also possible:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
# this statement will be explained later:
|
# this statement will be explained later:
|
||||||
from strutils import parseInt
|
from strutils import parseInt
|
||||||
|
|
||||||
|
|
@ -333,7 +333,7 @@ every value that ``n`` may contain, but the code only handles the values
|
||||||
(though it is possible thanks to the range notation), we fix this by telling
|
(though it is possible thanks to the range notation), we fix this by telling
|
||||||
the compiler that for every other value nothing should be done:
|
the compiler that for every other value nothing should be done:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
...
|
...
|
||||||
case n
|
case n
|
||||||
of 0..2, 4..7: echo("The number is in the set: {0, 1, 2, 4, 5, 6, 7}")
|
of 0..2, 4..7: echo("The number is in the set: {0, 1, 2, 4, 5, 6, 7}")
|
||||||
|
|
@ -355,7 +355,7 @@ While statement
|
||||||
|
|
||||||
The while statement is a simple looping construct:
|
The while statement is a simple looping construct:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
|
|
||||||
echo("What's your name? ")
|
echo("What's your name? ")
|
||||||
var name = readLine(stdin)
|
var name = readLine(stdin)
|
||||||
|
|
@ -375,7 +375,7 @@ The ``for`` statement is a construct to loop over any element an *iterator*
|
||||||
provides. The example uses the built-in `countup <system.html#countup>`_
|
provides. The example uses the built-in `countup <system.html#countup>`_
|
||||||
iterator:
|
iterator:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
echo("Counting to ten: ")
|
echo("Counting to ten: ")
|
||||||
for i in countup(1, 10):
|
for i in countup(1, 10):
|
||||||
echo($i)
|
echo($i)
|
||||||
|
|
@ -387,7 +387,7 @@ other types into a string. The variable ``i`` is implicitly declared by the
|
||||||
<system.html#countup>`_ returns. ``i`` runs through the values 1, 2, .., 10.
|
<system.html#countup>`_ returns. ``i`` runs through the values 1, 2, .., 10.
|
||||||
Each value is ``echo``-ed. This code does the same:
|
Each value is ``echo``-ed. This code does the same:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
echo("Counting to 10: ")
|
echo("Counting to 10: ")
|
||||||
var i = 1
|
var i = 1
|
||||||
while i <= 10:
|
while i <= 10:
|
||||||
|
|
@ -397,16 +397,16 @@ Each value is ``echo``-ed. This code does the same:
|
||||||
|
|
||||||
Counting down can be achieved as easily (but is less often needed):
|
Counting down can be achieved as easily (but is less often needed):
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
echo("Counting down from 10 to 1: ")
|
echo("Counting down from 10 to 1: ")
|
||||||
for i in countdown(10, 1):
|
for i in countdown(10, 1):
|
||||||
echo($i)
|
echo($i)
|
||||||
# --> Outputs 10 9 8 7 6 5 4 3 2 1 on different lines
|
# --> Outputs 10 9 8 7 6 5 4 3 2 1 on different lines
|
||||||
|
|
||||||
Since counting up occurs so often in programs, Nimrod also has a `..
|
Since counting up occurs so often in programs, Nim also has a `..
|
||||||
<system.html#...i,S,T>`_ iterator that does the same:
|
<system.html#...i,S,T>`_ iterator that does the same:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
for i in 1..10:
|
for i in 1..10:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
@ -417,7 +417,7 @@ Control flow statements have a feature not covered yet: they open a
|
||||||
new scope. This means that in the following example, ``x`` is not accessible
|
new scope. This means that in the following example, ``x`` is not accessible
|
||||||
outside the loop:
|
outside the loop:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
while false:
|
while false:
|
||||||
var x = "hi"
|
var x = "hi"
|
||||||
echo(x) # does not work
|
echo(x) # does not work
|
||||||
|
|
@ -426,7 +426,7 @@ A while (for) statement introduces an implicit block. Identifiers
|
||||||
are only visible within the block they have been declared. The ``block``
|
are only visible within the block they have been declared. The ``block``
|
||||||
statement can be used to open a new block explicitly:
|
statement can be used to open a new block explicitly:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
block myblock:
|
block myblock:
|
||||||
var x = "hi"
|
var x = "hi"
|
||||||
echo(x) # does not work either
|
echo(x) # does not work either
|
||||||
|
|
@ -440,7 +440,7 @@ A block can be left prematurely with a ``break`` statement. The break statement
|
||||||
can leave a ``while``, ``for``, or a ``block`` statement. It leaves the
|
can leave a ``while``, ``for``, or a ``block`` statement. It leaves the
|
||||||
innermost construct, unless a label of a block is given:
|
innermost construct, unless a label of a block is given:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
block myblock:
|
block myblock:
|
||||||
echo("entering block")
|
echo("entering block")
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -461,7 +461,7 @@ Continue statement
|
||||||
Like in many other programming languages, a ``continue`` statement starts
|
Like in many other programming languages, a ``continue`` statement starts
|
||||||
the next iteration immediately:
|
the next iteration immediately:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
while true:
|
while true:
|
||||||
let x = readLine(stdin)
|
let x = readLine(stdin)
|
||||||
if x == "": continue
|
if x == "": continue
|
||||||
|
|
@ -473,7 +473,7 @@ When statement
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
|
|
||||||
when system.hostOS == "windows":
|
when system.hostOS == "windows":
|
||||||
echo("running on Windows!")
|
echo("running on Windows!")
|
||||||
|
|
@ -504,17 +504,17 @@ possible.
|
||||||
Statements and indentation
|
Statements and indentation
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
Now that we covered the basic control flow statements, let's return to Nimrod
|
Now that we covered the basic control flow statements, let's return to Nim
|
||||||
indentation rules.
|
indentation rules.
|
||||||
|
|
||||||
In Nimrod there is a distinction between *simple statements* and *complex
|
In Nim there is a distinction between *simple statements* and *complex
|
||||||
statements*. *Simple statements* cannot contain other statements:
|
statements*. *Simple statements* cannot contain other statements:
|
||||||
Assignment, procedure calls or the ``return`` statement belong to the simple
|
Assignment, procedure calls or the ``return`` statement belong to the simple
|
||||||
statements. *Complex statements* like ``if``, ``when``, ``for``, ``while`` can
|
statements. *Complex statements* like ``if``, ``when``, ``for``, ``while`` can
|
||||||
contain other statements. To avoid ambiguities, complex statements always have
|
contain other statements. To avoid ambiguities, complex statements always have
|
||||||
to be indented, but single simple statements do not:
|
to be indented, but single simple statements do not:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
# no indentation needed for single assignment statement:
|
# no indentation needed for single assignment statement:
|
||||||
if x: x = false
|
if x: x = false
|
||||||
|
|
||||||
|
|
@ -535,7 +535,7 @@ to be indented, but single simple statements do not:
|
||||||
condition in an if statement is an example for an expression. Expressions can
|
condition in an if statement is an example for an expression. Expressions can
|
||||||
contain indentation at certain places for better readability:
|
contain indentation at certain places for better readability:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
|
|
||||||
if thisIsaLongCondition() and
|
if thisIsaLongCondition() and
|
||||||
thisIsAnotherLongCondition(1,
|
thisIsAnotherLongCondition(1,
|
||||||
|
|
@ -548,7 +548,7 @@ an open parenthesis and after commas.
|
||||||
With parenthesis and semicolons ``(;)`` you can use statements where only
|
With parenthesis and semicolons ``(;)`` you can use statements where only
|
||||||
an expression is allowed:
|
an expression is allowed:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
# computes fac(4) at compile time:
|
# computes fac(4) at compile time:
|
||||||
const fac4 = (var x = 1; for i in 1..4: x *= i; x)
|
const fac4 = (var x = 1; for i in 1..4: x *= i; x)
|
||||||
|
|
||||||
|
|
@ -558,10 +558,10 @@ Procedures
|
||||||
|
|
||||||
To define new commands like `echo <system.html#echo>`_ and `readLine
|
To define new commands like `echo <system.html#echo>`_ and `readLine
|
||||||
<system.html#readLine,TFile>`_ in the examples, the concept of a `procedure`
|
<system.html#readLine,TFile>`_ in the examples, the concept of a `procedure`
|
||||||
is needed. (Some languages call them *methods* or *functions*.) In Nimrod new
|
is needed. (Some languages call them *methods* or *functions*.) In Nim new
|
||||||
procedures are defined with the ``proc`` keyword:
|
procedures are defined with the ``proc`` keyword:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
proc yes(question: string): bool =
|
proc yes(question: string): bool =
|
||||||
echo(question, " (y/n)")
|
echo(question, " (y/n)")
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -597,7 +597,7 @@ shorthand for ``return result``. The ``result`` value is always returned
|
||||||
automatically at the end a procedure if there is no ``return`` statement at
|
automatically at the end a procedure if there is no ``return`` statement at
|
||||||
the exit.
|
the exit.
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
proc sumTillNegative(x: varargs[int]): int =
|
proc sumTillNegative(x: varargs[int]): int =
|
||||||
for i in x:
|
for i in x:
|
||||||
if i < 0:
|
if i < 0:
|
||||||
|
|
@ -624,7 +624,7 @@ most efficient way. If a mutable variable is needed inside the procedure, it has
|
||||||
to be declared with ``var`` in the procedure body. Shadowing the parameter name
|
to be declared with ``var`` in the procedure body. Shadowing the parameter name
|
||||||
is possible, and actually an idiom:
|
is possible, and actually an idiom:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
proc printSeq(s: seq, nprinted: int = -1) =
|
proc printSeq(s: seq, nprinted: int = -1) =
|
||||||
var nprinted = if nprinted == -1: s.len else: min(nprinted, s.len)
|
var nprinted = if nprinted == -1: s.len else: min(nprinted, s.len)
|
||||||
for i in 0 .. <nprinted:
|
for i in 0 .. <nprinted:
|
||||||
|
|
@ -633,7 +633,7 @@ is possible, and actually an idiom:
|
||||||
If the procedure needs to modify the argument for the
|
If the procedure needs to modify the argument for the
|
||||||
caller, a ``var`` parameter can be used:
|
caller, a ``var`` parameter can be used:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
proc divmod(a, b: int; res, remainder: var int) =
|
proc divmod(a, b: int; res, remainder: var int) =
|
||||||
res = a div b # integer division
|
res = a div b # integer division
|
||||||
remainder = a mod b # integer modulo operation
|
remainder = a mod b # integer modulo operation
|
||||||
|
|
@ -653,17 +653,17 @@ a tuple as a return value instead of using var parameters.
|
||||||
Discard statement
|
Discard statement
|
||||||
-----------------
|
-----------------
|
||||||
To call a procedure that returns a value just for its side effects and ignoring
|
To call a procedure that returns a value just for its side effects and ignoring
|
||||||
its return value, a ``discard`` statement **has** to be used. Nimrod does not
|
its return value, a ``discard`` statement **has** to be used. Nim does not
|
||||||
allow to silently throw away a return value:
|
allow to silently throw away a return value:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
discard yes("May I ask a pointless question?")
|
discard yes("May I ask a pointless question?")
|
||||||
|
|
||||||
|
|
||||||
The return value can be ignored implicitly if the called proc/iterator has
|
The return value can be ignored implicitly if the called proc/iterator has
|
||||||
been declared with the ``discardable`` pragma:
|
been declared with the ``discardable`` pragma:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
proc p(x, y: int): int {.discardable.} =
|
proc p(x, y: int): int {.discardable.} =
|
||||||
return x + y
|
return x + y
|
||||||
|
|
||||||
|
|
@ -681,7 +681,7 @@ parameters appear. This is especially true for procedures that construct a
|
||||||
complex data type. Therefore the arguments to a procedure can be named, so
|
complex data type. Therefore the arguments to a procedure can be named, so
|
||||||
that it is clear which argument belongs to which parameter:
|
that it is clear which argument belongs to which parameter:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
proc createWindow(x, y, width, height: int; title: string;
|
proc createWindow(x, y, width, height: int; title: string;
|
||||||
show: bool): Window =
|
show: bool): Window =
|
||||||
...
|
...
|
||||||
|
|
@ -693,7 +693,7 @@ Now that we use named arguments to call ``createWindow`` the argument order
|
||||||
does not matter anymore. Mixing named arguments with ordered arguments is
|
does not matter anymore. Mixing named arguments with ordered arguments is
|
||||||
also possible, but not very readable:
|
also possible, but not very readable:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
var w = createWindow(0, 0, title = "My Application",
|
var w = createWindow(0, 0, title = "My Application",
|
||||||
height = 600, width = 800, true)
|
height = 600, width = 800, true)
|
||||||
|
|
||||||
|
|
@ -706,7 +706,7 @@ To make the ``createWindow`` proc easier to use it should provide `default
|
||||||
values`, these are values that are used as arguments if the caller does not
|
values`, these are values that are used as arguments if the caller does not
|
||||||
specify them:
|
specify them:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
proc createWindow(x = 0, y = 0, width = 500, height = 700,
|
proc createWindow(x = 0, y = 0, width = 500, height = 700,
|
||||||
title = "unknown",
|
title = "unknown",
|
||||||
show = true): Window =
|
show = true): Window =
|
||||||
|
|
@ -723,9 +723,9 @@ no need to write ``title: string = "unknown"``, for example.
|
||||||
|
|
||||||
Overloaded procedures
|
Overloaded procedures
|
||||||
---------------------
|
---------------------
|
||||||
Nimrod provides the ability to overload procedures similar to C++:
|
Nim provides the ability to overload procedures similar to C++:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
proc toString(x: int): string = ...
|
proc toString(x: int): string = ...
|
||||||
proc toString(x: bool): string =
|
proc toString(x: bool): string =
|
||||||
if x: result = "true"
|
if x: result = "true"
|
||||||
|
|
@ -735,7 +735,7 @@ Nimrod provides the ability to overload procedures similar to C++:
|
||||||
echo(toString(true)) # calls the toString(x: bool) proc
|
echo(toString(true)) # calls the toString(x: bool) proc
|
||||||
|
|
||||||
(Note that ``toString`` is usually the `$ <system.html#$>`_ operator in
|
(Note that ``toString`` is usually the `$ <system.html#$>`_ operator in
|
||||||
Nimrod.) The compiler chooses the most appropriate proc for the ``toString``
|
Nim.) The compiler chooses the most appropriate proc for the ``toString``
|
||||||
calls. How this overloading resolution algorithm works exactly is not
|
calls. How this overloading resolution algorithm works exactly is not
|
||||||
discussed here (it will be specified in the manual soon). However, it does
|
discussed here (it will be specified in the manual soon). However, it does
|
||||||
not lead to nasty surprises and is based on a quite simple unification
|
not lead to nasty surprises and is based on a quite simple unification
|
||||||
|
|
@ -744,13 +744,13 @@ algorithm. Ambiguous calls are reported as errors.
|
||||||
|
|
||||||
Operators
|
Operators
|
||||||
---------
|
---------
|
||||||
The Nimrod library makes heavy use of overloading - one reason for this is that
|
The Nim library makes heavy use of overloading - one reason for this is that
|
||||||
each operator like ``+`` is a just an overloaded proc. The parser lets you
|
each operator like ``+`` is a just an overloaded proc. The parser lets you
|
||||||
use operators in `infix notation` (``a + b``) or `prefix notation` (``+ a``).
|
use operators in `infix notation` (``a + b``) or `prefix notation` (``+ a``).
|
||||||
An infix operator always receives two arguments, a prefix operator always one.
|
An infix operator always receives two arguments, a prefix operator always one.
|
||||||
Postfix operators are not possible, because this would be ambiguous: does
|
Postfix operators are not possible, because this would be ambiguous: does
|
||||||
``a @ @ b`` mean ``(a) @ (@b)`` or ``(a@) @ (b)``? It always means
|
``a @ @ b`` mean ``(a) @ (@b)`` or ``(a@) @ (b)``? It always means
|
||||||
``(a) @ (@b)``, because there are no postfix operators in Nimrod.
|
``(a) @ (@b)``, because there are no postfix operators in Nim.
|
||||||
|
|
||||||
Apart from a few built-in keyword operators such as ``and``, ``or``, ``not``,
|
Apart from a few built-in keyword operators such as ``and``, ``or``, ``not``,
|
||||||
operators always consist of these characters:
|
operators always consist of these characters:
|
||||||
|
|
@ -764,7 +764,7 @@ can be found in the manual.
|
||||||
|
|
||||||
To define a new operator enclose the operator in backticks "``":
|
To define a new operator enclose the operator in backticks "``":
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
proc `$` (x: myDataType): string = ...
|
proc `$` (x: myDataType): string = ...
|
||||||
# now the $ operator also works with myDataType, overloading resolution
|
# now the $ operator also works with myDataType, overloading resolution
|
||||||
# ensures that $ works for built-in types just like before
|
# ensures that $ works for built-in types just like before
|
||||||
|
|
@ -772,7 +772,7 @@ To define a new operator enclose the operator in backticks "``":
|
||||||
The "``" notation can also be used to call an operator just like any other
|
The "``" notation can also be used to call an operator just like any other
|
||||||
procedure:
|
procedure:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
if `==`( `+`(3, 4), 7): echo("True")
|
if `==`( `+`(3, 4), 7): echo("True")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -783,7 +783,7 @@ Every variable, procedure, etc. needs to be declared before it can be used.
|
||||||
(The reason for this is compilation efficiency.)
|
(The reason for this is compilation efficiency.)
|
||||||
However, this cannot be done for mutually recursive procedures:
|
However, this cannot be done for mutually recursive procedures:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
# forward declaration:
|
# forward declaration:
|
||||||
proc even(n: int): bool
|
proc even(n: int): bool
|
||||||
|
|
||||||
|
|
@ -810,7 +810,7 @@ Iterators
|
||||||
|
|
||||||
Let's return to the boring counting example:
|
Let's return to the boring counting example:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
echo("Counting to ten: ")
|
echo("Counting to ten: ")
|
||||||
for i in countup(1, 10):
|
for i in countup(1, 10):
|
||||||
echo($i)
|
echo($i)
|
||||||
|
|
@ -818,7 +818,7 @@ Let's return to the boring counting example:
|
||||||
Can a `countup <system.html#countup>`_ proc be written that supports this
|
Can a `countup <system.html#countup>`_ proc be written that supports this
|
||||||
loop? Lets try:
|
loop? Lets try:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
proc countup(a, b: int): int =
|
proc countup(a, b: int): int =
|
||||||
var res = a
|
var res = a
|
||||||
while res <= b:
|
while res <= b:
|
||||||
|
|
@ -831,7 +831,7 @@ finished. This *return and continue* is called a `yield` statement. Now
|
||||||
the only thing left to do is to replace the ``proc`` keyword by ``iterator``
|
the only thing left to do is to replace the ``proc`` keyword by ``iterator``
|
||||||
and there it is - our first iterator:
|
and there it is - our first iterator:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
iterator countup(a, b: int): int =
|
iterator countup(a, b: int): int =
|
||||||
var res = a
|
var res = a
|
||||||
while res <= b:
|
while res <= b:
|
||||||
|
|
@ -868,7 +868,7 @@ that are available for them in detail.
|
||||||
Booleans
|
Booleans
|
||||||
--------
|
--------
|
||||||
|
|
||||||
The boolean type is named ``bool`` in Nimrod and consists of the two
|
The boolean type is named ``bool`` in Nim and consists of the two
|
||||||
pre-defined values ``true`` and ``false``. Conditions in while,
|
pre-defined values ``true`` and ``false``. Conditions in while,
|
||||||
if, elif, when statements need to be of type bool.
|
if, elif, when statements need to be of type bool.
|
||||||
|
|
||||||
|
|
@ -876,7 +876,7 @@ The operators ``not, and, or, xor, <, <=, >, >=, !=, ==`` are defined
|
||||||
for the bool type. The ``and`` and ``or`` operators perform short-cut
|
for the bool type. The ``and`` and ``or`` operators perform short-cut
|
||||||
evaluation. Example:
|
evaluation. Example:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
|
|
||||||
while p != nil and p.name != "xyz":
|
while p != nil and p.name != "xyz":
|
||||||
# p.name is not evaluated if p == nil
|
# p.name is not evaluated if p == nil
|
||||||
|
|
@ -885,7 +885,7 @@ evaluation. Example:
|
||||||
|
|
||||||
Characters
|
Characters
|
||||||
----------
|
----------
|
||||||
The `character type` is named ``char`` in Nimrod. Its size is one byte.
|
The `character type` is named ``char`` in Nim. Its size is one byte.
|
||||||
Thus it cannot represent an UTF-8 character, but a part of it.
|
Thus it cannot represent an UTF-8 character, but a part of it.
|
||||||
The reason for this is efficiency: for the overwhelming majority of use-cases,
|
The reason for this is efficiency: for the overwhelming majority of use-cases,
|
||||||
the resulting programs will still handle UTF-8 properly as UTF-8 was specially
|
the resulting programs will still handle UTF-8 properly as UTF-8 was specially
|
||||||
|
|
@ -900,13 +900,13 @@ Converting from an integer to a ``char`` is done with the ``chr`` proc.
|
||||||
|
|
||||||
Strings
|
Strings
|
||||||
-------
|
-------
|
||||||
String variables in Nimrod are **mutable**, so appending to a string
|
String variables in Nim are **mutable**, so appending to a string
|
||||||
is quite efficient. Strings in Nimrod are both zero-terminated and have a
|
is quite efficient. Strings in Nim are both zero-terminated and have a
|
||||||
length field. One can retrieve a string's length with the builtin ``len``
|
length field. One can retrieve a string's length with the builtin ``len``
|
||||||
procedure; the length never counts the terminating zero. Accessing the
|
procedure; the length never counts the terminating zero. Accessing the
|
||||||
terminating zero is no error and often leads to simpler code:
|
terminating zero is no error and often leads to simpler code:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
if s[i] == 'a' and s[i+1] == 'b':
|
if s[i] == 'a' and s[i+1] == 'b':
|
||||||
# no need to check whether ``i < len(s)``!
|
# no need to check whether ``i < len(s)``!
|
||||||
...
|
...
|
||||||
|
|
@ -929,14 +929,14 @@ object on the heap, so there is a trade-off to be made here.
|
||||||
|
|
||||||
Integers
|
Integers
|
||||||
--------
|
--------
|
||||||
Nimrod has these integer types built-in:
|
Nim has these integer types built-in:
|
||||||
``int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64``.
|
``int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64``.
|
||||||
|
|
||||||
The default integer type is ``int``. Integer literals can have a *type suffix*
|
The default integer type is ``int``. Integer literals can have a *type suffix*
|
||||||
to mark them to be of another integer type:
|
to mark them to be of another integer type:
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
let
|
let
|
||||||
x = 0 # x is of type ``int``
|
x = 0 # x is of type ``int``
|
||||||
y = 0'i8 # y is of type ``int8``
|
y = 0'i8 # y is of type ``int8``
|
||||||
|
|
@ -964,7 +964,7 @@ cannot be detected at compile time).
|
||||||
|
|
||||||
Floats
|
Floats
|
||||||
------
|
------
|
||||||
Nimrod has these floating point types built-in: ``float float32 float64``.
|
Nim has these floating point types built-in: ``float float32 float64``.
|
||||||
|
|
||||||
The default float type is ``float``. In the current implementation,
|
The default float type is ``float``. In the current implementation,
|
||||||
``float`` is always 64 bit wide.
|
``float`` is always 64 bit wide.
|
||||||
|
|
@ -972,7 +972,7 @@ The default float type is ``float``. In the current implementation,
|
||||||
Float literals can have a *type suffix* to mark them to be of another float
|
Float literals can have a *type suffix* to mark them to be of another float
|
||||||
type:
|
type:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
var
|
var
|
||||||
x = 0.0 # x is of type ``float``
|
x = 0.0 # x is of type ``float``
|
||||||
y = 0.0'f32 # y is of type ``float32``
|
y = 0.0'f32 # y is of type ``float32``
|
||||||
|
|
@ -1001,11 +1001,11 @@ having to write its ``$`` operator. You can use then the `repr
|
||||||
graphs with cycles. The following example shows that even for basic types
|
graphs with cycles. The following example shows that even for basic types
|
||||||
there is a difference between the ``$`` and ``repr`` outputs:
|
there is a difference between the ``$`` and ``repr`` outputs:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
var
|
var
|
||||||
myBool = true
|
myBool = true
|
||||||
myCharacter = 'n'
|
myCharacter = 'n'
|
||||||
myString = "nimrod"
|
myString = "nim"
|
||||||
myInteger = 42
|
myInteger = 42
|
||||||
myFloat = 3.14
|
myFloat = 3.14
|
||||||
echo($myBool, ":", repr(myBool))
|
echo($myBool, ":", repr(myBool))
|
||||||
|
|
@ -1013,7 +1013,7 @@ there is a difference between the ``$`` and ``repr`` outputs:
|
||||||
echo($myCharacter, ":", repr(myCharacter))
|
echo($myCharacter, ":", repr(myCharacter))
|
||||||
# --> n:'n'
|
# --> n:'n'
|
||||||
echo($myString, ":", repr(myString))
|
echo($myString, ":", repr(myString))
|
||||||
# --> nimrod:0x10fa8c050"nimrod"
|
# --> nim:0x10fa8c050"nim"
|
||||||
echo($myInteger, ":", repr(myInteger))
|
echo($myInteger, ":", repr(myInteger))
|
||||||
# --> 42:42
|
# --> 42:42
|
||||||
echo($myFloat, ":", repr(myFloat))
|
echo($myFloat, ":", repr(myFloat))
|
||||||
|
|
@ -1023,9 +1023,9 @@ there is a difference between the ``$`` and ``repr`` outputs:
|
||||||
Advanced types
|
Advanced types
|
||||||
==============
|
==============
|
||||||
|
|
||||||
In Nimrod new types can be defined within a ``type`` statement:
|
In Nim new types can be defined within a ``type`` statement:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
biggestInt = int64 # biggest integer type that is available
|
biggestInt = int64 # biggest integer type that is available
|
||||||
biggestFloat = float64 # biggest float type that is available
|
biggestFloat = float64 # biggest float type that is available
|
||||||
|
|
@ -1041,7 +1041,7 @@ limited set. This set consists of ordered symbols. Each symbol is mapped
|
||||||
to an integer value internally. The first symbol is represented
|
to an integer value internally. The first symbol is represented
|
||||||
at runtime by 0, the second by 1 and so on. Example:
|
at runtime by 0, the second by 1 and so on. Example:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
|
|
||||||
type
|
type
|
||||||
TDirection = enum
|
TDirection = enum
|
||||||
|
|
@ -1050,7 +1050,7 @@ at runtime by 0, the second by 1 and so on. Example:
|
||||||
var x = south # `x` is of type `TDirection`; its value is `south`
|
var x = south # `x` is of type `TDirection`; its value is `south`
|
||||||
echo($x) # writes "south" to `stdout`
|
echo($x) # writes "south" to `stdout`
|
||||||
|
|
||||||
(To prefix a new type with the letter ``T`` is a convention in Nimrod.)
|
(To prefix a new type with the letter ``T`` is a convention in Nim.)
|
||||||
All comparison operators can be used with enumeration types.
|
All comparison operators can be used with enumeration types.
|
||||||
|
|
||||||
An enumeration's symbol can be qualified to avoid ambiguities:
|
An enumeration's symbol can be qualified to avoid ambiguities:
|
||||||
|
|
@ -1066,7 +1066,7 @@ explicitly given is assigned the value of the previous symbol + 1.
|
||||||
|
|
||||||
An explicit ordered enum can have *holes*:
|
An explicit ordered enum can have *holes*:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
TMyEnum = enum
|
TMyEnum = enum
|
||||||
a = 2, b = 4, c = 89
|
a = 2, b = 4, c = 89
|
||||||
|
|
@ -1104,7 +1104,7 @@ Subranges
|
||||||
A subrange type is a range of values from an integer or enumeration type
|
A subrange type is a range of values from an integer or enumeration type
|
||||||
(the base type). Example:
|
(the base type). Example:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
TSubrange = range[0..5]
|
TSubrange = range[0..5]
|
||||||
|
|
||||||
|
|
@ -1119,7 +1119,7 @@ type as ``range[0..high(int)]`` (`high <system.html#high>`_ returns the
|
||||||
maximal value). Other programming languages mandate the usage of unsigned
|
maximal value). Other programming languages mandate the usage of unsigned
|
||||||
integers for natural numbers. This is often **wrong**: you don't want unsigned
|
integers for natural numbers. This is often **wrong**: you don't want unsigned
|
||||||
arithmetic (which wraps around) just because the numbers cannot be negative.
|
arithmetic (which wraps around) just because the numbers cannot be negative.
|
||||||
Nimrod's ``Natural`` type helps to avoid this common programming error.
|
Nim's ``Natural`` type helps to avoid this common programming error.
|
||||||
|
|
||||||
|
|
||||||
Sets
|
Sets
|
||||||
|
|
@ -1134,7 +1134,7 @@ the array has the same type. The array's index type can be any ordinal type.
|
||||||
|
|
||||||
Arrays can be constructed via ``[]``:
|
Arrays can be constructed via ``[]``:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
|
|
||||||
type
|
type
|
||||||
TIntArray = array[0..5, int] # an array that is indexed with 0..5
|
TIntArray = array[0..5, int] # an array that is indexed with 0..5
|
||||||
|
|
@ -1149,14 +1149,14 @@ Array access is always bounds checked (at compile-time or at runtime). These
|
||||||
checks can be disabled via pragmas or invoking the compiler with the
|
checks can be disabled via pragmas or invoking the compiler with the
|
||||||
``--bound_checks:off`` command line switch.
|
``--bound_checks:off`` command line switch.
|
||||||
|
|
||||||
Arrays are value types, like any other Nimrod type. The assignment operator
|
Arrays are value types, like any other Nim type. The assignment operator
|
||||||
copies the whole array contents.
|
copies the whole array contents.
|
||||||
|
|
||||||
The built-in `len <system.html#len,TOpenArray>`_ proc returns the array's
|
The built-in `len <system.html#len,TOpenArray>`_ proc returns the array's
|
||||||
length. `low(a) <system.html#low>`_ returns the lowest valid index for the
|
length. `low(a) <system.html#low>`_ returns the lowest valid index for the
|
||||||
array `a` and `high(a) <system.html#high>`_ the highest valid index.
|
array `a` and `high(a) <system.html#high>`_ the highest valid index.
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
TDirection = enum
|
TDirection = enum
|
||||||
north, east, south, west
|
north, east, south, west
|
||||||
|
|
@ -1175,13 +1175,13 @@ array `a` and `high(a) <system.html#high>`_ the highest valid index.
|
||||||
|
|
||||||
The syntax for nested arrays (multidimensional) in other languages is a matter
|
The syntax for nested arrays (multidimensional) in other languages is a matter
|
||||||
of appending more brackets because usually each dimension is restricted to the
|
of appending more brackets because usually each dimension is restricted to the
|
||||||
same index type as the others. In nimrod you can have different dimensions with
|
same index type as the others. In Nim you can have different dimensions with
|
||||||
different index types, so the nesting syntax is slightly different. Building on
|
different index types, so the nesting syntax is slightly different. Building on
|
||||||
the previous example where a level is defined as an array of enums indexed by
|
the previous example where a level is defined as an array of enums indexed by
|
||||||
yet another enum, we can add the following lines to add a light tower type
|
yet another enum, we can add the following lines to add a light tower type
|
||||||
subdivided in height levels accessed through their integer index:
|
subdivided in height levels accessed through their integer index:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
TLightTower = array[1..10, TLevelSetting]
|
TLightTower = array[1..10, TLevelSetting]
|
||||||
var
|
var
|
||||||
|
|
@ -1200,14 +1200,14 @@ length. Another way of defining the ``TLightTower`` to show better its
|
||||||
nested nature would be to omit the previous definition of the ``TLevelSetting``
|
nested nature would be to omit the previous definition of the ``TLevelSetting``
|
||||||
type and instead write it embedded directly as the type of the first dimension:
|
type and instead write it embedded directly as the type of the first dimension:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
TLightTower = array[1..10, array[north..west, TBlinkLights]]
|
TLightTower = array[1..10, array[north..west, TBlinkLights]]
|
||||||
|
|
||||||
It is quite frequent to have arrays start at zero, so there's a shortcut syntax
|
It is quite frequent to have arrays start at zero, so there's a shortcut syntax
|
||||||
to specify a range from zero to the specified index minus one:
|
to specify a range from zero to the specified index minus one:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
TIntArray = array[0..5, int] # an array that is indexed with 0..5
|
TIntArray = array[0..5, int] # an array that is indexed with 0..5
|
||||||
TQuickArray = array[6, int] # an array that is indexed with 0..5
|
TQuickArray = array[6, int] # an array that is indexed with 0..5
|
||||||
|
|
@ -1239,7 +1239,7 @@ A sequence may be passed to an openarray parameter.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
|
|
||||||
var
|
var
|
||||||
x: seq[int] # a sequence of integers
|
x: seq[int] # a sequence of integers
|
||||||
|
|
@ -1261,7 +1261,7 @@ value. Here the ``for`` statement is looping over the results from the
|
||||||
`pairs() <system.html#pairs.i,seq[T]>`_ iterator from the `system
|
`pairs() <system.html#pairs.i,seq[T]>`_ iterator from the `system
|
||||||
<system.html>`_ module. Examples:
|
<system.html>`_ module. Examples:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
for i in @[3, 4, 5]:
|
for i in @[3, 4, 5]:
|
||||||
echo($i)
|
echo($i)
|
||||||
# --> 3
|
# --> 3
|
||||||
|
|
@ -1299,7 +1299,7 @@ also a means to implement passing a variable number of
|
||||||
arguments to a procedure. The compiler converts the list of arguments
|
arguments to a procedure. The compiler converts the list of arguments
|
||||||
to an array automatically:
|
to an array automatically:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
proc myWriteln(f: TFile, a: varargs[string]) =
|
proc myWriteln(f: TFile, a: varargs[string]) =
|
||||||
for s in items(a):
|
for s in items(a):
|
||||||
write(f, s)
|
write(f, s)
|
||||||
|
|
@ -1313,7 +1313,7 @@ This transformation is only done if the varargs parameter is the
|
||||||
last parameter in the procedure header. It is also possible to perform
|
last parameter in the procedure header. It is also possible to perform
|
||||||
type conversions in this context:
|
type conversions in this context:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
proc myWriteln(f: TFile, a: varargs[string, `$`]) =
|
proc myWriteln(f: TFile, a: varargs[string, `$`]) =
|
||||||
for s in items(a):
|
for s in items(a):
|
||||||
write(f, s)
|
write(f, s)
|
||||||
|
|
@ -1336,10 +1336,10 @@ context. A slice is just an object of type TSlice which contains two bounds,
|
||||||
`a` and `b`. By itself a slice is not very useful, but other collection types
|
`a` and `b`. By itself a slice is not very useful, but other collection types
|
||||||
define operators which accept TSlice objects to define ranges.
|
define operators which accept TSlice objects to define ranges.
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
|
|
||||||
var
|
var
|
||||||
a = "Nimrod is a progamming language"
|
a = "Nim is a progamming language"
|
||||||
b = "Slices are useless."
|
b = "Slices are useless."
|
||||||
|
|
||||||
echo a[10..15] # --> 'a prog'
|
echo a[10..15] # --> 'a prog'
|
||||||
|
|
@ -1366,7 +1366,7 @@ The assignment operator for tuples copies each component. The notation
|
||||||
``t[i]`` to access the ``i``'th field. Here ``i`` needs to be a constant
|
``t[i]`` to access the ``i``'th field. Here ``i`` needs to be a constant
|
||||||
integer.
|
integer.
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
|
|
||||||
type
|
type
|
||||||
TPerson = tuple[name: string, age: int] # type representing a person:
|
TPerson = tuple[name: string, age: int] # type representing a person:
|
||||||
|
|
@ -1411,19 +1411,19 @@ use parenthesis around the values you want to assign the unpacking to,
|
||||||
otherwise you will be assigning the same value to all the individual
|
otherwise you will be assigning the same value to all the individual
|
||||||
variables! Example:
|
variables! Example:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
let
|
let
|
||||||
path = "usr/local/nimrodc.html"
|
path = "usr/local/nimc.html"
|
||||||
(dir, name, ext) = splitFile(path)
|
(dir, name, ext) = splitFile(path)
|
||||||
baddir, badname, badext = splitFile(path)
|
baddir, badname, badext = splitFile(path)
|
||||||
echo dir # outputs `usr/local`
|
echo dir # outputs `usr/local`
|
||||||
echo name # outputs `nimrodc`
|
echo name # outputs `nimc`
|
||||||
echo ext # outputs `.html`
|
echo ext # outputs `.html`
|
||||||
# All the following output the same line:
|
# All the following output the same line:
|
||||||
# `(dir: usr/local, name: nimrodc, ext: .html)`
|
# `(dir: usr/local, name: nimc, ext: .html)`
|
||||||
echo baddir
|
echo baddir
|
||||||
echo badname
|
echo badname
|
||||||
echo badext
|
echo badext
|
||||||
|
|
@ -1431,12 +1431,12 @@ variables! Example:
|
||||||
Tuple unpacking **only** works in ``var`` or ``let`` blocks. The following code
|
Tuple unpacking **only** works in ``var`` or ``let`` blocks. The following code
|
||||||
won't compile:
|
won't compile:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
var
|
var
|
||||||
path = "usr/local/nimrodc.html"
|
path = "usr/local/nimc.html"
|
||||||
dir, name, ext = ""
|
dir, name, ext = ""
|
||||||
|
|
||||||
(dir, name, ext) = splitFile(path)
|
(dir, name, ext) = splitFile(path)
|
||||||
|
|
@ -1449,7 +1449,7 @@ References (similar to pointers in other programming languages) are a
|
||||||
way to introduce many-to-one relationships. This means different references can
|
way to introduce many-to-one relationships. This means different references can
|
||||||
point to and modify the same location in memory.
|
point to and modify the same location in memory.
|
||||||
|
|
||||||
Nimrod distinguishes between `traced`:idx: and `untraced`:idx: references.
|
Nim distinguishes between `traced`:idx: and `untraced`:idx: references.
|
||||||
Untraced references are also called *pointers*. Traced references point to
|
Untraced references are also called *pointers*. Traced references point to
|
||||||
objects of a garbage collected heap, untraced references point to
|
objects of a garbage collected heap, untraced references point to
|
||||||
manually allocated objects or to objects somewhere else in memory. Thus
|
manually allocated objects or to objects somewhere else in memory. Thus
|
||||||
|
|
@ -1464,7 +1464,7 @@ meaning to retrieve the item the reference points to. The ``.`` (access a
|
||||||
tuple/object field operator) and ``[]`` (array/string/sequence index operator)
|
tuple/object field operator) and ``[]`` (array/string/sequence index operator)
|
||||||
operators perform implicit dereferencing operations for reference types:
|
operators perform implicit dereferencing operations for reference types:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
|
|
||||||
type
|
type
|
||||||
PNode = ref TNode
|
PNode = ref TNode
|
||||||
|
|
@ -1489,12 +1489,12 @@ Procedural type
|
||||||
---------------
|
---------------
|
||||||
A procedural type is a (somewhat abstract) pointer to a procedure.
|
A procedural type is a (somewhat abstract) pointer to a procedure.
|
||||||
``nil`` is an allowed value for a variable of a procedural type.
|
``nil`` is an allowed value for a variable of a procedural type.
|
||||||
Nimrod uses procedural types to achieve `functional`:idx: programming
|
Nim uses procedural types to achieve `functional`:idx: programming
|
||||||
techniques.
|
techniques.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
proc echoItem(x: int) = echo(x)
|
proc echoItem(x: int) = echo(x)
|
||||||
|
|
||||||
proc forEach(action: proc (x: int)) =
|
proc forEach(action: proc (x: int)) =
|
||||||
|
|
@ -1513,13 +1513,13 @@ listed in the `manual <manual.html>`_.
|
||||||
|
|
||||||
Modules
|
Modules
|
||||||
=======
|
=======
|
||||||
Nimrod supports splitting a program into pieces with a module concept.
|
Nim supports splitting a program into pieces with a module concept.
|
||||||
Each module is in its own file. Modules enable `information hiding`:idx: and
|
Each module is in its own file. Modules enable `information hiding`:idx: and
|
||||||
`separate compilation`:idx:. A module may gain access to symbols of another
|
`separate compilation`:idx:. A module may gain access to symbols of another
|
||||||
module by the `import`:idx: statement. Only top-level symbols that are marked
|
module by the `import`:idx: statement. Only top-level symbols that are marked
|
||||||
with an asterisk (``*``) are exported:
|
with an asterisk (``*``) are exported:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
# Module A
|
# Module A
|
||||||
var
|
var
|
||||||
x*, y: int
|
x*, y: int
|
||||||
|
|
@ -1554,7 +1554,7 @@ The algorithm for compiling modules is:
|
||||||
|
|
||||||
This is best illustrated by an example:
|
This is best illustrated by an example:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
# Module A
|
# Module A
|
||||||
type
|
type
|
||||||
T1* = int # Module A exports the type ``T1``
|
T1* = int # Module A exports the type ``T1``
|
||||||
|
|
@ -1565,7 +1565,7 @@ This is best illustrated by an example:
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
# Module B
|
# Module B
|
||||||
import A # A is not parsed here! Only the already known symbols
|
import A # A is not parsed here! Only the already known symbols
|
||||||
# of A are imported.
|
# of A are imported.
|
||||||
|
|
@ -1581,15 +1581,15 @@ the symbol is ambiguous, it even *has* to be qualified. A symbol is ambiguous
|
||||||
if it is defined in two (or more) different modules and both modules are
|
if it is defined in two (or more) different modules and both modules are
|
||||||
imported by a third one:
|
imported by a third one:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
# Module A
|
# Module A
|
||||||
var x*: string
|
var x*: string
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
# Module B
|
# Module B
|
||||||
var x*: int
|
var x*: int
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
# Module C
|
# Module C
|
||||||
import A, B
|
import A, B
|
||||||
write(stdout, x) # error: x is ambiguous
|
write(stdout, x) # error: x is ambiguous
|
||||||
|
|
@ -1602,15 +1602,15 @@ imported by a third one:
|
||||||
But this rule does not apply to procedures or iterators. Here the overloading
|
But this rule does not apply to procedures or iterators. Here the overloading
|
||||||
rules apply:
|
rules apply:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
# Module A
|
# Module A
|
||||||
proc x*(a: int): string = result = $a
|
proc x*(a: int): string = result = $a
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
# Module B
|
# Module B
|
||||||
proc x*(a: string): string = result = $a
|
proc x*(a: string): string = result = $a
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
# Module C
|
# Module C
|
||||||
import A, B
|
import A, B
|
||||||
write(stdout, x(3)) # no error: A.x is called
|
write(stdout, x(3)) # no error: A.x is called
|
||||||
|
|
@ -1627,7 +1627,7 @@ The normal ``import`` statement will bring in all exported symbols.
|
||||||
These can be limited by naming symbols which should be excluded with
|
These can be limited by naming symbols which should be excluded with
|
||||||
the ``except`` qualifier.
|
the ``except`` qualifier.
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
import mymodule except y
|
import mymodule except y
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1638,19 +1638,19 @@ We have already seen the simple ``import`` statement that just imports all
|
||||||
exported symbols. An alternative that only imports listed symbols is the
|
exported symbols. An alternative that only imports listed symbols is the
|
||||||
``from import`` statement:
|
``from import`` statement:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
from mymodule import x, y, z
|
from mymodule import x, y, z
|
||||||
|
|
||||||
The ``from`` statement can also force namespace qualification on
|
The ``from`` statement can also force namespace qualification on
|
||||||
symbols, thereby making symbols available, but needing to be qualified
|
symbols, thereby making symbols available, but needing to be qualified
|
||||||
to be used.
|
to be used.
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
from mymodule import x, y, z
|
from mymodule import x, y, z
|
||||||
|
|
||||||
x() # use x without any qualification
|
x() # use x without any qualification
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
from mymodule import nil
|
from mymodule import nil
|
||||||
|
|
||||||
mymodule.x() # must qualify x with the module name as prefix
|
mymodule.x() # must qualify x with the module name as prefix
|
||||||
|
|
@ -1660,7 +1660,7 @@ to be used.
|
||||||
Since module names are generally long to be descriptive, you can also
|
Since module names are generally long to be descriptive, you can also
|
||||||
define a shorter alias to use when qualifying symbols.
|
define a shorter alias to use when qualifying symbols.
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
from mymodule as m import nil
|
from mymodule as m import nil
|
||||||
|
|
||||||
m.x() # m is aliasing mymodule
|
m.x() # m is aliasing mymodule
|
||||||
|
|
@ -1672,7 +1672,7 @@ The ``include`` statement does something fundamentally different than
|
||||||
importing a module: it merely includes the contents of a file. The ``include``
|
importing a module: it merely includes the contents of a file. The ``include``
|
||||||
statement is useful to split up a large module into several files:
|
statement is useful to split up a large module into several files:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
include fileA, fileB, fileC
|
include fileA, fileB, fileC
|
||||||
|
|
||||||
**Note**: The documentation generator currently does not follow ``include``
|
**Note**: The documentation generator currently does not follow ``include``
|
||||||
|
|
@ -1683,7 +1683,7 @@ generated documentation.
|
||||||
Part 2
|
Part 2
|
||||||
======
|
======
|
||||||
|
|
||||||
So, now that we are done with the basics, let's see what Nimrod offers apart
|
So, now that we are done with the basics, let's see what Nim offers apart
|
||||||
from a nice syntax for procedural programming: `Part II <tut2.html>`_
|
from a nice syntax for procedural programming: `Part II <tut2.html>`_
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
139
doc/tut2.txt
139
doc/tut2.txt
|
|
@ -1,9 +1,9 @@
|
||||||
=========================
|
======================
|
||||||
Nimrod Tutorial (Part II)
|
Nim Tutorial (Part II)
|
||||||
=========================
|
======================
|
||||||
|
|
||||||
:Author: Andreas Rumpf
|
:Author: Andreas Rumpf
|
||||||
:Version: |nimrodversion|
|
:Version: |nimversion|
|
||||||
|
|
||||||
.. contents::
|
.. contents::
|
||||||
|
|
||||||
|
|
@ -15,7 +15,7 @@ Introduction
|
||||||
only have originated in California." --Edsger Dijkstra
|
only have originated in California." --Edsger Dijkstra
|
||||||
|
|
||||||
|
|
||||||
This document is a tutorial for the advanced constructs of the *Nimrod*
|
This document is a tutorial for the advanced constructs of the *Nim*
|
||||||
programming language. **Note that this document is somewhat obsolete as the**
|
programming language. **Note that this document is somewhat obsolete as the**
|
||||||
`manual <manual.html>`_ **contains many more examples of the advanced language
|
`manual <manual.html>`_ **contains many more examples of the advanced language
|
||||||
features.**
|
features.**
|
||||||
|
|
@ -24,18 +24,18 @@ features.**
|
||||||
Pragmas
|
Pragmas
|
||||||
=======
|
=======
|
||||||
|
|
||||||
Pragmas are Nimrod's method to give the compiler additional information/
|
Pragmas are Nim's method to give the compiler additional information/
|
||||||
commands without introducing a massive number of new keywords. Pragmas are
|
commands without introducing a massive number of new keywords. Pragmas are
|
||||||
enclosed in the special ``{.`` and ``.}`` curly dot brackets. This tutorial
|
enclosed in the special ``{.`` and ``.}`` curly dot brackets. This tutorial
|
||||||
does not cover pragmas. See the `manual <manual.html#pragmas>`_ or `user guide
|
does not cover pragmas. See the `manual <manual.html#pragmas>`_ or `user guide
|
||||||
<nimrodc.html#additional-features>`_ for a description of the available
|
<nimc.html#additional-features>`_ for a description of the available
|
||||||
pragmas.
|
pragmas.
|
||||||
|
|
||||||
|
|
||||||
Object Oriented Programming
|
Object Oriented Programming
|
||||||
===========================
|
===========================
|
||||||
|
|
||||||
While Nimrod's support for object oriented programming (OOP) is minimalistic,
|
While Nim's support for object oriented programming (OOP) is minimalistic,
|
||||||
powerful OOP technics can be used. OOP is seen as *one* way to design a
|
powerful OOP technics can be used. OOP is seen as *one* way to design a
|
||||||
program, not *the only* way. Often a procedural approach leads to simpler
|
program, not *the only* way. Often a procedural approach leads to simpler
|
||||||
and more efficient code. In particular, prefering composition over inheritance
|
and more efficient code. In particular, prefering composition over inheritance
|
||||||
|
|
@ -55,7 +55,7 @@ a *constructor*).
|
||||||
Objects have access to their type at runtime. There is an
|
Objects have access to their type at runtime. There is an
|
||||||
``of`` operator that can be used to check the object's type:
|
``of`` operator that can be used to check the object's type:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
TPerson = object of TObject
|
TPerson = object of TObject
|
||||||
name*: string # the * means that `name` is accessible from other modules
|
name*: string # the * means that `name` is accessible from other modules
|
||||||
|
|
@ -86,20 +86,20 @@ in the GTK wrapper for instance.)
|
||||||
|
|
||||||
**Note**: Composition (*has-a* relation) is often preferable to inheritance
|
**Note**: Composition (*has-a* relation) is often preferable to inheritance
|
||||||
(*is-a* relation) for simple code reuse. Since objects are value types in
|
(*is-a* relation) for simple code reuse. Since objects are value types in
|
||||||
Nimrod, composition is as efficient as inheritance.
|
Nim, composition is as efficient as inheritance.
|
||||||
|
|
||||||
|
|
||||||
Mutually recursive types
|
Mutually recursive types
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
Objects, tuples and references can model quite complex data structures which
|
Objects, tuples and references can model quite complex data structures which
|
||||||
depend on each other; they are *mutually recursive*. In Nimrod
|
depend on each other; they are *mutually recursive*. In Nim
|
||||||
these types can only be declared within a single type section. (Anything else
|
these types can only be declared within a single type section. (Anything else
|
||||||
would require arbitrary symbol lookahead which slows down compilation.)
|
would require arbitrary symbol lookahead which slows down compilation.)
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
PNode = ref TNode # a traced reference to a TNode
|
PNode = ref TNode # a traced reference to a TNode
|
||||||
TNode = object
|
TNode = object
|
||||||
|
|
@ -114,7 +114,7 @@ Example:
|
||||||
|
|
||||||
Type conversions
|
Type conversions
|
||||||
----------------
|
----------------
|
||||||
Nimrod distinguishes between `type casts`:idx: and `type conversions`:idx:.
|
Nim distinguishes between `type casts`:idx: and `type conversions`:idx:.
|
||||||
Casts are done with the ``cast`` operator and force the compiler to
|
Casts are done with the ``cast`` operator and force the compiler to
|
||||||
interpret a bit pattern to be of another type.
|
interpret a bit pattern to be of another type.
|
||||||
|
|
||||||
|
|
@ -126,7 +126,7 @@ raised.
|
||||||
The syntax for type conversions is ``destination_type(expression_to_convert)``
|
The syntax for type conversions is ``destination_type(expression_to_convert)``
|
||||||
(like an ordinary call):
|
(like an ordinary call):
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
proc getID(x: TPerson): int =
|
proc getID(x: TPerson): int =
|
||||||
TStudent(x).id
|
TStudent(x).id
|
||||||
|
|
||||||
|
|
@ -141,9 +141,9 @@ variant types are needed.
|
||||||
|
|
||||||
An example:
|
An example:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
|
|
||||||
# This is an example how an abstract syntax tree could be modeled in Nimrod
|
# This is an example how an abstract syntax tree could be modeled in Nim
|
||||||
type
|
type
|
||||||
TNodeKind = enum # the different node types
|
TNodeKind = enum # the different node types
|
||||||
nkInt, # a leaf with an integer value
|
nkInt, # a leaf with an integer value
|
||||||
|
|
@ -183,8 +183,8 @@ bound to a class. This has disadvantages:
|
||||||
* Often it is unclear where the method should belong to: is
|
* Often it is unclear where the method should belong to: is
|
||||||
``join`` a string method or an array method?
|
``join`` a string method or an array method?
|
||||||
|
|
||||||
Nimrod avoids these problems by not assigning methods to a class. All methods
|
Nim avoids these problems by not assigning methods to a class. All methods
|
||||||
in Nimrod are multi-methods. As we will see later, multi-methods are
|
in Nim are multi-methods. As we will see later, multi-methods are
|
||||||
distinguished from procs only for dynamic binding purposes.
|
distinguished from procs only for dynamic binding purposes.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -199,7 +199,7 @@ If there are no remaining arguments, the parentheses can be omitted:
|
||||||
This method call syntax is not restricted to objects, it can be used
|
This method call syntax is not restricted to objects, it can be used
|
||||||
for any type:
|
for any type:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
|
|
||||||
echo("abc".len) # is the same as echo(len("abc"))
|
echo("abc".len) # is the same as echo(len("abc"))
|
||||||
echo("abc".toUpper())
|
echo("abc".toUpper())
|
||||||
|
|
@ -211,7 +211,7 @@ postfix notation.)
|
||||||
|
|
||||||
So "pure object oriented" code is easy to write:
|
So "pure object oriented" code is easy to write:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
import strutils
|
import strutils
|
||||||
|
|
||||||
stdout.writeln("Give a list of numbers (separated by spaces): ")
|
stdout.writeln("Give a list of numbers (separated by spaces): ")
|
||||||
|
|
@ -221,12 +221,12 @@ So "pure object oriented" code is easy to write:
|
||||||
|
|
||||||
Properties
|
Properties
|
||||||
----------
|
----------
|
||||||
As the above example shows, Nimrod has no need for *get-properties*:
|
As the above example shows, Nim has no need for *get-properties*:
|
||||||
Ordinary get-procedures that are called with the *method call syntax* achieve
|
Ordinary get-procedures that are called with the *method call syntax* achieve
|
||||||
the same. But setting a value is different; for this a special setter syntax
|
the same. But setting a value is different; for this a special setter syntax
|
||||||
is needed:
|
is needed:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
|
|
||||||
type
|
type
|
||||||
TSocket* = object of TObject
|
TSocket* = object of TObject
|
||||||
|
|
@ -252,7 +252,7 @@ is needed:
|
||||||
The ``[]`` array access operator can be overloaded to provide
|
The ``[]`` array access operator can be overloaded to provide
|
||||||
`array properties`:idx:\ :
|
`array properties`:idx:\ :
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
TVector* = object
|
TVector* = object
|
||||||
x, y, z: float
|
x, y, z: float
|
||||||
|
|
@ -283,7 +283,7 @@ Dynamic dispatch
|
||||||
Procedures always use static dispatch. For dynamic dispatch replace the
|
Procedures always use static dispatch. For dynamic dispatch replace the
|
||||||
``proc`` keyword by ``method``:
|
``proc`` keyword by ``method``:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
PExpr = ref object of TObject ## abstract base class for an expression
|
PExpr = ref object of TObject ## abstract base class for an expression
|
||||||
PLiteral = ref object of PExpr
|
PLiteral = ref object of PExpr
|
||||||
|
|
@ -311,7 +311,7 @@ requires dynamic binding.
|
||||||
In a multi-method all parameters that have an object type are used for the
|
In a multi-method all parameters that have an object type are used for the
|
||||||
dispatching:
|
dispatching:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
|
|
||||||
type
|
type
|
||||||
TThing = object of TObject
|
TThing = object of TObject
|
||||||
|
|
@ -336,7 +336,7 @@ As the example demonstrates, invocation of a multi-method cannot be ambiguous:
|
||||||
Collide 2 is preferred over collide 1 because the resolution works from left to
|
Collide 2 is preferred over collide 1 because the resolution works from left to
|
||||||
right. Thus ``TUnit, TThing`` is preferred over ``TThing, TUnit``.
|
right. Thus ``TUnit, TThing`` is preferred over ``TThing, TUnit``.
|
||||||
|
|
||||||
**Perfomance note**: Nimrod does not produce a virtual method table, but
|
**Perfomance note**: Nim does not produce a virtual method table, but
|
||||||
generates dispatch trees. This avoids the expensive indirect branch for method
|
generates dispatch trees. This avoids the expensive indirect branch for method
|
||||||
calls and enables inlining. However, other optimizations like compile time
|
calls and enables inlining. However, other optimizations like compile time
|
||||||
evaluation or dead code elimination do not work with methods.
|
evaluation or dead code elimination do not work with methods.
|
||||||
|
|
@ -345,7 +345,7 @@ evaluation or dead code elimination do not work with methods.
|
||||||
Exceptions
|
Exceptions
|
||||||
==========
|
==========
|
||||||
|
|
||||||
In Nimrod exceptions are objects. By convention, exception types are
|
In Nim exceptions are objects. By convention, exception types are
|
||||||
prefixed with an 'E', not 'T'. The `system <system.html>`_ module defines an
|
prefixed with an 'E', not 'T'. The `system <system.html>`_ module defines an
|
||||||
exception hierarchy that you might want to stick to. Exceptions derive from
|
exception hierarchy that you might want to stick to. Exceptions derive from
|
||||||
E_Base, which provides the common interface.
|
E_Base, which provides the common interface.
|
||||||
|
|
@ -364,7 +364,7 @@ Raise statement
|
||||||
---------------
|
---------------
|
||||||
Raising an exception is done with the ``raise`` statement:
|
Raising an exception is done with the ``raise`` statement:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
var
|
var
|
||||||
e: ref EOS
|
e: ref EOS
|
||||||
new(e)
|
new(e)
|
||||||
|
|
@ -375,7 +375,7 @@ If the ``raise`` keyword is not followed by an expression, the last exception
|
||||||
is *re-raised*. For the purpose of avoiding repeating this common code pattern,
|
is *re-raised*. For the purpose of avoiding repeating this common code pattern,
|
||||||
the template ``newException`` in the ``system`` module can be used:
|
the template ``newException`` in the ``system`` module can be used:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
raise newException(EOS, "the request to the OS failed")
|
raise newException(EOS, "the request to the OS failed")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -384,7 +384,7 @@ Try statement
|
||||||
|
|
||||||
The ``try`` statement handles exceptions:
|
The ``try`` statement handles exceptions:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
# read the first two lines of a text file that should contain numbers
|
# read the first two lines of a text file that should contain numbers
|
||||||
# and tries to add them
|
# and tries to add them
|
||||||
var
|
var
|
||||||
|
|
@ -428,7 +428,7 @@ If you need to *access* the actual exception object or message inside an
|
||||||
<system.html#getCurrentExceptionMsg>`_ procs from the `system <system.html>`_
|
<system.html#getCurrentExceptionMsg>`_ procs from the `system <system.html>`_
|
||||||
module. Example:
|
module. Example:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
try:
|
try:
|
||||||
doSomethingHere()
|
doSomethingHere()
|
||||||
except:
|
except:
|
||||||
|
|
@ -460,7 +460,7 @@ instance, if you specify that a proc raises ``EIO``, and at some point it (or
|
||||||
one of the procs it calls) starts raising a new exception the compiler will
|
one of the procs it calls) starts raising a new exception the compiler will
|
||||||
prevent that proc from compiling. Usage example:
|
prevent that proc from compiling. Usage example:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
proc complexProc() {.raises: [EIO, EArithmetic].} =
|
proc complexProc() {.raises: [EIO, EArithmetic].} =
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
@ -476,21 +476,21 @@ help you locate the offending code which has changed.
|
||||||
If you want to add the ``{.raises.}`` pragma to existing code, the compiler can
|
If you want to add the ``{.raises.}`` pragma to existing code, the compiler can
|
||||||
also help you. You can add the ``{.effects.}`` pragma statement to your proc and
|
also help you. You can add the ``{.effects.}`` pragma statement to your proc and
|
||||||
the compiler will output all inferred effects up to that point (exception
|
the compiler will output all inferred effects up to that point (exception
|
||||||
tracking is part of Nimrod's effect system). Another more roundabout way to
|
tracking is part of Nim's effect system). Another more roundabout way to
|
||||||
find out the list of exceptions raised by a proc is to use the Nimrod ``doc2``
|
find out the list of exceptions raised by a proc is to use the Nim ``doc2``
|
||||||
command which generates documentation for a whole module and decorates all
|
command which generates documentation for a whole module and decorates all
|
||||||
procs with the list of raised exceptions. You can read more about Nimrod's
|
procs with the list of raised exceptions. You can read more about Nim's
|
||||||
`effect system and related pragmas in the manual <manual.html#effect-system>`_.
|
`effect system and related pragmas in the manual <manual.html#effect-system>`_.
|
||||||
|
|
||||||
|
|
||||||
Generics
|
Generics
|
||||||
========
|
========
|
||||||
|
|
||||||
Generics are Nimrod's means to parametrize procs, iterators or types
|
Generics are Nim's means to parametrize procs, iterators or types
|
||||||
with `type parameters`:idx:. They are most useful for efficient type safe
|
with `type parameters`:idx:. They are most useful for efficient type safe
|
||||||
containers:
|
containers:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
TBinaryTree[T] = object # TBinaryTree is a generic type with
|
TBinaryTree[T] = object # TBinaryTree is a generic type with
|
||||||
# with generic param ``T``
|
# with generic param ``T``
|
||||||
|
|
@ -557,7 +557,7 @@ is not hidden and is used in the ``preorder`` iterator.
|
||||||
Templates
|
Templates
|
||||||
=========
|
=========
|
||||||
|
|
||||||
Templates are a simple substitution mechanism that operates on Nimrod's
|
Templates are a simple substitution mechanism that operates on Nim's
|
||||||
abstract syntax trees. Templates are processed in the semantic pass of the
|
abstract syntax trees. Templates are processed in the semantic pass of the
|
||||||
compiler. They integrate well with the rest of the language and share none
|
compiler. They integrate well with the rest of the language and share none
|
||||||
of C's preprocessor macros flaws.
|
of C's preprocessor macros flaws.
|
||||||
|
|
@ -566,7 +566,7 @@ To *invoke* a template, call it like a procedure.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
template `!=` (a, b: expr): expr =
|
template `!=` (a, b: expr): expr =
|
||||||
# this definition exists in the System module
|
# this definition exists in the System module
|
||||||
not (a == b)
|
not (a == b)
|
||||||
|
|
@ -585,7 +585,7 @@ for IEEE floating point numbers - NaN breaks basic boolean logic.)
|
||||||
Templates are especially useful for lazy evaluation purposes. Consider a
|
Templates are especially useful for lazy evaluation purposes. Consider a
|
||||||
simple proc for logging:
|
simple proc for logging:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
const
|
const
|
||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
|
|
@ -602,7 +602,7 @@ evaluation for procedures is *eager*).
|
||||||
|
|
||||||
Turning the ``log`` proc into a template solves this problem:
|
Turning the ``log`` proc into a template solves this problem:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
const
|
const
|
||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
|
|
@ -618,32 +618,11 @@ The parameters' types can be ordinary types or the meta types ``expr``
|
||||||
(stands for *type description*). If the template has no explicit return type,
|
(stands for *type description*). If the template has no explicit return type,
|
||||||
``stmt`` is used for consistency with procs and methods.
|
``stmt`` is used for consistency with procs and methods.
|
||||||
|
|
||||||
The template body does not open a new scope. To open a new scope use a ``block``
|
|
||||||
statement:
|
|
||||||
|
|
||||||
.. code-block:: nimrod
|
|
||||||
template declareInScope(x: expr, t: typeDesc): stmt {.immediate.} =
|
|
||||||
var x: t
|
|
||||||
|
|
||||||
template declareInNewScope(x: expr, t: typeDesc): stmt {.immediate.} =
|
|
||||||
# open a new scope:
|
|
||||||
block:
|
|
||||||
var x: t
|
|
||||||
|
|
||||||
declareInScope(a, int)
|
|
||||||
a = 42 # works, `a` is known here
|
|
||||||
|
|
||||||
declareInNewScope(b, int)
|
|
||||||
b = 42 # does not work, `b` is unknown
|
|
||||||
|
|
||||||
(The `manual explains <manual.html#ordinary-vs-immediate-templates>`_ why the
|
|
||||||
``immediate`` pragma is needed for these templates.)
|
|
||||||
|
|
||||||
If there is a ``stmt`` parameter it should be the last in the template
|
If there is a ``stmt`` parameter it should be the last in the template
|
||||||
declaration. The reason is that statements can be passed to a template
|
declaration. The reason is that statements can be passed to a template
|
||||||
via a special ``:`` syntax:
|
via a special ``:`` syntax:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
|
|
||||||
template withFile(f: expr, filename: string, mode: TFileMode,
|
template withFile(f: expr, filename: string, mode: TFileMode,
|
||||||
body: stmt): stmt {.immediate.} =
|
body: stmt): stmt {.immediate.} =
|
||||||
|
|
@ -672,18 +651,18 @@ Macros
|
||||||
======
|
======
|
||||||
|
|
||||||
Macros enable advanced compile-time code transformations, but they cannot
|
Macros enable advanced compile-time code transformations, but they cannot
|
||||||
change Nimrod's syntax. However, this is no real restriction because Nimrod's
|
change Nim's syntax. However, this is no real restriction because Nim's
|
||||||
syntax is flexible enough anyway. Macros have to be implemented in pure Nimrod
|
syntax is flexible enough anyway. Macros have to be implemented in pure Nim
|
||||||
code if `foreign function interface (FFI)
|
code if `foreign function interface (FFI)
|
||||||
<manual.html#foreign-function-interface>`_ is not enabled in the compiler, but
|
<manual.html#foreign-function-interface>`_ is not enabled in the compiler, but
|
||||||
other than that restriction (which at some point in the future will go away)
|
other than that restriction (which at some point in the future will go away)
|
||||||
you can write any kind of Nimrod code and the compiler will run it at compile
|
you can write any kind of Nim code and the compiler will run it at compile
|
||||||
time.
|
time.
|
||||||
|
|
||||||
There are two ways to write a macro, either *generating* Nimrod source code and
|
There are two ways to write a macro, either *generating* Nim source code and
|
||||||
letting the compiler parse it, or creating manually an abstract syntax tree
|
letting the compiler parse it, or creating manually an abstract syntax tree
|
||||||
(AST) which you feed to the compiler. In order to build the AST one needs to
|
(AST) which you feed to the compiler. In order to build the AST one needs to
|
||||||
know how the Nimrod concrete syntax is converted to an abstract syntax tree
|
know how the Nim concrete syntax is converted to an abstract syntax tree
|
||||||
(AST). The AST is documented in the `macros <macros.html>`_ module.
|
(AST). The AST is documented in the `macros <macros.html>`_ module.
|
||||||
|
|
||||||
Once your macro is finished, there are two ways to invoke it:
|
Once your macro is finished, there are two ways to invoke it:
|
||||||
|
|
@ -698,13 +677,13 @@ Expression Macros
|
||||||
The following example implements a powerful ``debug`` command that accepts a
|
The following example implements a powerful ``debug`` command that accepts a
|
||||||
variable number of arguments:
|
variable number of arguments:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
# to work with Nimrod syntax trees, we need an API that is defined in the
|
# to work with Nim syntax trees, we need an API that is defined in the
|
||||||
# ``macros`` module:
|
# ``macros`` module:
|
||||||
import macros
|
import macros
|
||||||
|
|
||||||
macro debug(n: varargs[expr]): stmt =
|
macro debug(n: varargs[expr]): stmt =
|
||||||
# `n` is a Nimrod AST that contains a list of expressions;
|
# `n` is a Nim AST that contains a list of expressions;
|
||||||
# this macro returns a list of statements:
|
# this macro returns a list of statements:
|
||||||
result = newNimNode(nnkStmtList, n)
|
result = newNimNode(nnkStmtList, n)
|
||||||
# iterate over any argument that is passed to this macro:
|
# iterate over any argument that is passed to this macro:
|
||||||
|
|
@ -727,7 +706,7 @@ variable number of arguments:
|
||||||
|
|
||||||
The macro call expands to:
|
The macro call expands to:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
write(stdout, "a[0]")
|
write(stdout, "a[0]")
|
||||||
write(stdout, ": ")
|
write(stdout, ": ")
|
||||||
writeln(stdout, a[0])
|
writeln(stdout, a[0])
|
||||||
|
|
@ -751,7 +730,7 @@ invoked by an expression following a colon.
|
||||||
The following example outlines a macro that generates a lexical analyzer from
|
The following example outlines a macro that generates a lexical analyzer from
|
||||||
regular expressions:
|
regular expressions:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
|
|
||||||
macro case_token(n: stmt): stmt =
|
macro case_token(n: stmt): stmt =
|
||||||
# creates a lexical analyzer from regular expressions
|
# creates a lexical analyzer from regular expressions
|
||||||
|
|
@ -784,7 +763,7 @@ To give a footstart to writing macros we will show now how to turn your typical
|
||||||
dynamic code into something that compiles statically. For the exercise we will
|
dynamic code into something that compiles statically. For the exercise we will
|
||||||
use the following snippet of code as the starting point:
|
use the following snippet of code as the starting point:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
|
|
||||||
import strutils, tables
|
import strutils, tables
|
||||||
|
|
||||||
|
|
@ -848,7 +827,7 @@ time string with the *generated source code*, which we then pass to the
|
||||||
``parseStmt`` proc from the `macros module <macros.html>`_. Here is the
|
``parseStmt`` proc from the `macros module <macros.html>`_. Here is the
|
||||||
modified source code implementing the macro:
|
modified source code implementing the macro:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
import macros, strutils
|
import macros, strutils
|
||||||
|
|
||||||
macro readCfgAndBuildSource(cfgFilename: string): stmt =
|
macro readCfgAndBuildSource(cfgFilename: string): stmt =
|
||||||
|
|
@ -893,13 +872,13 @@ this limitation by using the ``slurp`` proc from the `system module
|
||||||
``gorge`` which executes an external program and captures its output).
|
``gorge`` which executes an external program and captures its output).
|
||||||
|
|
||||||
The interesting thing is that our macro does not return a runtime ``TTable``
|
The interesting thing is that our macro does not return a runtime ``TTable``
|
||||||
object. Instead, it builds up Nimrod source code into the ``source`` variable.
|
object. Instead, it builds up Nim source code into the ``source`` variable.
|
||||||
For each line of the configuration file a ``const`` variable will be generated.
|
For each line of the configuration file a ``const`` variable will be generated.
|
||||||
To avoid conflicts we prefix these variables with ``cfg``. In essence, what the
|
To avoid conflicts we prefix these variables with ``cfg``. In essence, what the
|
||||||
compiler is doing is replacing the line calling the macro with the following
|
compiler is doing is replacing the line calling the macro with the following
|
||||||
snippet of code:
|
snippet of code:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
const cfgversion= "1.1"
|
const cfgversion= "1.1"
|
||||||
const cfglicenseOwner= "Hyori Lee"
|
const cfglicenseOwner= "Hyori Lee"
|
||||||
const cfglicenseKey= "M1Tl3PjBWO2CC48m"
|
const cfglicenseKey= "M1Tl3PjBWO2CC48m"
|
||||||
|
|
@ -919,14 +898,14 @@ Generating AST by hand
|
||||||
++++++++++++++++++++++
|
++++++++++++++++++++++
|
||||||
|
|
||||||
To generate an AST we would need to intimately know the structures used by the
|
To generate an AST we would need to intimately know the structures used by the
|
||||||
Nimrod compiler exposed in the `macros module <macros.html>`_, which at first
|
Nim compiler exposed in the `macros module <macros.html>`_, which at first
|
||||||
look seems a daunting task. But we can use as helper shortcut the ``dumpTree``
|
look seems a daunting task. But we can use as helper shortcut the ``dumpTree``
|
||||||
macro, which is used as a statement macro instead of an expression macro.
|
macro, which is used as a statement macro instead of an expression macro.
|
||||||
Since we know that we want to generate a bunch of ``const`` symbols we can
|
Since we know that we want to generate a bunch of ``const`` symbols we can
|
||||||
create the following source file and compile it to see what the compiler
|
create the following source file and compile it to see what the compiler
|
||||||
*expects* from us:
|
*expects* from us:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
import macros
|
import macros
|
||||||
|
|
||||||
dumpTree:
|
dumpTree:
|
||||||
|
|
@ -969,7 +948,7 @@ identifier, optionally a type (can be an *empty* node) and the value. Armed
|
||||||
with this knowledge, let's look at the finished version of the AST building
|
with this knowledge, let's look at the finished version of the AST building
|
||||||
macro:
|
macro:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nim
|
||||||
import macros, strutils
|
import macros, strutils
|
||||||
|
|
||||||
macro readCfgAndBuildAST(cfgFilename: string): stmt =
|
macro readCfgAndBuildAST(cfgFilename: string): stmt =
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ var links = 0 # count the number of links
|
||||||
var filename = addFileExt(ParamStr(1), "html")
|
var filename = addFileExt(ParamStr(1), "html")
|
||||||
var s = newFileStream(filename, fmRead)
|
var s = newFileStream(filename, fmRead)
|
||||||
if s == nil: quit("cannot open the file " & filename)
|
if s == nil: quit("cannot open the file " & filename)
|
||||||
var x: TXmlParser
|
var x: XmlParser
|
||||||
open(x, s, filename)
|
open(x, s, filename)
|
||||||
next(x) # get first event
|
next(x) # get first event
|
||||||
block mainLoop:
|
block mainLoop:
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ if paramCount() < 1:
|
||||||
var filename = addFileExt(paramStr(1), "html")
|
var filename = addFileExt(paramStr(1), "html")
|
||||||
var s = newFileStream(filename, fmRead)
|
var s = newFileStream(filename, fmRead)
|
||||||
if s == nil: quit("cannot open the file " & filename)
|
if s == nil: quit("cannot open the file " & filename)
|
||||||
var x: TXmlParser
|
var x: XmlParser
|
||||||
open(x, s, filename)
|
open(x, s, filename)
|
||||||
while true:
|
while true:
|
||||||
x.next()
|
x.next()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -21,6 +21,11 @@
|
||||||
## for i in 0 .. < 300:
|
## for i in 0 .. < 300:
|
||||||
## a.spawn(i, proc (x: int) {.thread.} = echo x)
|
## a.spawn(i, proc (x: int) {.thread.} = echo x)
|
||||||
## a.join()
|
## a.join()
|
||||||
|
##
|
||||||
|
## **Note**: This whole module is deprecated. Use `threadpool` and ``spawn``
|
||||||
|
## instead.
|
||||||
|
|
||||||
|
{.deprecated.}
|
||||||
|
|
||||||
from os import sleep
|
from os import sleep
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -10,9 +10,12 @@
|
||||||
## This module implements some common generic algorithms.
|
## This module implements some common generic algorithms.
|
||||||
|
|
||||||
type
|
type
|
||||||
TSortOrder* = enum ## sort order
|
SortOrder* = enum ## sort order
|
||||||
Descending, Ascending
|
Descending, Ascending
|
||||||
|
|
||||||
|
{.deprecated: [TSortOrder: SortOrder].}
|
||||||
|
|
||||||
|
|
||||||
proc `*`*(x: int, order: TSortOrder): int {.inline.} =
|
proc `*`*(x: int, order: TSortOrder): int {.inline.} =
|
||||||
## flips `x` if ``order == Descending``;
|
## flips `x` if ``order == Descending``;
|
||||||
## if ``order == Ascending`` then `x` is returned.
|
## if ``order == Ascending`` then `x` is returned.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2014 Dominik Picheta
|
# (c) Copyright 2014 Dominik Picheta
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -13,7 +13,7 @@ import os, oids, tables, strutils, macros, times
|
||||||
|
|
||||||
import rawsockets, net
|
import rawsockets, net
|
||||||
|
|
||||||
export TPort, TSocketFlags
|
export Port, SocketFlags
|
||||||
|
|
||||||
#{.injectStmt: newGcInvariant().}
|
#{.injectStmt: newGcInvariant().}
|
||||||
|
|
||||||
|
|
@ -36,21 +36,24 @@ export TPort, TSocketFlags
|
||||||
# -- Futures
|
# -- Futures
|
||||||
|
|
||||||
type
|
type
|
||||||
PFutureBase* = ref object of PObject
|
FutureBase* = ref object of RootObj
|
||||||
cb: proc () {.closure,gcsafe.}
|
cb: proc () {.closure,gcsafe.}
|
||||||
finished: bool
|
finished: bool
|
||||||
error*: ref EBase
|
error*: ref Exception
|
||||||
errorStackTrace*: string
|
errorStackTrace*: string
|
||||||
when not defined(release):
|
when not defined(release):
|
||||||
stackTrace: string ## For debugging purposes only.
|
stackTrace: string ## For debugging purposes only.
|
||||||
id: int
|
id: int
|
||||||
fromProc: string
|
fromProc: string
|
||||||
|
|
||||||
PFuture*[T] = ref object of PFutureBase
|
Future*[T] = ref object of FutureBase
|
||||||
value: T
|
value: T
|
||||||
|
|
||||||
|
{.deprecated: [PFutureBase: FutureBase, PFuture: Future].}
|
||||||
|
|
||||||
|
|
||||||
var currentID* = 0
|
var currentID* = 0
|
||||||
proc newFuture*[T](fromProc: string = "unspecified"): PFuture[T] =
|
proc newFuture*[T](fromProc: string = "unspecified"): Future[T] =
|
||||||
## Creates a new future.
|
## Creates a new future.
|
||||||
##
|
##
|
||||||
## Specifying ``fromProc``, which is a string specifying the name of the proc
|
## Specifying ``fromProc``, which is a string specifying the name of the proc
|
||||||
|
|
@ -63,7 +66,7 @@ proc newFuture*[T](fromProc: string = "unspecified"): PFuture[T] =
|
||||||
result.fromProc = fromProc
|
result.fromProc = fromProc
|
||||||
currentID.inc()
|
currentID.inc()
|
||||||
|
|
||||||
proc checkFinished[T](future: PFuture[T]) =
|
proc checkFinished[T](future: Future[T]) =
|
||||||
when not defined(release):
|
when not defined(release):
|
||||||
if future.finished:
|
if future.finished:
|
||||||
echo("<-----> ", future.id, " ", future.fromProc)
|
echo("<-----> ", future.id, " ", future.fromProc)
|
||||||
|
|
@ -76,7 +79,7 @@ proc checkFinished[T](future: PFuture[T]) =
|
||||||
echo getStackTrace()
|
echo getStackTrace()
|
||||||
assert false
|
assert false
|
||||||
|
|
||||||
proc complete*[T](future: PFuture[T], val: T) =
|
proc complete*[T](future: Future[T], val: T) =
|
||||||
## Completes ``future`` with value ``val``.
|
## Completes ``future`` with value ``val``.
|
||||||
#assert(not future.finished, "Future already finished, cannot finish twice.")
|
#assert(not future.finished, "Future already finished, cannot finish twice.")
|
||||||
checkFinished(future)
|
checkFinished(future)
|
||||||
|
|
@ -86,7 +89,7 @@ proc complete*[T](future: PFuture[T], val: T) =
|
||||||
if future.cb != nil:
|
if future.cb != nil:
|
||||||
future.cb()
|
future.cb()
|
||||||
|
|
||||||
proc complete*(future: PFuture[void]) =
|
proc complete*(future: Future[void]) =
|
||||||
## Completes a void ``future``.
|
## Completes a void ``future``.
|
||||||
#assert(not future.finished, "Future already finished, cannot finish twice.")
|
#assert(not future.finished, "Future already finished, cannot finish twice.")
|
||||||
checkFinished(future)
|
checkFinished(future)
|
||||||
|
|
@ -95,7 +98,7 @@ proc complete*(future: PFuture[void]) =
|
||||||
if future.cb != nil:
|
if future.cb != nil:
|
||||||
future.cb()
|
future.cb()
|
||||||
|
|
||||||
proc fail*[T](future: PFuture[T], error: ref EBase) =
|
proc fail*[T](future: Future[T], error: ref Exception) =
|
||||||
## Completes ``future`` with ``error``.
|
## Completes ``future`` with ``error``.
|
||||||
#assert(not future.finished, "Future already finished, cannot finish twice.")
|
#assert(not future.finished, "Future already finished, cannot finish twice.")
|
||||||
checkFinished(future)
|
checkFinished(future)
|
||||||
|
|
@ -111,8 +114,9 @@ proc fail*[T](future: PFuture[T], error: ref EBase) =
|
||||||
# TODO: This may turn out to be a bad idea.
|
# TODO: This may turn out to be a bad idea.
|
||||||
# Turns out this is a bad idea.
|
# Turns out this is a bad idea.
|
||||||
#raise error
|
#raise error
|
||||||
|
discard
|
||||||
|
|
||||||
proc `callback=`*(future: PFutureBase, cb: proc () {.closure,gcsafe.}) =
|
proc `callback=`*(future: FutureBase, cb: proc () {.closure,gcsafe.}) =
|
||||||
## Sets the callback proc to be called when the future completes.
|
## Sets the callback proc to be called when the future completes.
|
||||||
##
|
##
|
||||||
## If future has already completed then ``cb`` will be called immediately.
|
## If future has already completed then ``cb`` will be called immediately.
|
||||||
|
|
@ -123,23 +127,23 @@ proc `callback=`*(future: PFutureBase, cb: proc () {.closure,gcsafe.}) =
|
||||||
if future.finished:
|
if future.finished:
|
||||||
future.cb()
|
future.cb()
|
||||||
|
|
||||||
proc `callback=`*[T](future: PFuture[T],
|
proc `callback=`*[T](future: Future[T],
|
||||||
cb: proc (future: PFuture[T]) {.closure,gcsafe.}) =
|
cb: proc (future: Future[T]) {.closure,gcsafe.}) =
|
||||||
## Sets the callback proc to be called when the future completes.
|
## Sets the callback proc to be called when the future completes.
|
||||||
##
|
##
|
||||||
## If future has already completed then ``cb`` will be called immediately.
|
## If future has already completed then ``cb`` will be called immediately.
|
||||||
future.callback = proc () = cb(future)
|
future.callback = proc () = cb(future)
|
||||||
|
|
||||||
proc echoOriginalStackTrace[T](future: PFuture[T]) =
|
proc echoOriginalStackTrace[T](future: Future[T]) =
|
||||||
# TODO: Come up with something better.
|
# TODO: Come up with something better.
|
||||||
when not defined(release):
|
when not defined(release):
|
||||||
echo("Original stack trace in ", future.fromProc, ":")
|
echo("Original stack trace in ", future.fromProc, ":")
|
||||||
if not future.errorStackTrace.isNil() and future.errorStackTrace != "":
|
if not future.errorStackTrace.isNil and future.errorStackTrace != "":
|
||||||
echo(future.errorStackTrace)
|
echo(future.errorStackTrace)
|
||||||
else:
|
else:
|
||||||
echo("Empty or nil stack trace.")
|
echo("Empty or nil stack trace.")
|
||||||
|
|
||||||
proc read*[T](future: PFuture[T]): T =
|
proc read*[T](future: Future[T]): T =
|
||||||
## Retrieves the value of ``future``. Future must be finished otherwise
|
## Retrieves the value of ``future``. Future must be finished otherwise
|
||||||
## this function will fail with a ``EInvalidValue`` exception.
|
## this function will fail with a ``EInvalidValue`` exception.
|
||||||
##
|
##
|
||||||
|
|
@ -154,22 +158,22 @@ proc read*[T](future: PFuture[T]): T =
|
||||||
# TODO: Make a custom exception type for this?
|
# TODO: Make a custom exception type for this?
|
||||||
raise newException(EInvalidValue, "Future still in progress.")
|
raise newException(EInvalidValue, "Future still in progress.")
|
||||||
|
|
||||||
proc readError*[T](future: PFuture[T]): ref EBase =
|
proc readError*[T](future: Future[T]): ref EBase =
|
||||||
if future.error != nil: return future.error
|
if future.error != nil: return future.error
|
||||||
else:
|
else:
|
||||||
raise newException(EInvalidValue, "No error in future.")
|
raise newException(EInvalidValue, "No error in future.")
|
||||||
|
|
||||||
proc finished*[T](future: PFuture[T]): bool =
|
proc finished*[T](future: Future[T]): bool =
|
||||||
## Determines whether ``future`` has completed.
|
## Determines whether ``future`` has completed.
|
||||||
##
|
##
|
||||||
## ``True`` may indicate an error or a value. Use ``failed`` to distinguish.
|
## ``True`` may indicate an error or a value. Use ``failed`` to distinguish.
|
||||||
future.finished
|
future.finished
|
||||||
|
|
||||||
proc failed*(future: PFutureBase): bool =
|
proc failed*(future: FutureBase): bool =
|
||||||
## Determines whether ``future`` completed with an error.
|
## Determines whether ``future`` completed with an error.
|
||||||
future.error != nil
|
future.error != nil
|
||||||
|
|
||||||
proc asyncCheck*[T](future: PFuture[T]) =
|
proc asyncCheck*[T](future: Future[T]) =
|
||||||
## Sets a callback on ``future`` which raises an exception if the future
|
## Sets a callback on ``future`` which raises an exception if the future
|
||||||
## finished with an error.
|
## finished with an error.
|
||||||
##
|
##
|
||||||
|
|
@ -180,7 +184,7 @@ proc asyncCheck*[T](future: PFuture[T]) =
|
||||||
echoOriginalStackTrace(future)
|
echoOriginalStackTrace(future)
|
||||||
raise future.error
|
raise future.error
|
||||||
|
|
||||||
proc `and`*[T, Y](fut1: PFuture[T], fut2: PFuture[Y]): PFuture[void] =
|
proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
|
||||||
## Returns a future which will complete once both ``fut1`` and ``fut2``
|
## Returns a future which will complete once both ``fut1`` and ``fut2``
|
||||||
## complete.
|
## complete.
|
||||||
var retFuture = newFuture[void]()
|
var retFuture = newFuture[void]()
|
||||||
|
|
@ -192,7 +196,7 @@ proc `and`*[T, Y](fut1: PFuture[T], fut2: PFuture[Y]): PFuture[void] =
|
||||||
if fut1.finished: retFuture.complete()
|
if fut1.finished: retFuture.complete()
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
proc `or`*[T, Y](fut1: PFuture[T], fut2: PFuture[Y]): PFuture[void] =
|
proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
|
||||||
## Returns a future which will complete once either ``fut1`` or ``fut2``
|
## Returns a future which will complete once either ``fut1`` or ``fut2``
|
||||||
## complete.
|
## complete.
|
||||||
var retFuture = newFuture[void]()
|
var retFuture = newFuture[void]()
|
||||||
|
|
@ -203,7 +207,7 @@ proc `or`*[T, Y](fut1: PFuture[T], fut2: PFuture[Y]): PFuture[void] =
|
||||||
|
|
||||||
type
|
type
|
||||||
PDispatcherBase = ref object of PObject
|
PDispatcherBase = ref object of PObject
|
||||||
timers: seq[tuple[finishAt: float, fut: PFuture[void]]]
|
timers: seq[tuple[finishAt: float, fut: Future[void]]]
|
||||||
|
|
||||||
proc processTimers(p: PDispatcherBase) =
|
proc processTimers(p: PDispatcherBase) =
|
||||||
var oldTimers = p.timers
|
var oldTimers = p.timers
|
||||||
|
|
@ -375,10 +379,10 @@ when defined(windows) or defined(nimdoc):
|
||||||
RemoteSockaddr, RemoteSockaddrLength)
|
RemoteSockaddr, RemoteSockaddrLength)
|
||||||
|
|
||||||
proc connect*(socket: TAsyncFD, address: string, port: TPort,
|
proc connect*(socket: TAsyncFD, address: string, port: TPort,
|
||||||
af = AF_INET): PFuture[void] =
|
af = AF_INET): Future[void] =
|
||||||
## Connects ``socket`` to server at ``address:port``.
|
## Connects ``socket`` to server at ``address:port``.
|
||||||
##
|
##
|
||||||
## Returns a ``PFuture`` which will complete when the connection succeeds
|
## Returns a ``Future`` which will complete when the connection succeeds
|
||||||
## or an error occurs.
|
## or an error occurs.
|
||||||
verifyPresence(socket)
|
verifyPresence(socket)
|
||||||
var retFuture = newFuture[void]("connect")
|
var retFuture = newFuture[void]("connect")
|
||||||
|
|
@ -437,7 +441,7 @@ when defined(windows) or defined(nimdoc):
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
proc recv*(socket: TAsyncFD, size: int,
|
proc recv*(socket: TAsyncFD, size: int,
|
||||||
flags = {TSocketFlags.SafeDisconn}): PFuture[string] =
|
flags = {TSocketFlags.SafeDisconn}): Future[string] =
|
||||||
## Reads **up to** ``size`` bytes from ``socket``. Returned future will
|
## Reads **up to** ``size`` bytes from ``socket``. Returned future will
|
||||||
## complete once all the data requested is read, a part of the data has been
|
## complete once all the data requested is read, a part of the data has been
|
||||||
## read, or the socket has disconnected in which case the future will
|
## read, or the socket has disconnected in which case the future will
|
||||||
|
|
@ -525,7 +529,7 @@ when defined(windows) or defined(nimdoc):
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
proc send*(socket: TAsyncFD, data: string,
|
proc send*(socket: TAsyncFD, data: string,
|
||||||
flags = {TSocketFlags.SafeDisconn}): PFuture[void] =
|
flags = {TSocketFlags.SafeDisconn}): Future[void] =
|
||||||
## Sends ``data`` to ``socket``. The returned future will complete once all
|
## Sends ``data`` to ``socket``. The returned future will complete once all
|
||||||
## data has been sent.
|
## data has been sent.
|
||||||
verifyPresence(socket)
|
verifyPresence(socket)
|
||||||
|
|
@ -568,7 +572,7 @@ when defined(windows) or defined(nimdoc):
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
proc acceptAddr*(socket: TAsyncFD, flags = {TSocketFlags.SafeDisconn}):
|
proc acceptAddr*(socket: TAsyncFD, flags = {TSocketFlags.SafeDisconn}):
|
||||||
PFuture[tuple[address: string, client: TAsyncFD]] =
|
Future[tuple[address: string, client: TAsyncFD]] =
|
||||||
## Accepts a new connection. Returns a future containing the client socket
|
## Accepts a new connection. Returns a future containing the client socket
|
||||||
## corresponding to that connection and the remote address of the client.
|
## corresponding to that connection and the remote address of the client.
|
||||||
## The future will complete when the connection is successfully accepted.
|
## The future will complete when the connection is successfully accepted.
|
||||||
|
|
@ -789,7 +793,7 @@ else:
|
||||||
processTimers(p)
|
processTimers(p)
|
||||||
|
|
||||||
proc connect*(socket: TAsyncFD, address: string, port: TPort,
|
proc connect*(socket: TAsyncFD, address: string, port: TPort,
|
||||||
af = AF_INET): PFuture[void] =
|
af = AF_INET): Future[void] =
|
||||||
var retFuture = newFuture[void]("connect")
|
var retFuture = newFuture[void]("connect")
|
||||||
|
|
||||||
proc cb(sock: TAsyncFD): bool =
|
proc cb(sock: TAsyncFD): bool =
|
||||||
|
|
@ -824,7 +828,7 @@ else:
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
proc recv*(socket: TAsyncFD, size: int,
|
proc recv*(socket: TAsyncFD, size: int,
|
||||||
flags = {TSocketFlags.SafeDisconn}): PFuture[string] =
|
flags = {TSocketFlags.SafeDisconn}): Future[string] =
|
||||||
var retFuture = newFuture[string]("recv")
|
var retFuture = newFuture[string]("recv")
|
||||||
|
|
||||||
var readBuffer = newString(size)
|
var readBuffer = newString(size)
|
||||||
|
|
@ -855,7 +859,7 @@ else:
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
proc send*(socket: TAsyncFD, data: string,
|
proc send*(socket: TAsyncFD, data: string,
|
||||||
flags = {TSocketFlags.SafeDisconn}): PFuture[void] =
|
flags = {TSocketFlags.SafeDisconn}): Future[void] =
|
||||||
var retFuture = newFuture[void]("send")
|
var retFuture = newFuture[void]("send")
|
||||||
|
|
||||||
var written = 0
|
var written = 0
|
||||||
|
|
@ -887,7 +891,7 @@ else:
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
proc acceptAddr*(socket: TAsyncFD, flags = {TSocketFlags.SafeDisconn}):
|
proc acceptAddr*(socket: TAsyncFD, flags = {TSocketFlags.SafeDisconn}):
|
||||||
PFuture[tuple[address: string, client: TAsyncFD]] =
|
Future[tuple[address: string, client: TAsyncFD]] =
|
||||||
var retFuture = newFuture[tuple[address: string,
|
var retFuture = newFuture[tuple[address: string,
|
||||||
client: TAsyncFD]]("acceptAddr")
|
client: TAsyncFD]]("acceptAddr")
|
||||||
proc cb(sock: TAsyncFD): bool =
|
proc cb(sock: TAsyncFD): bool =
|
||||||
|
|
@ -912,7 +916,7 @@ else:
|
||||||
addRead(socket, cb)
|
addRead(socket, cb)
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
proc sleepAsync*(ms: int): PFuture[void] =
|
proc sleepAsync*(ms: int): Future[void] =
|
||||||
## Suspends the execution of the current async procedure for the next
|
## Suspends the execution of the current async procedure for the next
|
||||||
## ``ms`` miliseconds.
|
## ``ms`` miliseconds.
|
||||||
var retFuture = newFuture[void]("sleepAsync")
|
var retFuture = newFuture[void]("sleepAsync")
|
||||||
|
|
@ -921,14 +925,14 @@ proc sleepAsync*(ms: int): PFuture[void] =
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
proc accept*(socket: TAsyncFD,
|
proc accept*(socket: TAsyncFD,
|
||||||
flags = {TSocketFlags.SafeDisconn}): PFuture[TAsyncFD] =
|
flags = {TSocketFlags.SafeDisconn}): Future[TAsyncFD] =
|
||||||
## Accepts a new connection. Returns a future containing the client socket
|
## Accepts a new connection. Returns a future containing the client socket
|
||||||
## corresponding to that connection.
|
## corresponding to that connection.
|
||||||
## The future will complete when the connection is successfully accepted.
|
## The future will complete when the connection is successfully accepted.
|
||||||
var retFut = newFuture[TAsyncFD]("accept")
|
var retFut = newFuture[TAsyncFD]("accept")
|
||||||
var fut = acceptAddr(socket, flags)
|
var fut = acceptAddr(socket, flags)
|
||||||
fut.callback =
|
fut.callback =
|
||||||
proc (future: PFuture[tuple[address: string, client: TAsyncFD]]) =
|
proc (future: Future[tuple[address: string, client: TAsyncFD]]) =
|
||||||
assert future.finished
|
assert future.finished
|
||||||
if future.failed:
|
if future.failed:
|
||||||
retFut.fail(future.error)
|
retFut.fail(future.error)
|
||||||
|
|
@ -1111,12 +1115,12 @@ macro async*(prc: stmt): stmt {.immediate.} =
|
||||||
hint("Processing " & prc[0].getName & " as an async proc.")
|
hint("Processing " & prc[0].getName & " as an async proc.")
|
||||||
|
|
||||||
let returnType = prc[3][0]
|
let returnType = prc[3][0]
|
||||||
# Verify that the return type is a PFuture[T]
|
# Verify that the return type is a Future[T]
|
||||||
if returnType.kind == nnkIdent:
|
if returnType.kind == nnkIdent:
|
||||||
error("Expected return type of 'PFuture' got '" & $returnType & "'")
|
error("Expected return type of 'Future' got '" & $returnType & "'")
|
||||||
elif returnType.kind == nnkBracketExpr:
|
elif returnType.kind == nnkBracketExpr:
|
||||||
if $returnType[0] != "PFuture":
|
if $returnType[0] != "Future":
|
||||||
error("Expected return type of 'PFuture' got '" & $returnType[0] & "'")
|
error("Expected return type of 'Future' got '" & $returnType[0] & "'")
|
||||||
|
|
||||||
let subtypeIsVoid = returnType.kind == nnkEmpty or
|
let subtypeIsVoid = returnType.kind == nnkEmpty or
|
||||||
(returnType.kind == nnkBracketExpr and
|
(returnType.kind == nnkBracketExpr and
|
||||||
|
|
@ -1137,7 +1141,7 @@ macro async*(prc: stmt): stmt {.immediate.} =
|
||||||
subRetType),
|
subRetType),
|
||||||
newLit(prc[0].getName)))) # Get type from return type of this proc
|
newLit(prc[0].getName)))) # Get type from return type of this proc
|
||||||
|
|
||||||
# -> iterator nameIter(): PFutureBase {.closure.} =
|
# -> iterator nameIter(): FutureBase {.closure.} =
|
||||||
# -> var result: T
|
# -> var result: T
|
||||||
# -> <proc_body>
|
# -> <proc_body>
|
||||||
# -> complete(retFuture, result)
|
# -> complete(retFuture, result)
|
||||||
|
|
@ -1153,7 +1157,7 @@ macro async*(prc: stmt): stmt {.immediate.} =
|
||||||
# -> complete(retFuture)
|
# -> complete(retFuture)
|
||||||
procBody.add(newCall(newIdentNode("complete"), retFutureSym))
|
procBody.add(newCall(newIdentNode("complete"), retFutureSym))
|
||||||
|
|
||||||
var closureIterator = newProc(iteratorNameSym, [newIdentNode("PFutureBase")],
|
var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase")],
|
||||||
procBody, nnkIteratorDef)
|
procBody, nnkIteratorDef)
|
||||||
closureIterator[4] = newNimNode(nnkPragma, prc[6]).add(newIdentNode("closure"))
|
closureIterator[4] = newNimNode(nnkPragma, prc[6]).add(newIdentNode("closure"))
|
||||||
outerProcBody.add(closureIterator)
|
outerProcBody.add(closureIterator)
|
||||||
|
|
@ -1176,8 +1180,8 @@ macro async*(prc: stmt): stmt {.immediate.} =
|
||||||
if subtypeIsVoid:
|
if subtypeIsVoid:
|
||||||
# Add discardable pragma.
|
# Add discardable pragma.
|
||||||
if returnType.kind == nnkEmpty:
|
if returnType.kind == nnkEmpty:
|
||||||
# Add PFuture[void]
|
# Add Future[void]
|
||||||
result[3][0] = parseExpr("PFuture[void]")
|
result[3][0] = parseExpr("Future[void]")
|
||||||
|
|
||||||
result[6] = outerProcBody
|
result[6] = outerProcBody
|
||||||
|
|
||||||
|
|
@ -1185,7 +1189,7 @@ macro async*(prc: stmt): stmt {.immediate.} =
|
||||||
#if prc[0].getName == "processClient":
|
#if prc[0].getName == "processClient":
|
||||||
# echo(toStrLit(result))
|
# echo(toStrLit(result))
|
||||||
|
|
||||||
proc recvLine*(socket: TAsyncFD): PFuture[string] {.async.} =
|
proc recvLine*(socket: TAsyncFD): Future[string] {.async.} =
|
||||||
## Reads a line of data from ``socket``. Returned future will complete once
|
## Reads a line of data from ``socket``. Returned future will complete once
|
||||||
## a full line is read or an error occurs.
|
## a full line is read or an error occurs.
|
||||||
##
|
##
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2014 Dominik Picheta
|
# (c) Copyright 2014 Dominik Picheta
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
import strtabs, asyncnet, asyncdispatch, parseutils, uri, strutils
|
import strtabs, asyncnet, asyncdispatch, parseutils, uri, strutils
|
||||||
type
|
type
|
||||||
TRequest* = object
|
Request* = object
|
||||||
client*: PAsyncSocket # TODO: Separate this into a Response object?
|
client*: PAsyncSocket # TODO: Separate this into a Response object?
|
||||||
reqMethod*: string
|
reqMethod*: string
|
||||||
headers*: PStringTable
|
headers*: PStringTable
|
||||||
|
|
@ -22,10 +22,10 @@ type
|
||||||
hostname*: string ## The hostname of the client that made the request.
|
hostname*: string ## The hostname of the client that made the request.
|
||||||
body*: string
|
body*: string
|
||||||
|
|
||||||
PAsyncHttpServer* = ref object
|
AsyncHttpServer* = ref object
|
||||||
socket: PAsyncSocket
|
socket: PAsyncSocket
|
||||||
|
|
||||||
THttpCode* = enum
|
HttpCode* = enum
|
||||||
Http200 = "200 OK",
|
Http200 = "200 OK",
|
||||||
Http303 = "303 Moved",
|
Http303 = "303 Moved",
|
||||||
Http400 = "400 Bad Request",
|
Http400 = "400 Bad Request",
|
||||||
|
|
@ -33,10 +33,13 @@ type
|
||||||
Http500 = "500 Internal Server Error",
|
Http500 = "500 Internal Server Error",
|
||||||
Http502 = "502 Bad Gateway"
|
Http502 = "502 Bad Gateway"
|
||||||
|
|
||||||
THttpVersion* = enum
|
HttpVersion* = enum
|
||||||
HttpVer11,
|
HttpVer11,
|
||||||
HttpVer10
|
HttpVer10
|
||||||
|
|
||||||
|
{.deprecated: [TRequest: Request, PAsyncHttpServer: AsyncHttpServer,
|
||||||
|
THttpCode: HttpCode, THttpVersion: HttpVersion].}
|
||||||
|
|
||||||
proc `==`*(protocol: tuple[orig: string, major, minor: int],
|
proc `==`*(protocol: tuple[orig: string, major, minor: int],
|
||||||
ver: THttpVersion): bool =
|
ver: THttpVersion): bool =
|
||||||
let major =
|
let major =
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf, Dominik Picheta
|
# (c) Copyright 2012 Andreas Rumpf, Dominik Picheta
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
@ -10,8 +10,8 @@ include "system/inclrtl"
|
||||||
|
|
||||||
import sockets, os
|
import sockets, os
|
||||||
|
|
||||||
## This module implements an asynchronous event loop together with asynchronous sockets
|
## This module implements an asynchronous event loop together with asynchronous
|
||||||
## which use this event loop.
|
## sockets which use this event loop.
|
||||||
## It is akin to Python's asyncore module. Many modules that use sockets
|
## It is akin to Python's asyncore module. Many modules that use sockets
|
||||||
## have an implementation for this module, those modules should all have a
|
## have an implementation for this module, those modules should all have a
|
||||||
## ``register`` function which you should use to add the desired objects to a
|
## ``register`` function which you should use to add the desired objects to a
|
||||||
|
|
@ -31,10 +31,10 @@ import sockets, os
|
||||||
##
|
##
|
||||||
## Most (if not all) modules that use asyncio provide a userArg which is passed
|
## Most (if not all) modules that use asyncio provide a userArg which is passed
|
||||||
## on with the events. The type that you set userArg to must be inheriting from
|
## on with the events. The type that you set userArg to must be inheriting from
|
||||||
## TObject!
|
## ``RootObj``!
|
||||||
##
|
##
|
||||||
## **Note:** If you want to provide async ability to your module please do not
|
## **Note:** If you want to provide async ability to your module please do not
|
||||||
## use the ``TDelegate`` object, instead use ``PAsyncSocket``. It is possible
|
## use the ``Delegate`` object, instead use ``AsyncSocket``. It is possible
|
||||||
## that in the future this type's fields will not be exported therefore breaking
|
## that in the future this type's fields will not be exported therefore breaking
|
||||||
## your code.
|
## your code.
|
||||||
##
|
##
|
||||||
|
|
@ -44,10 +44,10 @@ import sockets, os
|
||||||
## Asynchronous sockets
|
## Asynchronous sockets
|
||||||
## ====================
|
## ====================
|
||||||
##
|
##
|
||||||
## For most purposes you do not need to worry about the ``TDelegate`` type. The
|
## For most purposes you do not need to worry about the ``Delegate`` type. The
|
||||||
## ``PAsyncSocket`` is what you are after. It's a reference to the ``TAsyncSocket``
|
## ``AsyncSocket`` is what you are after. It's a reference to
|
||||||
## object. This object defines events which you should overwrite by your own
|
## the ``AsyncSocketObj`` object. This object defines events which you should
|
||||||
## procedures.
|
## overwrite by your own procedures.
|
||||||
##
|
##
|
||||||
## For server sockets the only event you need to worry about is the ``handleAccept``
|
## For server sockets the only event you need to worry about is the ``handleAccept``
|
||||||
## event, in your handleAccept proc you should call ``accept`` on the server
|
## event, in your handleAccept proc you should call ``accept`` on the server
|
||||||
|
|
@ -57,13 +57,13 @@ import sockets, os
|
||||||
##
|
##
|
||||||
## An example ``handleAccept`` follows:
|
## An example ``handleAccept`` follows:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
##
|
##
|
||||||
## var disp: PDispatcher = newDispatcher()
|
## var disp = newDispatcher()
|
||||||
## ...
|
## ...
|
||||||
## proc handleAccept(s: PAsyncSocket) =
|
## proc handleAccept(s: AsyncSocket) =
|
||||||
## echo("Accepted client.")
|
## echo("Accepted client.")
|
||||||
## var client: PAsyncSocket
|
## var client: AsyncSocket
|
||||||
## new(client)
|
## new(client)
|
||||||
## s.accept(client)
|
## s.accept(client)
|
||||||
## client.handleRead = ...
|
## client.handleRead = ...
|
||||||
|
|
@ -76,29 +76,29 @@ import sockets, os
|
||||||
## the socket has established a connection to a server socket; from that point
|
## the socket has established a connection to a server socket; from that point
|
||||||
## it can be safely written to.
|
## it can be safely written to.
|
||||||
##
|
##
|
||||||
## Getting a blocking client from a PAsyncSocket
|
## Getting a blocking client from an AsyncSocket
|
||||||
## =============================================
|
## =============================================
|
||||||
##
|
##
|
||||||
## If you need a asynchronous server socket but you wish to process the clients
|
## If you need a asynchronous server socket but you wish to process the clients
|
||||||
## synchronously then you can use the ``getSocket`` converter to get a TSocket
|
## synchronously then you can use the ``getSocket`` converter to get
|
||||||
## object from the PAsyncSocket object, this can then be combined with ``accept``
|
## a ``Socket`` from the ``AsyncSocket`` object, this can then be combined
|
||||||
## like so:
|
## with ``accept`` like so:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
##
|
##
|
||||||
## proc handleAccept(s: PAsyncSocket) =
|
## proc handleAccept(s: AsyncSocket) =
|
||||||
## var client: TSocket
|
## var client: Socket
|
||||||
## getSocket(s).accept(client)
|
## getSocket(s).accept(client)
|
||||||
|
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
from winlean import TTimeVal, TSocketHandle, TFdSet, FD_ZERO, FD_SET, FD_ISSET, select
|
from winlean import TimeVal, SocketHandle, FdSet, FD_ZERO, FD_SET, FD_ISSET, select
|
||||||
else:
|
else:
|
||||||
from posix import TTimeVal, TSocketHandle, TFdSet, FD_ZERO, FD_SET, FD_ISSET, select
|
from posix import TimeVal, SocketHandle, FdSet, FD_ZERO, FD_SET, FD_ISSET, select
|
||||||
|
|
||||||
type
|
type
|
||||||
TDelegate* = object
|
DelegateObj* = object
|
||||||
fd*: TSocketHandle
|
fd*: SocketHandle
|
||||||
deleVal*: PObject
|
deleVal*: RootRef
|
||||||
|
|
||||||
handleRead*: proc (h: PObject) {.nimcall, gcsafe.}
|
handleRead*: proc (h: PObject) {.nimcall, gcsafe.}
|
||||||
handleWrite*: proc (h: PObject) {.nimcall, gcsafe.}
|
handleWrite*: proc (h: PObject) {.nimcall, gcsafe.}
|
||||||
|
|
@ -107,18 +107,18 @@ type
|
||||||
|
|
||||||
open*: bool
|
open*: bool
|
||||||
task*: proc (h: PObject) {.nimcall, gcsafe.}
|
task*: proc (h: PObject) {.nimcall, gcsafe.}
|
||||||
mode*: TFileMode
|
mode*: FileMode
|
||||||
|
|
||||||
PDelegate* = ref TDelegate
|
Delegate* = ref DelegateObj
|
||||||
|
|
||||||
PDispatcher* = ref TDispatcher
|
Dispatcher* = ref DispatcherObj
|
||||||
TDispatcher = object
|
DispatcherObj = object
|
||||||
delegates: seq[PDelegate]
|
delegates: seq[PDelegate]
|
||||||
|
|
||||||
PAsyncSocket* = ref TAsyncSocket
|
AsyncSocket* = ref AsyncSocketObj
|
||||||
TAsyncSocket* = object of TObject
|
AsyncSocketObj* = object of RootObj
|
||||||
socket: TSocket
|
socket: Socket
|
||||||
info: TInfo
|
info: SocketStatus
|
||||||
|
|
||||||
handleRead*: proc (s: PAsyncSocket) {.closure, gcsafe.}
|
handleRead*: proc (s: PAsyncSocket) {.closure, gcsafe.}
|
||||||
handleWrite: proc (s: PAsyncSocket) {.closure, gcsafe.}
|
handleWrite: proc (s: PAsyncSocket) {.closure, gcsafe.}
|
||||||
|
|
@ -134,10 +134,17 @@ type
|
||||||
proto: TProtocol
|
proto: TProtocol
|
||||||
deleg: PDelegate
|
deleg: PDelegate
|
||||||
|
|
||||||
TInfo* = enum
|
SocketStatus* = enum
|
||||||
SockIdle, SockConnecting, SockConnected, SockListening, SockClosed,
|
SockIdle, SockConnecting, SockConnected, SockListening, SockClosed,
|
||||||
SockUDPBound
|
SockUDPBound
|
||||||
|
|
||||||
|
{.deprecated: [TDelegate: DelegateObj, PDelegate: Delegate,
|
||||||
|
TProcessOption: ProcessOption,
|
||||||
|
TInfo: SocketStatus, PAsyncSocket: AsyncSocket, TAsyncSocket: AsyncSocketObj,
|
||||||
|
TDispatcher: DispatcherObj, PDispatcher: Dispatcher,
|
||||||
|
].}
|
||||||
|
|
||||||
|
|
||||||
proc newDelegate*(): PDelegate =
|
proc newDelegate*(): PDelegate =
|
||||||
## Creates a new delegate.
|
## Creates a new delegate.
|
||||||
new(result)
|
new(result)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2014 Dominik Picheta
|
# (c) Copyright 2014 Dominik Picheta
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -19,9 +19,9 @@
|
||||||
##
|
##
|
||||||
## import asyncnet, asyncdispatch
|
## import asyncnet, asyncdispatch
|
||||||
##
|
##
|
||||||
## var clients: seq[PAsyncSocket] = @[]
|
## var clients: seq[AsyncSocket] = @[]
|
||||||
##
|
##
|
||||||
## proc processClient(client: PAsyncSocket) {.async.} =
|
## proc processClient(client: AsyncSocket) {.async.} =
|
||||||
## while true:
|
## while true:
|
||||||
## let line = await client.recvLine()
|
## let line = await client.recvLine()
|
||||||
## for c in clients:
|
## for c in clients:
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
##
|
##
|
||||||
## proc serve() {.async.} =
|
## proc serve() {.async.} =
|
||||||
## var server = newAsyncSocket()
|
## var server = newAsyncSocket()
|
||||||
## server.bindAddr(TPort(12345))
|
## server.bindAddr(Port(12345))
|
||||||
## server.listen()
|
## server.listen()
|
||||||
##
|
##
|
||||||
## while true:
|
## while true:
|
||||||
|
|
@ -54,8 +54,10 @@ when defined(ssl):
|
||||||
type
|
type
|
||||||
# TODO: I would prefer to just do:
|
# TODO: I would prefer to just do:
|
||||||
# PAsyncSocket* {.borrow: `.`.} = distinct PSocket. But that doesn't work.
|
# PAsyncSocket* {.borrow: `.`.} = distinct PSocket. But that doesn't work.
|
||||||
TAsyncSocket {.borrow: `.`.} = distinct TSocketImpl
|
AsyncSocketDesc {.borrow: `.`.} = distinct TSocketImpl
|
||||||
PAsyncSocket* = ref TAsyncSocket
|
AsyncSocket* = ref AsyncSocketDesc
|
||||||
|
|
||||||
|
{.deprecated: [PAsyncSocket: AsyncSocket].}
|
||||||
|
|
||||||
# TODO: Save AF, domain etc info and reuse it in procs which need it like connect.
|
# TODO: Save AF, domain etc info and reuse it in procs which need it like connect.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2010 Andreas Rumpf
|
# (c) Copyright 2010 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2013 Robert Persson
|
# (c) Copyright 2013 Robert Persson
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2013 Robert Persson
|
# (c) Copyright 2013 Robert Persson
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -44,5 +44,5 @@ proc openDefaultBrowser*(url: string) =
|
||||||
# we use ``startProcess`` here because we don't want to block!
|
# we use ``startProcess`` here because we don't want to block!
|
||||||
discard startProcess(command=b, args=[url], options={poUseShell})
|
discard startProcess(command=b, args=[url], options={poUseShell})
|
||||||
return
|
return
|
||||||
except EOS:
|
except OSError:
|
||||||
discard
|
discard
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
## This module implements helper procs for CGI applications. Example:
|
## This module implements helper procs for CGI applications. Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: Nimrod
|
## .. code-block:: Nim
|
||||||
##
|
##
|
||||||
## import strtabs, cgi
|
## import strtabs, cgi
|
||||||
##
|
##
|
||||||
|
|
@ -93,12 +93,14 @@ proc XMLencode*(s: string): string =
|
||||||
for i in 0..len(s)-1: addXmlChar(result, s[i])
|
for i in 0..len(s)-1: addXmlChar(result, s[i])
|
||||||
|
|
||||||
type
|
type
|
||||||
ECgi* = object of IOError ## the exception that is raised, if a CGI error occurs
|
CgiError* = object of IOError ## exception that is raised if a CGI error occurs
|
||||||
TRequestMethod* = enum ## the used request method
|
RequestMethod* = enum ## the used request method
|
||||||
methodNone, ## no REQUEST_METHOD environment variable
|
methodNone, ## no REQUEST_METHOD environment variable
|
||||||
methodPost, ## query uses the POST method
|
methodPost, ## query uses the POST method
|
||||||
methodGet ## query uses the GET method
|
methodGet ## query uses the GET method
|
||||||
|
|
||||||
|
{.deprecated: [TRequestMethod: RequestMethod, ECgi: CgiError].}
|
||||||
|
|
||||||
proc cgiError*(msg: string) {.noreturn.} =
|
proc cgiError*(msg: string) {.noreturn.} =
|
||||||
## raises an ECgi exception with message `msg`.
|
## raises an ECgi exception with message `msg`.
|
||||||
var e: ref ECgi
|
var e: ref ECgi
|
||||||
|
|
@ -377,7 +379,7 @@ proc setCookie*(name, value: string) =
|
||||||
write(stdout, "Set-Cookie: ", name, "=", value, "\n")
|
write(stdout, "Set-Cookie: ", name, "=", value, "\n")
|
||||||
|
|
||||||
var
|
var
|
||||||
gcookies {.threadvar.}: PStringTable
|
gcookies {.threadvar.}: StringTableRef
|
||||||
|
|
||||||
proc getCookie*(name: string): TaintedString =
|
proc getCookie*(name: string): TaintedString =
|
||||||
## Gets a cookie. If no cookie of `name` exists, "" is returned.
|
## Gets a cookie. If no cookie of `name` exists, "" is returned.
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,40 @@
|
||||||
#nimrod c -t:-march=i686 --cpu:amd64 --threads:on -d:release lockfreehash.nim
|
#nimrod c -t:-march=i686 --cpu:amd64 --threads:on -d:release lockfreehash.nim
|
||||||
|
|
||||||
import baseutils, unsigned, math, hashes
|
import unsigned, math, hashes
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
## Memory Utility Functions
|
||||||
|
|
||||||
|
proc newHeap*[T](): ptr T =
|
||||||
|
result = cast[ptr T](alloc0(sizeof(T)))
|
||||||
|
|
||||||
|
proc copyNew*[T](x: var T): ptr T =
|
||||||
|
var
|
||||||
|
size = sizeof(T)
|
||||||
|
mem = alloc(size)
|
||||||
|
copyMem(mem, x.addr, size)
|
||||||
|
return cast[ptr T](mem)
|
||||||
|
|
||||||
|
proc copyTo*[T](val: var T, dest: int) =
|
||||||
|
copyMem(pointer(dest), val.addr, sizeof(T))
|
||||||
|
|
||||||
|
proc allocType*[T](): pointer = alloc(sizeof(T))
|
||||||
|
|
||||||
|
proc newShared*[T](): ptr T =
|
||||||
|
result = cast[ptr T](allocShared0(sizeof(T)))
|
||||||
|
|
||||||
|
proc copyShared*[T](x: var T): ptr T =
|
||||||
|
var
|
||||||
|
size = sizeof(T)
|
||||||
|
mem = allocShared(size)
|
||||||
|
copyMem(mem, x.addr, size)
|
||||||
|
return cast[ptr T](mem)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
## Pointer arithmetic
|
||||||
|
|
||||||
|
proc `+`*(p: pointer, i: int): pointer {.inline.} =
|
||||||
|
cast[pointer](cast[int](p) + i)
|
||||||
|
|
||||||
const
|
const
|
||||||
minTableSize = 8
|
minTableSize = 8
|
||||||
|
|
@ -194,7 +226,7 @@ proc copySlot[K,V](idx: int, oldTbl: var PConcTable[K,V], newTbl: var PConcTable
|
||||||
#Prevent new values from appearing in the old table by priming
|
#Prevent new values from appearing in the old table by priming
|
||||||
oldVal = atomic_load_n(oldTbl[idx].value.addr, ATOMIC_RELAXED)
|
oldVal = atomic_load_n(oldTbl[idx].value.addr, ATOMIC_RELAXED)
|
||||||
while not isPrime(oldVal):
|
while not isPrime(oldVal):
|
||||||
var box = if oldVal == NULL or isTomb(oldVal) : oldVal.setTomb.setPrime
|
var box = if oldVal == 0 or isTomb(oldVal) : oldVal.setTomb.setPrime
|
||||||
else: oldVal.setPrime
|
else: oldVal.setPrime
|
||||||
if atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr,
|
if atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr,
|
||||||
box, false, ATOMIC_RELAXED, ATOMIC_RELAXED):
|
box, false, ATOMIC_RELAXED, ATOMIC_RELAXED):
|
||||||
|
|
@ -209,8 +241,8 @@ proc copySlot[K,V](idx: int, oldTbl: var PConcTable[K,V], newTbl: var PConcTable
|
||||||
return false
|
return false
|
||||||
if isTomb(oldVal):
|
if isTomb(oldVal):
|
||||||
echo("oldVal is Tomb!!!, should not happen")
|
echo("oldVal is Tomb!!!, should not happen")
|
||||||
if pop(oldVal) != NULL:
|
if pop(oldVal) != 0:
|
||||||
result = setVal(newTbl, pop(oldKey), pop(oldVal), NULL, true) == NULL
|
result = setVal(newTbl, pop(oldKey), pop(oldVal), 0, true) == 0
|
||||||
if result:
|
if result:
|
||||||
#echo("Copied a Slot! idx= " & $idx & " key= " & $oldKey & " val= " & $oldVal)
|
#echo("Copied a Slot! idx= " & $idx & " key= " & $oldKey & " val= " & $oldVal)
|
||||||
else:
|
else:
|
||||||
|
|
@ -323,7 +355,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||||
idx = idx and (table.len - 1)
|
idx = idx and (table.len - 1)
|
||||||
#echo("try set idx = " & $idx & "for" & $key)
|
#echo("try set idx = " & $idx & "for" & $key)
|
||||||
var
|
var
|
||||||
probedKey = NULL
|
probedKey = 0
|
||||||
openKey = atomic_compare_exchange_n(table[idx].key.addr, probedKey.addr,
|
openKey = atomic_compare_exchange_n(table[idx].key.addr, probedKey.addr,
|
||||||
key, false, ATOMIC_RELAXED, ATOMIC_RELAXED)
|
key, false, ATOMIC_RELAXED, ATOMIC_RELAXED)
|
||||||
if openKey:
|
if openKey:
|
||||||
|
|
@ -339,7 +371,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||||
if keyEQ[K](probedKey, key):
|
if keyEQ[K](probedKey, key):
|
||||||
#echo("we found the matching slot")
|
#echo("we found the matching slot")
|
||||||
break # We found a matching slot
|
break # We found a matching slot
|
||||||
if (not(expVal != NULL and match)) and (probes >= reProbeLimit or key.isTomb):
|
if (not(expVal != 0 and match)) and (probes >= reProbeLimit or key.isTomb):
|
||||||
if key.isTomb: echo("Key is Tombstone")
|
if key.isTomb: echo("Key is Tombstone")
|
||||||
#if probes >= reProbeLimit: echo("Too much probing " & $probes)
|
#if probes >= reProbeLimit: echo("Too much probing " & $probes)
|
||||||
#echo("try to resize")
|
#echo("try to resize")
|
||||||
|
|
@ -361,7 +393,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||||
return oldVal
|
return oldVal
|
||||||
nextTable = atomic_load_n(table.next.addr, ATOMIC_SEQ_CST)
|
nextTable = atomic_load_n(table.next.addr, ATOMIC_SEQ_CST)
|
||||||
if nextTable == nil and
|
if nextTable == nil and
|
||||||
((oldVal == NULL and
|
((oldVal == 0 and
|
||||||
(probes >= reProbeLimit or table.used / table.len > 0.8)) or
|
(probes >= reProbeLimit or table.used / table.len > 0.8)) or
|
||||||
(isPrime(oldVal))):
|
(isPrime(oldVal))):
|
||||||
if table.used / table.len > 0.8: echo("resize because usage ratio = " &
|
if table.used / table.len > 0.8: echo("resize because usage ratio = " &
|
||||||
|
|
@ -380,12 +412,12 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||||
if atomic_compare_exchange_n(table[idx].value.addr, oldVal.addr,
|
if atomic_compare_exchange_n(table[idx].value.addr, oldVal.addr,
|
||||||
val, false, ATOMIC_RELEASE, ATOMIC_RELAXED):
|
val, false, ATOMIC_RELEASE, ATOMIC_RELAXED):
|
||||||
#echo("val set at table " & $cast[int](table))
|
#echo("val set at table " & $cast[int](table))
|
||||||
if expVal != NULL:
|
if expVal != 0:
|
||||||
if (oldVal == NULL or isTomb(oldVal)) and not isTomb(val):
|
if (oldVal == 0 or isTomb(oldVal)) and not isTomb(val):
|
||||||
discard atomic_add_fetch(table.active.addr, 1, ATOMIC_RELAXED)
|
discard atomic_add_fetch(table.active.addr, 1, ATOMIC_RELAXED)
|
||||||
elif not (oldVal == NULL or isTomb(oldVal)) and isTomb(val):
|
elif not (oldVal == 0 or isTomb(oldVal)) and isTomb(val):
|
||||||
discard atomic_add_fetch(table.active.addr, -1, ATOMIC_RELAXED)
|
discard atomic_add_fetch(table.active.addr, -1, ATOMIC_RELAXED)
|
||||||
if oldVal == NULL and expVal != NULL:
|
if oldVal == 0 and expVal != 0:
|
||||||
return setTomb(oldVal)
|
return setTomb(oldVal)
|
||||||
else: return oldVal
|
else: return oldVal
|
||||||
if isPrime(oldVal):
|
if isPrime(oldVal):
|
||||||
|
|
@ -415,7 +447,7 @@ proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
|
||||||
if not isPrime(val):
|
if not isPrime(val):
|
||||||
if isTomb(val):
|
if isTomb(val):
|
||||||
#echo("val was tomb but not prime")
|
#echo("val was tomb but not prime")
|
||||||
return NULL
|
return 0
|
||||||
else:
|
else:
|
||||||
#echo("-GotIt- idx = ", idx, " key = ", key, " val ", val )
|
#echo("-GotIt- idx = ", idx, " key = ", key, " val ", val )
|
||||||
return val
|
return val
|
||||||
|
|
@ -427,7 +459,7 @@ proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
|
||||||
if probes >= reProbeLimit*4 or key.isTomb:
|
if probes >= reProbeLimit*4 or key.isTomb:
|
||||||
if newTable == nil:
|
if newTable == nil:
|
||||||
#echo("too many probes and no new table ", key, " ", idx )
|
#echo("too many probes and no new table ", key, " ", idx )
|
||||||
return NULL
|
return 0
|
||||||
else:
|
else:
|
||||||
newTable = helpCopy(table)
|
newTable = helpCopy(table)
|
||||||
return getVal(newTable, key)
|
return getVal(newTable, key)
|
||||||
|
|
@ -437,10 +469,10 @@ proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
#proc set*(table: var PConcTable[TRaw,TRaw], key: TRaw, val: TRaw) =
|
#proc set*(table: var PConcTable[TRaw,TRaw], key: TRaw, val: TRaw) =
|
||||||
# discard setVal(table, pack(key), pack(key), NULL, false)
|
# discard setVal(table, pack(key), pack(key), 0, false)
|
||||||
|
|
||||||
#proc set*[V](table: var PConcTable[TRaw,V], key: TRaw, val: ptr V) =
|
#proc set*[V](table: var PConcTable[TRaw,V], key: TRaw, val: ptr V) =
|
||||||
# discard setVal(table, pack(key), cast[int](val), NULL, false)
|
# discard setVal(table, pack(key), cast[int](val), 0, false)
|
||||||
|
|
||||||
proc set*[K,V](table: var PConcTable[K,V], key: var K, val: var V) =
|
proc set*[K,V](table: var PConcTable[K,V], key: var K, val: var V) =
|
||||||
when not (K is TRaw):
|
when not (K is TRaw):
|
||||||
|
|
@ -451,10 +483,10 @@ proc set*[K,V](table: var PConcTable[K,V], key: var K, val: var V) =
|
||||||
var newVal = cast[int](copyShared(val))
|
var newVal = cast[int](copyShared(val))
|
||||||
else:
|
else:
|
||||||
var newVal = pack(val)
|
var newVal = pack(val)
|
||||||
var oldPtr = pop(setVal(table, newKey, newVal, NULL, false))
|
var oldPtr = pop(setVal(table, newKey, newVal, 0, false))
|
||||||
#echo("oldPtr = ", cast[int](oldPtr), " newPtr = ", cast[int](newPtr))
|
#echo("oldPtr = ", cast[int](oldPtr), " newPtr = ", cast[int](newPtr))
|
||||||
when not (V is TRaw):
|
when not (V is TRaw):
|
||||||
if newVal != oldPtr and oldPtr != NULL:
|
if newVal != oldPtr and oldPtr != 0:
|
||||||
deallocShared(cast[ptr V](oldPtr))
|
deallocShared(cast[ptr V](oldPtr))
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -573,10 +605,3 @@ when isMainModule:
|
||||||
# echo(i, " = ", hashInt(i) and 8191)
|
# echo(i, " = ", hashInt(i) and 8191)
|
||||||
|
|
||||||
deleteConcTable(table)
|
deleteConcTable(table)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -12,30 +12,32 @@
|
||||||
## by Adam Langley.
|
## by Adam Langley.
|
||||||
|
|
||||||
type
|
type
|
||||||
TNode[T] = object {.pure, final, acyclic.}
|
NodeObj[T] = object {.pure, final, acyclic.}
|
||||||
byte: int ## byte index of the difference
|
byte: int ## byte index of the difference
|
||||||
otherbits: char
|
otherbits: char
|
||||||
case isLeaf: bool
|
case isLeaf: bool
|
||||||
of false: child: array[0..1, ref TNode[T]]
|
of false: child: array[0..1, ref NodeObj[T]]
|
||||||
of true:
|
of true:
|
||||||
key: string
|
key: string
|
||||||
when T isnot void:
|
when T isnot void:
|
||||||
val: T
|
val: T
|
||||||
|
|
||||||
PNode[T] = ref TNode[T]
|
Node[T] = ref NodeObj[T]
|
||||||
TCritBitTree*[T] = object {.
|
CritBitTree*[T] = object {.
|
||||||
pure, final.} ## The crit bit tree can either be used
|
pure, final.} ## The crit bit tree can either be used
|
||||||
## as a mapping from strings to
|
## as a mapping from strings to
|
||||||
## some type ``T`` or as a set of
|
## some type ``T`` or as a set of
|
||||||
## strings if ``T`` is void.
|
## strings if ``T`` is void.
|
||||||
root: PNode[T]
|
root: Node[T]
|
||||||
count: int
|
count: int
|
||||||
|
|
||||||
proc len*[T](c: TCritBitTree[T]): int =
|
{.deprecated: [TCritBitTree: CritBitTree].}
|
||||||
|
|
||||||
|
proc len*[T](c: CritBitTree[T]): int =
|
||||||
## returns the number of elements in `c` in O(1).
|
## returns the number of elements in `c` in O(1).
|
||||||
result = c.count
|
result = c.count
|
||||||
|
|
||||||
proc rawGet[T](c: TCritBitTree[T], key: string): PNode[T] =
|
proc rawGet[T](c: CritBitTree[T], key: string): Node[T] =
|
||||||
var it = c.root
|
var it = c.root
|
||||||
while it != nil:
|
while it != nil:
|
||||||
if not it.isLeaf:
|
if not it.isLeaf:
|
||||||
|
|
@ -45,15 +47,15 @@ proc rawGet[T](c: TCritBitTree[T], key: string): PNode[T] =
|
||||||
else:
|
else:
|
||||||
return if it.key == key: it else: nil
|
return if it.key == key: it else: nil
|
||||||
|
|
||||||
proc contains*[T](c: TCritBitTree[T], key: string): bool {.inline.} =
|
proc contains*[T](c: CritBitTree[T], key: string): bool {.inline.} =
|
||||||
## returns true iff `c` contains the given `key`.
|
## returns true iff `c` contains the given `key`.
|
||||||
result = rawGet(c, key) != nil
|
result = rawGet(c, key) != nil
|
||||||
|
|
||||||
proc hasKey*[T](c: TCritBitTree[T], key: string): bool {.inline.} =
|
proc hasKey*[T](c: CritBitTree[T], key: string): bool {.inline.} =
|
||||||
## alias for `contains`.
|
## alias for `contains`.
|
||||||
result = rawGet(c, key) != nil
|
result = rawGet(c, key) != nil
|
||||||
|
|
||||||
proc rawInsert[T](c: var TCritBitTree[T], key: string): PNode[T] =
|
proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] =
|
||||||
if c.root == nil:
|
if c.root == nil:
|
||||||
new c.root
|
new c.root
|
||||||
c.root.isleaf = true
|
c.root.isleaf = true
|
||||||
|
|
@ -84,7 +86,7 @@ proc rawInsert[T](c: var TCritBitTree[T], key: string): PNode[T] =
|
||||||
let ch = it.key[newByte]
|
let ch = it.key[newByte]
|
||||||
let dir = (1 + (ord(ch) or newOtherBits)) shr 8
|
let dir = (1 + (ord(ch) or newOtherBits)) shr 8
|
||||||
|
|
||||||
var inner: PNode[T]
|
var inner: Node[T]
|
||||||
new inner
|
new inner
|
||||||
new result
|
new result
|
||||||
result.isLeaf = true
|
result.isLeaf = true
|
||||||
|
|
@ -106,7 +108,7 @@ proc rawInsert[T](c: var TCritBitTree[T], key: string): PNode[T] =
|
||||||
wherep[] = inner
|
wherep[] = inner
|
||||||
inc c.count
|
inc c.count
|
||||||
|
|
||||||
proc containsOrIncl*[T](c: var TCritBitTree[T], key: string, val: T): bool =
|
proc containsOrIncl*[T](c: var CritBitTree[T], key: string, val: T): bool =
|
||||||
## returns true iff `c` contains the given `key`. If the key does not exist
|
## returns true iff `c` contains the given `key`. If the key does not exist
|
||||||
## ``c[key] = val`` is performed.
|
## ``c[key] = val`` is performed.
|
||||||
let oldCount = c.count
|
let oldCount = c.count
|
||||||
|
|
@ -115,23 +117,23 @@ proc containsOrIncl*[T](c: var TCritBitTree[T], key: string, val: T): bool =
|
||||||
when T isnot void:
|
when T isnot void:
|
||||||
if not result: n.val = val
|
if not result: n.val = val
|
||||||
|
|
||||||
proc containsOrIncl*(c: var TCritBitTree[void], key: string): bool =
|
proc containsOrIncl*(c: var CritBitTree[void], key: string): bool =
|
||||||
## returns true iff `c` contains the given `key`. If the key does not exist
|
## returns true iff `c` contains the given `key`. If the key does not exist
|
||||||
## it is inserted into `c`.
|
## it is inserted into `c`.
|
||||||
let oldCount = c.count
|
let oldCount = c.count
|
||||||
var n = rawInsert(c, key)
|
var n = rawInsert(c, key)
|
||||||
result = c.count == oldCount
|
result = c.count == oldCount
|
||||||
|
|
||||||
proc incl*(c: var TCritBitTree[void], key: string) =
|
proc incl*(c: var CritBitTree[void], key: string) =
|
||||||
## includes `key` in `c`.
|
## includes `key` in `c`.
|
||||||
discard rawInsert(c, key)
|
discard rawInsert(c, key)
|
||||||
|
|
||||||
proc `[]=`*[T](c: var TCritBitTree[T], key: string, val: T) =
|
proc `[]=`*[T](c: var CritBitTree[T], key: string, val: T) =
|
||||||
## puts a (key, value)-pair into `t`.
|
## puts a (key, value)-pair into `t`.
|
||||||
var n = rawInsert(c, key)
|
var n = rawInsert(c, key)
|
||||||
n.val = val
|
n.val = val
|
||||||
|
|
||||||
proc `[]`*[T](c: TCritBitTree[T], key: string): T {.inline.} =
|
proc `[]`*[T](c: CritBitTree[T], key: string): T {.inline.} =
|
||||||
## retrieves the value at ``c[key]``. If `key` is not in `t`,
|
## retrieves the value at ``c[key]``. If `key` is not in `t`,
|
||||||
## default empty value for the type `B` is returned
|
## default empty value for the type `B` is returned
|
||||||
## and no exception is raised. One can check with ``hasKey`` whether the key
|
## and no exception is raised. One can check with ``hasKey`` whether the key
|
||||||
|
|
@ -139,22 +141,22 @@ proc `[]`*[T](c: TCritBitTree[T], key: string): T {.inline.} =
|
||||||
let n = rawGet(c, key)
|
let n = rawGet(c, key)
|
||||||
if n != nil: result = n.val
|
if n != nil: result = n.val
|
||||||
|
|
||||||
proc mget*[T](c: var TCritBitTree[T], key: string): var T {.inline.} =
|
proc mget*[T](c: var CritBitTree[T], key: string): var T {.inline.} =
|
||||||
## retrieves the value at ``c[key]``. The value can be modified.
|
## retrieves the value at ``c[key]``. The value can be modified.
|
||||||
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
|
## If `key` is not in `t`, the ``KeyError`` exception is raised.
|
||||||
let n = rawGet(c, key)
|
let n = rawGet(c, key)
|
||||||
if n != nil: result = n.val
|
if n != nil: result = n.val
|
||||||
else: raise newException(EInvalidKey, "key not found: " & $key)
|
else: raise newException(KeyError, "key not found: " & $key)
|
||||||
|
|
||||||
proc excl*[T](c: var TCritBitTree[T], key: string) =
|
proc excl*[T](c: var CritBitTree[T], key: string) =
|
||||||
## removes `key` (and its associated value) from the set `c`.
|
## removes `key` (and its associated value) from the set `c`.
|
||||||
## If the `key` does not exist, nothing happens.
|
## If the `key` does not exist, nothing happens.
|
||||||
var p = c.root
|
var p = c.root
|
||||||
var wherep = addr(c.root)
|
var wherep = addr(c.root)
|
||||||
var whereq: ptr PNode = nil
|
var whereq: ptr Node[T] = nil
|
||||||
if p == nil: return
|
if p == nil: return
|
||||||
var dir = 0
|
var dir = 0
|
||||||
var q: PNode
|
var q: Node[T]
|
||||||
while not p.isLeaf:
|
while not p.isLeaf:
|
||||||
whereq = wherep
|
whereq = wherep
|
||||||
q = p
|
q = p
|
||||||
|
|
@ -170,7 +172,7 @@ proc excl*[T](c: var TCritBitTree[T], key: string) =
|
||||||
whereq[] = q.child[1 - dir]
|
whereq[] = q.child[1 - dir]
|
||||||
dec c.count
|
dec c.count
|
||||||
|
|
||||||
iterator leaves[T](n: PNode[T]): PNode[T] =
|
iterator leaves[T](n: Node[T]): Node[T] =
|
||||||
if n != nil:
|
if n != nil:
|
||||||
# XXX actually we could compute the necessary stack size in advance:
|
# XXX actually we could compute the necessary stack size in advance:
|
||||||
# it's rougly log2(c.count).
|
# it's rougly log2(c.count).
|
||||||
|
|
@ -183,33 +185,33 @@ iterator leaves[T](n: PNode[T]): PNode[T] =
|
||||||
assert(it != nil)
|
assert(it != nil)
|
||||||
yield it
|
yield it
|
||||||
|
|
||||||
iterator keys*[T](c: TCritBitTree[T]): string =
|
iterator keys*[T](c: CritBitTree[T]): string =
|
||||||
## yields all keys in lexicographical order.
|
## yields all keys in lexicographical order.
|
||||||
for x in leaves(c.root): yield x.key
|
for x in leaves(c.root): yield x.key
|
||||||
|
|
||||||
iterator values*[T](c: TCritBitTree[T]): T =
|
iterator values*[T](c: CritBitTree[T]): T =
|
||||||
## yields all values of `c` in the lexicographical order of the
|
## yields all values of `c` in the lexicographical order of the
|
||||||
## corresponding keys.
|
## corresponding keys.
|
||||||
for x in leaves(c.root): yield x.val
|
for x in leaves(c.root): yield x.val
|
||||||
|
|
||||||
iterator mvalues*[T](c: var TCritBitTree[T]): var T =
|
iterator mvalues*[T](c: var CritBitTree[T]): var T =
|
||||||
## yields all values of `c` in the lexicographical order of the
|
## yields all values of `c` in the lexicographical order of the
|
||||||
## corresponding keys. The values can be modified.
|
## corresponding keys. The values can be modified.
|
||||||
for x in leaves(c.root): yield x.val
|
for x in leaves(c.root): yield x.val
|
||||||
|
|
||||||
iterator items*[T](c: TCritBitTree[T]): string =
|
iterator items*[T](c: CritBitTree[T]): string =
|
||||||
## yields all keys in lexicographical order.
|
## yields all keys in lexicographical order.
|
||||||
for x in leaves(c.root): yield x.key
|
for x in leaves(c.root): yield x.key
|
||||||
|
|
||||||
iterator pairs*[T](c: TCritBitTree[T]): tuple[key: string, val: T] =
|
iterator pairs*[T](c: CritBitTree[T]): tuple[key: string, val: T] =
|
||||||
## yields all (key, value)-pairs of `c`.
|
## yields all (key, value)-pairs of `c`.
|
||||||
for x in leaves(c.root): yield (x.key, x.val)
|
for x in leaves(c.root): yield (x.key, x.val)
|
||||||
|
|
||||||
iterator mpairs*[T](c: var TCritBitTree[T]): tuple[key: string, val: var T] =
|
iterator mpairs*[T](c: var CritBitTree[T]): tuple[key: string, val: var T] =
|
||||||
## yields all (key, value)-pairs of `c`. The yielded values can be modified.
|
## yields all (key, value)-pairs of `c`. The yielded values can be modified.
|
||||||
for x in leaves(c.root): yield (x.key, x.val)
|
for x in leaves(c.root): yield (x.key, x.val)
|
||||||
|
|
||||||
proc allprefixedAux[T](c: TCritBitTree[T], key: string): PNode[T] =
|
proc allprefixedAux[T](c: CritBitTree[T], key: string): Node[T] =
|
||||||
var p = c.root
|
var p = c.root
|
||||||
var top = p
|
var top = p
|
||||||
if p != nil:
|
if p != nil:
|
||||||
|
|
@ -223,42 +225,42 @@ proc allprefixedAux[T](c: TCritBitTree[T], key: string): PNode[T] =
|
||||||
if p.key[i] != key[i]: return
|
if p.key[i] != key[i]: return
|
||||||
result = top
|
result = top
|
||||||
|
|
||||||
iterator itemsWithPrefix*[T](c: TCritBitTree[T], prefix: string): string =
|
iterator itemsWithPrefix*[T](c: CritBitTree[T], prefix: string): string =
|
||||||
## yields all keys starting with `prefix`.
|
## yields all keys starting with `prefix`.
|
||||||
let top = allprefixedAux(c, prefix)
|
let top = allprefixedAux(c, prefix)
|
||||||
for x in leaves(top): yield x.key
|
for x in leaves(top): yield x.key
|
||||||
|
|
||||||
iterator keysWithPrefix*[T](c: TCritBitTree[T], prefix: string): string =
|
iterator keysWithPrefix*[T](c: CritBitTree[T], prefix: string): string =
|
||||||
## yields all keys starting with `prefix`.
|
## yields all keys starting with `prefix`.
|
||||||
let top = allprefixedAux(c, prefix)
|
let top = allprefixedAux(c, prefix)
|
||||||
for x in leaves(top): yield x.key
|
for x in leaves(top): yield x.key
|
||||||
|
|
||||||
iterator valuesWithPrefix*[T](c: TCritBitTree[T], prefix: string): T =
|
iterator valuesWithPrefix*[T](c: CritBitTree[T], prefix: string): T =
|
||||||
## yields all values of `c` starting with `prefix` of the
|
## yields all values of `c` starting with `prefix` of the
|
||||||
## corresponding keys.
|
## corresponding keys.
|
||||||
let top = allprefixedAux(c, prefix)
|
let top = allprefixedAux(c, prefix)
|
||||||
for x in leaves(top): yield x.val
|
for x in leaves(top): yield x.val
|
||||||
|
|
||||||
iterator mvaluesWithPrefix*[T](c: var TCritBitTree[T], prefix: string): var T =
|
iterator mvaluesWithPrefix*[T](c: var CritBitTree[T], prefix: string): var T =
|
||||||
## yields all values of `c` starting with `prefix` of the
|
## yields all values of `c` starting with `prefix` of the
|
||||||
## corresponding keys. The values can be modified.
|
## corresponding keys. The values can be modified.
|
||||||
let top = allprefixedAux(c, prefix)
|
let top = allprefixedAux(c, prefix)
|
||||||
for x in leaves(top): yield x.val
|
for x in leaves(top): yield x.val
|
||||||
|
|
||||||
iterator pairsWithPrefix*[T](c: TCritBitTree[T],
|
iterator pairsWithPrefix*[T](c: CritBitTree[T],
|
||||||
prefix: string): tuple[key: string, val: T] =
|
prefix: string): tuple[key: string, val: T] =
|
||||||
## yields all (key, value)-pairs of `c` starting with `prefix`.
|
## yields all (key, value)-pairs of `c` starting with `prefix`.
|
||||||
let top = allprefixedAux(c, prefix)
|
let top = allprefixedAux(c, prefix)
|
||||||
for x in leaves(top): yield (x.key, x.val)
|
for x in leaves(top): yield (x.key, x.val)
|
||||||
|
|
||||||
iterator mpairsWithPrefix*[T](c: var TCritBitTree[T],
|
iterator mpairsWithPrefix*[T](c: var CritBitTree[T],
|
||||||
prefix: string): tuple[key: string, val: var T] =
|
prefix: string): tuple[key: string, val: var T] =
|
||||||
## yields all (key, value)-pairs of `c` starting with `prefix`.
|
## yields all (key, value)-pairs of `c` starting with `prefix`.
|
||||||
## The yielded values can be modified.
|
## The yielded values can be modified.
|
||||||
let top = allprefixedAux(c, prefix)
|
let top = allprefixedAux(c, prefix)
|
||||||
for x in leaves(top): yield (x.key, x.val)
|
for x in leaves(top): yield (x.key, x.val)
|
||||||
|
|
||||||
proc `$`*[T](c: TCritBitTree[T]): string =
|
proc `$`*[T](c: CritBitTree[T]): string =
|
||||||
## turns `c` into a string representation. Example outputs:
|
## turns `c` into a string representation. Example outputs:
|
||||||
## ``{keyA: value, keyB: value}``, ``{:}``
|
## ``{keyA: value, keyB: value}``, ``{:}``
|
||||||
## If `T` is void the outputs look like:
|
## If `T` is void the outputs look like:
|
||||||
|
|
@ -285,7 +287,7 @@ proc `$`*[T](c: TCritBitTree[T]): string =
|
||||||
result.add("}")
|
result.add("}")
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
var r: TCritBitTree[void]
|
var r: CritBitTree[void]
|
||||||
r.incl "abc"
|
r.incl "abc"
|
||||||
r.incl "xyz"
|
r.incl "xyz"
|
||||||
r.incl "def"
|
r.incl "def"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
## The ``intsets`` module implements an efficient int set implemented as a
|
## The ``intsets`` module implements an efficient int set implemented as a
|
||||||
## sparse bit set.
|
## sparse bit set.
|
||||||
## **Note**: Since Nimrod currently does not allow the assignment operator to
|
## **Note**: Since Nim currently does not allow the assignment operator to
|
||||||
## be overloaded, ``=`` for int sets performs some rather meaningless shallow
|
## be overloaded, ``=`` for int sets performs some rather meaningless shallow
|
||||||
## copy; use ``assign`` to get a deep copy.
|
## copy; use ``assign`` to get a deep copy.
|
||||||
|
|
||||||
|
|
@ -17,7 +17,7 @@ import
|
||||||
os, hashes, math
|
os, hashes, math
|
||||||
|
|
||||||
type
|
type
|
||||||
TBitScalar = int
|
BitScalar = int
|
||||||
|
|
||||||
const
|
const
|
||||||
InitIntSetSize = 8 # must be a power of two!
|
InitIntSetSize = 8 # must be a power of two!
|
||||||
|
|
@ -25,8 +25,8 @@ const
|
||||||
BitsPerTrunk = 1 shl TrunkShift # needs to be a power of 2 and
|
BitsPerTrunk = 1 shl TrunkShift # needs to be a power of 2 and
|
||||||
# divisible by 64
|
# divisible by 64
|
||||||
TrunkMask = BitsPerTrunk - 1
|
TrunkMask = BitsPerTrunk - 1
|
||||||
IntsPerTrunk = BitsPerTrunk div (sizeof(TBitScalar) * 8)
|
IntsPerTrunk = BitsPerTrunk div (sizeof(BitScalar) * 8)
|
||||||
IntShift = 5 + ord(sizeof(TBitScalar) == 8) # 5 or 6, depending on int width
|
IntShift = 5 + ord(sizeof(BitScalar) == 8) # 5 or 6, depending on int width
|
||||||
IntMask = 1 shl IntShift - 1
|
IntMask = 1 shl IntShift - 1
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -34,15 +34,16 @@ type
|
||||||
TTrunk {.final.} = object
|
TTrunk {.final.} = object
|
||||||
next: PTrunk # all nodes are connected with this pointer
|
next: PTrunk # all nodes are connected with this pointer
|
||||||
key: int # start address at bit 0
|
key: int # start address at bit 0
|
||||||
bits: array[0..IntsPerTrunk - 1, TBitScalar] # a bit vector
|
bits: array[0..IntsPerTrunk - 1, BitScalar] # a bit vector
|
||||||
|
|
||||||
TTrunkSeq = seq[PTrunk]
|
TTrunkSeq = seq[PTrunk]
|
||||||
TIntSet* {.final.} = object ## an efficient set of 'int' implemented as a
|
IntSet* = object ## an efficient set of 'int' implemented as a sparse bit set
|
||||||
## sparse bit set
|
|
||||||
counter, max: int
|
counter, max: int
|
||||||
head: PTrunk
|
head: PTrunk
|
||||||
data: TTrunkSeq
|
data: TTrunkSeq
|
||||||
|
|
||||||
|
{.deprecated: [TIntSet: IntSet].}
|
||||||
|
|
||||||
proc mustRehash(length, counter: int): bool {.inline.} =
|
proc mustRehash(length, counter: int): bool {.inline.} =
|
||||||
assert(length > counter)
|
assert(length > counter)
|
||||||
result = (length * 2 < counter * 3) or (length - counter < 4)
|
result = (length * 2 < counter * 3) or (length - counter < 4)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -15,52 +15,58 @@ when not defined(nimhygiene):
|
||||||
{.pragma: dirty.}
|
{.pragma: dirty.}
|
||||||
|
|
||||||
type
|
type
|
||||||
TDoublyLinkedNode* {.pure,
|
DoublyLinkedNodeObj*[T] = object ## a node a doubly linked list consists of
|
||||||
final.}[T] = object ## a node a doubly linked list consists of
|
next*, prev*: ref DoublyLinkedNodeObj[T]
|
||||||
next*, prev*: ref TDoublyLinkedNode[T]
|
|
||||||
value*: T
|
value*: T
|
||||||
PDoublyLinkedNode*[T] = ref TDoublyLinkedNode[T]
|
DoublyLinkedNode*[T] = ref DoublyLinkedNodeObj[T]
|
||||||
|
|
||||||
TSinglyLinkedNode* {.pure,
|
SinglyLinkedNodeObj*[T] = object ## a node a singly linked list consists of
|
||||||
final.}[T] = object ## a node a singly linked list consists of
|
next*: ref SinglyLinkedNodeObj[T]
|
||||||
next*: ref TSinglyLinkedNode[T]
|
|
||||||
value*: T
|
value*: T
|
||||||
PSinglyLinkedNode*[T] = ref TSinglyLinkedNode[T]
|
SinglyLinkedNode*[T] = ref SinglyLinkedNodeObj[T]
|
||||||
|
|
||||||
TSinglyLinkedList* {.pure, final.}[T] = object ## a singly linked list
|
SinglyLinkedList*[T] = object ## a singly linked list
|
||||||
head*, tail*: PSinglyLinkedNode[T]
|
head*, tail*: SinglyLinkedNode[T]
|
||||||
|
|
||||||
TDoublyLinkedList* {.pure, final.}[T] = object ## a doubly linked list
|
DoublyLinkedList*[T] = object ## a doubly linked list
|
||||||
head*, tail*: PDoublyLinkedNode[T]
|
head*, tail*: DoublyLinkedNode[T]
|
||||||
|
|
||||||
TSinglyLinkedRing* {.pure, final.}[T] = object ## a singly linked ring
|
SinglyLinkedRing*[T] = object ## a singly linked ring
|
||||||
head*: PSinglyLinkedNode[T]
|
head*: SinglyLinkedNode[T]
|
||||||
|
|
||||||
TDoublyLinkedRing* {.pure, final.}[T] = object ## a doubly linked ring
|
DoublyLinkedRing*[T] = object ## a doubly linked ring
|
||||||
head*: PDoublyLinkedNode[T]
|
head*: DoublyLinkedNode[T]
|
||||||
|
|
||||||
proc initSinglyLinkedList*[T](): TSinglyLinkedList[T] =
|
{.deprecated: [TDoublyLinkedNode: DoublyLinkedNodeObj,
|
||||||
|
PDoublyLinkedNode: DoublyLinkedNode,
|
||||||
|
TSinglyLinkedNode: SinglyLinkedNodeObj,
|
||||||
|
PSinglyLinkedNode: SinglyLinkedNode,
|
||||||
|
TDoublyLinkedList: DoublyLinkedList,
|
||||||
|
TSinglyLinkedRing: SinglyLinkedRing,
|
||||||
|
TDoublyLinkedRing: DoublyLinkedRing].}
|
||||||
|
|
||||||
|
proc initSinglyLinkedList*[T](): SinglyLinkedList[T] =
|
||||||
## creates a new singly linked list that is empty.
|
## creates a new singly linked list that is empty.
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc initDoublyLinkedList*[T](): TDoublyLinkedList[T] =
|
proc initDoublyLinkedList*[T](): DoublyLinkedList[T] =
|
||||||
## creates a new doubly linked list that is empty.
|
## creates a new doubly linked list that is empty.
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc initSinglyLinkedRing*[T](): TSinglyLinkedRing[T] =
|
proc initSinglyLinkedRing*[T](): SinglyLinkedRing[T] =
|
||||||
## creates a new singly linked ring that is empty.
|
## creates a new singly linked ring that is empty.
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc initDoublyLinkedRing*[T](): TDoublyLinkedRing[T] =
|
proc initDoublyLinkedRing*[T](): DoublyLinkedRing[T] =
|
||||||
## creates a new doubly linked ring that is empty.
|
## creates a new doubly linked ring that is empty.
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc newDoublyLinkedNode*[T](value: T): PDoublyLinkedNode[T] =
|
proc newDoublyLinkedNode*[T](value: T): DoublyLinkedNode[T] =
|
||||||
## creates a new doubly linked node with the given `value`.
|
## creates a new doubly linked node with the given `value`.
|
||||||
new(result)
|
new(result)
|
||||||
result.value = value
|
result.value = value
|
||||||
|
|
||||||
proc newSinglyLinkedNode*[T](value: T): PSinglyLinkedNode[T] =
|
proc newSinglyLinkedNode*[T](value: T): SinglyLinkedNode[T] =
|
||||||
## creates a new singly linked node with the given `value`.
|
## creates a new singly linked node with the given `value`.
|
||||||
new(result)
|
new(result)
|
||||||
result.value = value
|
result.value = value
|
||||||
|
|
@ -99,38 +105,38 @@ template findImpl() {.dirty.} =
|
||||||
for x in nodes(L):
|
for x in nodes(L):
|
||||||
if x.value == value: return x
|
if x.value == value: return x
|
||||||
|
|
||||||
iterator items*[T](L: TDoublyLinkedList[T]): T =
|
iterator items*[T](L: DoublyLinkedList[T]): T =
|
||||||
## yields every value of `L`.
|
## yields every value of `L`.
|
||||||
itemsListImpl()
|
itemsListImpl()
|
||||||
|
|
||||||
iterator items*[T](L: TSinglyLinkedList[T]): T =
|
iterator items*[T](L: SinglyLinkedList[T]): T =
|
||||||
## yields every value of `L`.
|
## yields every value of `L`.
|
||||||
itemsListImpl()
|
itemsListImpl()
|
||||||
|
|
||||||
iterator items*[T](L: TSinglyLinkedRing[T]): T =
|
iterator items*[T](L: SinglyLinkedRing[T]): T =
|
||||||
## yields every value of `L`.
|
## yields every value of `L`.
|
||||||
itemsRingImpl()
|
itemsRingImpl()
|
||||||
|
|
||||||
iterator items*[T](L: TDoublyLinkedRing[T]): T =
|
iterator items*[T](L: DoublyLinkedRing[T]): T =
|
||||||
## yields every value of `L`.
|
## yields every value of `L`.
|
||||||
itemsRingImpl()
|
itemsRingImpl()
|
||||||
|
|
||||||
iterator nodes*[T](L: TSinglyLinkedList[T]): PSinglyLinkedNode[T] =
|
iterator nodes*[T](L: SinglyLinkedList[T]): SinglyLinkedNode[T] =
|
||||||
## iterates over every node of `x`. Removing the current node from the
|
## iterates over every node of `x`. Removing the current node from the
|
||||||
## list during traversal is supported.
|
## list during traversal is supported.
|
||||||
nodesListImpl()
|
nodesListImpl()
|
||||||
|
|
||||||
iterator nodes*[T](L: TDoublyLinkedList[T]): PDoublyLinkedNode[T] =
|
iterator nodes*[T](L: DoublyLinkedList[T]): DoublyLinkedNode[T] =
|
||||||
## iterates over every node of `x`. Removing the current node from the
|
## iterates over every node of `x`. Removing the current node from the
|
||||||
## list during traversal is supported.
|
## list during traversal is supported.
|
||||||
nodesListImpl()
|
nodesListImpl()
|
||||||
|
|
||||||
iterator nodes*[T](L: TSinglyLinkedRing[T]): PSinglyLinkedNode[T] =
|
iterator nodes*[T](L: SinglyLinkedRing[T]): SinglyLinkedNode[T] =
|
||||||
## iterates over every node of `x`. Removing the current node from the
|
## iterates over every node of `x`. Removing the current node from the
|
||||||
## list during traversal is supported.
|
## list during traversal is supported.
|
||||||
nodesRingImpl()
|
nodesRingImpl()
|
||||||
|
|
||||||
iterator nodes*[T](L: TDoublyLinkedRing[T]): PDoublyLinkedNode[T] =
|
iterator nodes*[T](L: DoublyLinkedRing[T]): DoublyLinkedNode[T] =
|
||||||
## iterates over every node of `x`. Removing the current node from the
|
## iterates over every node of `x`. Removing the current node from the
|
||||||
## list during traversal is supported.
|
## list during traversal is supported.
|
||||||
nodesRingImpl()
|
nodesRingImpl()
|
||||||
|
|
@ -142,33 +148,33 @@ template dollarImpl() {.dirty.} =
|
||||||
result.add($x.value)
|
result.add($x.value)
|
||||||
result.add("]")
|
result.add("]")
|
||||||
|
|
||||||
proc `$`*[T](L: TSinglyLinkedList[T]): string =
|
proc `$`*[T](L: SinglyLinkedList[T]): string =
|
||||||
## turns a list into its string representation.
|
## turns a list into its string representation.
|
||||||
dollarImpl()
|
dollarImpl()
|
||||||
|
|
||||||
proc `$`*[T](L: TDoublyLinkedList[T]): string =
|
proc `$`*[T](L: DoublyLinkedList[T]): string =
|
||||||
## turns a list into its string representation.
|
## turns a list into its string representation.
|
||||||
dollarImpl()
|
dollarImpl()
|
||||||
|
|
||||||
proc `$`*[T](L: TSinglyLinkedRing[T]): string =
|
proc `$`*[T](L: SinglyLinkedRing[T]): string =
|
||||||
## turns a list into its string representation.
|
## turns a list into its string representation.
|
||||||
dollarImpl()
|
dollarImpl()
|
||||||
|
|
||||||
proc `$`*[T](L: TDoublyLinkedRing[T]): string =
|
proc `$`*[T](L: DoublyLinkedRing[T]): string =
|
||||||
## turns a list into its string representation.
|
## turns a list into its string representation.
|
||||||
dollarImpl()
|
dollarImpl()
|
||||||
|
|
||||||
proc find*[T](L: TSinglyLinkedList[T], value: T): PSinglyLinkedNode[T] =
|
proc find*[T](L: SinglyLinkedList[T], value: T): SinglyLinkedNode[T] =
|
||||||
## searches in the list for a value. Returns nil if the value does not
|
## searches in the list for a value. Returns nil if the value does not
|
||||||
## exist.
|
## exist.
|
||||||
findImpl()
|
findImpl()
|
||||||
|
|
||||||
proc find*[T](L: TDoublyLinkedList[T], value: T): PDoublyLinkedNode[T] =
|
proc find*[T](L: DoublyLinkedList[T], value: T): DoublyLinkedNode[T] =
|
||||||
## searches in the list for a value. Returns nil if the value does not
|
## searches in the list for a value. Returns nil if the value does not
|
||||||
## exist.
|
## exist.
|
||||||
findImpl()
|
findImpl()
|
||||||
|
|
||||||
proc find*[T](L: TSinglyLinkedRing[T], value: T): PSinglyLinkedNode[T] =
|
proc find*[T](L: SinglyLinkedRing[T], value: T): SinglyLinkedNode[T] =
|
||||||
## searches in the list for a value. Returns nil if the value does not
|
## searches in the list for a value. Returns nil if the value does not
|
||||||
## exist.
|
## exist.
|
||||||
findImpl()
|
findImpl()
|
||||||
|
|
@ -300,5 +306,3 @@ proc remove*[T](L: var TDoublyLinkedRing[T], n: PDoublyLinkedNode[T]) =
|
||||||
L.head = nil
|
L.head = nil
|
||||||
else:
|
else:
|
||||||
L.head = L.head.prev
|
L.head = L.head.prev
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -12,13 +12,15 @@
|
||||||
import math
|
import math
|
||||||
|
|
||||||
type
|
type
|
||||||
TQueue* {.pure, final.}[T] = object ## a queue
|
Queue*[T] = object ## a queue
|
||||||
data: seq[T]
|
data: seq[T]
|
||||||
rd, wr, count, mask: int
|
rd, wr, count, mask: int
|
||||||
|
|
||||||
|
{.deprecated: [TQueue: Queue].}
|
||||||
|
|
||||||
proc initQueue*[T](initialSize=4): TQueue[T] =
|
proc initQueue*[T](initialSize=4): TQueue[T] =
|
||||||
## creates a new queue. `initialSize` needs to be a power of 2.
|
## creates a new queue. `initialSize` needs to be a power of 2.
|
||||||
assert IsPowerOfTwo(initialSize)
|
assert isPowerOfTwo(initialSize)
|
||||||
result.mask = initialSize-1
|
result.mask = initialSize-1
|
||||||
newSeq(result.data, initialSize)
|
newSeq(result.data, initialSize)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2011 Alex Mitchell
|
# (c) Copyright 2011 Alex Mitchell
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -31,7 +31,7 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
|
||||||
##
|
##
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block::
|
||||||
## let
|
## let
|
||||||
## s1 = @[1, 2, 3]
|
## s1 = @[1, 2, 3]
|
||||||
## s2 = @[4, 5]
|
## s2 = @[4, 5]
|
||||||
|
|
@ -50,7 +50,7 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
|
||||||
proc deduplicate*[T](seq1: seq[T]): seq[T] =
|
proc deduplicate*[T](seq1: seq[T]): seq[T] =
|
||||||
## Returns a new sequence without duplicates.
|
## Returns a new sequence without duplicates.
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block::
|
||||||
## let
|
## let
|
||||||
## dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4]
|
## dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4]
|
||||||
## dup2 = @["a", "a", "c", "d", "d"]
|
## dup2 = @["a", "a", "c", "d", "d"]
|
||||||
|
|
@ -69,7 +69,7 @@ proc zip*[S, T](seq1: seq[S], seq2: seq[T]): seq[tuple[a: S, b: T]] =
|
||||||
## fields `a` and `b`. If one sequence is shorter, the remaining items in the
|
## fields `a` and `b`. If one sequence is shorter, the remaining items in the
|
||||||
## longer sequence are discarded. Example:
|
## longer sequence are discarded. Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block::
|
||||||
## let
|
## let
|
||||||
## short = @[1, 2, 3]
|
## short = @[1, 2, 3]
|
||||||
## long = @[6, 5, 4, 3, 2, 1]
|
## long = @[6, 5, 4, 3, 2, 1]
|
||||||
|
|
@ -104,7 +104,7 @@ proc distribute*[T](s: seq[T], num: int, spread = true): seq[seq[T]] =
|
||||||
##
|
##
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block::
|
||||||
## let numbers = @[1, 2, 3, 4, 5, 6, 7]
|
## let numbers = @[1, 2, 3, 4, 5, 6, 7]
|
||||||
## assert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
|
## assert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
|
||||||
## assert numbers.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]]
|
## assert numbers.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]]
|
||||||
|
|
@ -155,7 +155,7 @@ iterator filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): T =
|
||||||
##
|
##
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block::
|
||||||
## let numbers = @[1, 4, 5, 8, 9, 7, 4]
|
## let numbers = @[1, 4, 5, 8, 9, 7, 4]
|
||||||
## for n in filter(numbers, proc (x: int): bool = x mod 2 == 0):
|
## for n in filter(numbers, proc (x: int): bool = x mod 2 == 0):
|
||||||
## echo($n)
|
## echo($n)
|
||||||
|
|
@ -169,7 +169,7 @@ proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T] =
|
||||||
##
|
##
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block::
|
||||||
## let
|
## let
|
||||||
## colors = @["red", "yellow", "black"]
|
## colors = @["red", "yellow", "black"]
|
||||||
## f1 = filter(colors, proc(x: string): bool = x.len < 6)
|
## f1 = filter(colors, proc(x: string): bool = x.len < 6)
|
||||||
|
|
@ -184,7 +184,7 @@ proc keepIf*[T](seq1: var seq[T], pred: proc(item: T): bool {.closure.}) =
|
||||||
##
|
##
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block::
|
||||||
## var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1]
|
## var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1]
|
||||||
## keepIf(floats, proc(x: float): bool = x > 10)
|
## keepIf(floats, proc(x: float): bool = x > 10)
|
||||||
## assert floats == @[13.0, 12.5, 10.1]
|
## assert floats == @[13.0, 12.5, 10.1]
|
||||||
|
|
@ -202,7 +202,7 @@ proc delete*[T](s: var seq[T], first=0, last=0) =
|
||||||
##
|
##
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
##.. code-block:: nimrod
|
##.. code-block::
|
||||||
## let outcome = @[1,1,1,1,1,1,1,1]
|
## let outcome = @[1,1,1,1,1,1,1,1]
|
||||||
## var dest = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
|
## var dest = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
|
||||||
## dest.delete(3, 8)
|
## dest.delete(3, 8)
|
||||||
|
|
@ -223,7 +223,7 @@ proc insert*[T](dest: var seq[T], src: openArray[T], pos=0) =
|
||||||
##
|
##
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
##.. code-block:: nimrod
|
##.. code-block::
|
||||||
## var dest = @[1,1,1,1,1,1,1,1]
|
## var dest = @[1,1,1,1,1,1,1,1]
|
||||||
## let
|
## let
|
||||||
## src = @[2,2,2,2,2,2]
|
## src = @[2,2,2,2,2,2]
|
||||||
|
|
@ -254,7 +254,7 @@ template filterIt*(seq1, pred: expr): expr {.immediate.} =
|
||||||
## the ``it`` variable for testing, like: ``filterIt("abcxyz", it == 'x')``.
|
## the ``it`` variable for testing, like: ``filterIt("abcxyz", it == 'x')``.
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block::
|
||||||
## let
|
## let
|
||||||
## temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44]
|
## temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44]
|
||||||
## acceptable = filterIt(temperatures, it < 50 and it > -10)
|
## acceptable = filterIt(temperatures, it < 50 and it > -10)
|
||||||
|
|
@ -273,7 +273,7 @@ template keepItIf*(varSeq, pred: expr) =
|
||||||
## the ``it`` variable for testing, like: ``keepItIf("abcxyz", it == 'x')``.
|
## the ``it`` variable for testing, like: ``keepItIf("abcxyz", it == 'x')``.
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block::
|
||||||
## var candidates = @["foo", "bar", "baz", "foobar"]
|
## var candidates = @["foo", "bar", "baz", "foobar"]
|
||||||
## keepItIf(candidates, it.len == 3 and it[0] == 'b')
|
## keepItIf(candidates, it.len == 3 and it[0] == 'b')
|
||||||
## assert candidates == @["bar", "baz"]
|
## assert candidates == @["bar", "baz"]
|
||||||
|
|
@ -292,7 +292,7 @@ template toSeq*(iter: expr): expr {.immediate.} =
|
||||||
##
|
##
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block::
|
||||||
## let
|
## let
|
||||||
## numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
|
## numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||||
## odd_numbers = toSeq(filter(numeric) do (x: int) -> bool:
|
## odd_numbers = toSeq(filter(numeric) do (x: int) -> bool:
|
||||||
|
|
@ -318,18 +318,18 @@ template foldl*(sequence, operation: expr): expr =
|
||||||
## the sequence of numbers 1, 2 and 3 will be parenthesized as (((1) - 2) -
|
## the sequence of numbers 1, 2 and 3 will be parenthesized as (((1) - 2) -
|
||||||
## 3). Example:
|
## 3). Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block::
|
||||||
## let
|
## let
|
||||||
## numbers = @[5, 9, 11]
|
## numbers = @[5, 9, 11]
|
||||||
## addition = foldl(numbers, a + b)
|
## addition = foldl(numbers, a + b)
|
||||||
## substraction = foldl(numbers, a - b)
|
## substraction = foldl(numbers, a - b)
|
||||||
## multiplication = foldl(numbers, a * b)
|
## multiplication = foldl(numbers, a * b)
|
||||||
## words = @["nim", "rod", "is", "cool"]
|
## words = @["nim", "is", "cool"]
|
||||||
## concatenation = foldl(words, a & b)
|
## concatenation = foldl(words, a & b)
|
||||||
## assert addition == 25, "Addition is (((5)+9)+11)"
|
## assert addition == 25, "Addition is (((5)+9)+11)"
|
||||||
## assert substraction == -15, "Substraction is (((5)-9)-11)"
|
## assert substraction == -15, "Substraction is (((5)-9)-11)"
|
||||||
## assert multiplication == 495, "Multiplication is (((5)*9)*11)"
|
## assert multiplication == 495, "Multiplication is (((5)*9)*11)"
|
||||||
## assert concatenation == "nimrodiscool"
|
## assert concatenation == "nimiscool"
|
||||||
assert sequence.len > 0, "Can't fold empty sequences"
|
assert sequence.len > 0, "Can't fold empty sequences"
|
||||||
var result {.gensym.}: type(sequence[0])
|
var result {.gensym.}: type(sequence[0])
|
||||||
result = sequence[0]
|
result = sequence[0]
|
||||||
|
|
@ -354,18 +354,18 @@ template foldr*(sequence, operation: expr): expr =
|
||||||
## the sequence of numbers 1, 2 and 3 will be parenthesized as (1 - (2 -
|
## the sequence of numbers 1, 2 and 3 will be parenthesized as (1 - (2 -
|
||||||
## (3))). Example:
|
## (3))). Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block::
|
||||||
## let
|
## let
|
||||||
## numbers = @[5, 9, 11]
|
## numbers = @[5, 9, 11]
|
||||||
## addition = foldr(numbers, a + b)
|
## addition = foldr(numbers, a + b)
|
||||||
## substraction = foldr(numbers, a - b)
|
## substraction = foldr(numbers, a - b)
|
||||||
## multiplication = foldr(numbers, a * b)
|
## multiplication = foldr(numbers, a * b)
|
||||||
## words = @["nim", "rod", "is", "cool"]
|
## words = @["nim", "is", "cool"]
|
||||||
## concatenation = foldr(words, a & b)
|
## concatenation = foldr(words, a & b)
|
||||||
## assert addition == 25, "Addition is (5+(9+(11)))"
|
## assert addition == 25, "Addition is (5+(9+(11)))"
|
||||||
## assert substraction == 7, "Substraction is (5-(9-(11)))"
|
## assert substraction == 7, "Substraction is (5-(9-(11)))"
|
||||||
## assert multiplication == 495, "Multiplication is (5*(9*(11)))"
|
## assert multiplication == 495, "Multiplication is (5*(9*(11)))"
|
||||||
## assert concatenation == "nimrodiscool"
|
## assert concatenation == "nimiscool"
|
||||||
assert sequence.len > 0, "Can't fold empty sequences"
|
assert sequence.len > 0, "Can't fold empty sequences"
|
||||||
var result {.gensym.}: type(sequence[0])
|
var result {.gensym.}: type(sequence[0])
|
||||||
result = sequence[sequence.len - 1]
|
result = sequence[sequence.len - 1]
|
||||||
|
|
@ -384,7 +384,7 @@ template mapIt*(seq1, typ, pred: expr): expr =
|
||||||
## since the new returned sequence can have a different type than the
|
## since the new returned sequence can have a different type than the
|
||||||
## original. Example:
|
## original. Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block::
|
||||||
## let
|
## let
|
||||||
## nums = @[1, 2, 3, 4]
|
## nums = @[1, 2, 3, 4]
|
||||||
## strings = nums.mapIt(string, $(4 * it))
|
## strings = nums.mapIt(string, $(4 * it))
|
||||||
|
|
@ -401,7 +401,7 @@ template mapIt*(varSeq, pred: expr) =
|
||||||
## expression. The expression has to return the same type as the sequence you
|
## expression. The expression has to return the same type as the sequence you
|
||||||
## are mutating. Example:
|
## are mutating. Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block::
|
||||||
## var nums = @[1, 2, 3, 4]
|
## var nums = @[1, 2, 3, 4]
|
||||||
## nums.mapIt(it * 3)
|
## nums.mapIt(it * 3)
|
||||||
## assert nums[0] + nums[3] == 15
|
## assert nums[0] + nums[3] == 15
|
||||||
|
|
@ -412,7 +412,7 @@ template mapIt*(varSeq, pred: expr) =
|
||||||
template newSeqWith*(len: int, init: expr): expr =
|
template newSeqWith*(len: int, init: expr): expr =
|
||||||
## creates a new sequence, calling `init` to initialize each value. Example:
|
## creates a new sequence, calling `init` to initialize each value. Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block::
|
||||||
## var seq2D = newSeqWith(20, newSeq[bool](10))
|
## var seq2D = newSeqWith(20, newSeq[bool](10))
|
||||||
## seq2D[0][0] = true
|
## seq2D[0][0] = true
|
||||||
## seq2D[1][0] = true
|
## seq2D[1][0] = true
|
||||||
|
|
@ -503,12 +503,12 @@ when isMainModule:
|
||||||
addition = foldl(numbers, a + b)
|
addition = foldl(numbers, a + b)
|
||||||
substraction = foldl(numbers, a - b)
|
substraction = foldl(numbers, a - b)
|
||||||
multiplication = foldl(numbers, a * b)
|
multiplication = foldl(numbers, a * b)
|
||||||
words = @["nim", "rod", "is", "cool"]
|
words = @["nim", "is", "cool"]
|
||||||
concatenation = foldl(words, a & b)
|
concatenation = foldl(words, a & b)
|
||||||
assert addition == 25, "Addition is (((5)+9)+11)"
|
assert addition == 25, "Addition is (((5)+9)+11)"
|
||||||
assert substraction == -15, "Substraction is (((5)-9)-11)"
|
assert substraction == -15, "Substraction is (((5)-9)-11)"
|
||||||
assert multiplication == 495, "Multiplication is (((5)*9)*11)"
|
assert multiplication == 495, "Multiplication is (((5)*9)*11)"
|
||||||
assert concatenation == "nimrodiscool"
|
assert concatenation == "nimiscool"
|
||||||
|
|
||||||
block: # foldr tests
|
block: # foldr tests
|
||||||
let
|
let
|
||||||
|
|
@ -516,12 +516,12 @@ when isMainModule:
|
||||||
addition = foldr(numbers, a + b)
|
addition = foldr(numbers, a + b)
|
||||||
substraction = foldr(numbers, a - b)
|
substraction = foldr(numbers, a - b)
|
||||||
multiplication = foldr(numbers, a * b)
|
multiplication = foldr(numbers, a * b)
|
||||||
words = @["nim", "rod", "is", "cool"]
|
words = @["nim", "is", "cool"]
|
||||||
concatenation = foldr(words, a & b)
|
concatenation = foldr(words, a & b)
|
||||||
assert addition == 25, "Addition is (5+(9+(11)))"
|
assert addition == 25, "Addition is (5+(9+(11)))"
|
||||||
assert substraction == 7, "Substraction is (5-(9-(11)))"
|
assert substraction == 7, "Substraction is (5-(9-(11)))"
|
||||||
assert multiplication == 495, "Multiplication is (5*(9*(11)))"
|
assert multiplication == 495, "Multiplication is (5*(9*(11)))"
|
||||||
assert concatenation == "nimrodiscool"
|
assert concatenation == "nimiscool"
|
||||||
|
|
||||||
block: # delete tests
|
block: # delete tests
|
||||||
let outcome = @[1,1,1,1,1,1,1,1]
|
let outcome = @[1,1,1,1,1,1,1,1]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -24,17 +24,19 @@ when not defined(nimhygiene):
|
||||||
{.pragma: dirty.}
|
{.pragma: dirty.}
|
||||||
|
|
||||||
type
|
type
|
||||||
TSlotEnum = enum seEmpty, seFilled, seDeleted
|
SlotEnum = enum seEmpty, seFilled, seDeleted
|
||||||
TKeyValuePair[A] = tuple[slot: TSlotEnum, key: A]
|
KeyValuePair[A] = tuple[slot: SlotEnum, key: A]
|
||||||
TKeyValuePairSeq[A] = seq[TKeyValuePair[A]]
|
KeyValuePairSeq[A] = seq[KeyValuePair[A]]
|
||||||
TSet* {.final, myShallow.}[A] = object ## \
|
HashSet* {.myShallow.}[A] = object ## \
|
||||||
## A generic hash set.
|
## A generic hash set.
|
||||||
##
|
##
|
||||||
## Use `init() <#init,TSet[A],int>`_ or `initSet[type]() <#initSet>`_
|
## Use `init() <#init,HashSet[A],int>`_ or `initSet[type]() <#initSet>`_
|
||||||
## before calling other procs on it.
|
## before calling other procs on it.
|
||||||
data: TKeyValuePairSeq[A]
|
data: KeyValuePairSeq[A]
|
||||||
counter: int
|
counter: int
|
||||||
|
|
||||||
|
{.deprecated: [TSet: HashSet].}
|
||||||
|
|
||||||
proc isValid*[A](s: TSet[A]): bool =
|
proc isValid*[A](s: TSet[A]): bool =
|
||||||
## Returns `true` if the set has been initialized with `initSet <#initSet>`_.
|
## Returns `true` if the set has been initialized with `initSet <#initSet>`_.
|
||||||
##
|
##
|
||||||
|
|
@ -43,7 +45,7 @@ proc isValid*[A](s: TSet[A]): bool =
|
||||||
## your own procs to verify that sets passed to your procs are correctly
|
## your own procs to verify that sets passed to your procs are correctly
|
||||||
## initialized. Example:
|
## initialized. Example:
|
||||||
##
|
##
|
||||||
## .. code-block :: nimrod
|
## .. code-block ::
|
||||||
## proc savePreferences(options: TSet[string]) =
|
## proc savePreferences(options: TSet[string]) =
|
||||||
## assert options.isValid, "Pass an initialized set!"
|
## assert options.isValid, "Pass an initialized set!"
|
||||||
## # Do stuff here, may crash in release builds!
|
## # Do stuff here, may crash in release builds!
|
||||||
|
|
@ -490,19 +492,20 @@ proc map*[A, B](data: TSet[A], op: proc (x: A): B {.closure.}): TSet[B] =
|
||||||
# ------------------------------ ordered set ------------------------------
|
# ------------------------------ ordered set ------------------------------
|
||||||
|
|
||||||
type
|
type
|
||||||
TOrderedKeyValuePair[A] = tuple[
|
OrderedKeyValuePair[A] = tuple[
|
||||||
slot: TSlotEnum, next: int, key: A]
|
slot: SlotEnum, next: int, key: A]
|
||||||
TOrderedKeyValuePairSeq[A] = seq[TOrderedKeyValuePair[A]]
|
OrderedKeyValuePairSeq[A] = seq[OrderedKeyValuePair[A]]
|
||||||
TOrderedSet* {.
|
OrderedSet* {.myShallow.}[A] = object ## \
|
||||||
final, myShallow.}[A] = object ## \
|
|
||||||
## A generic hash set that remembers insertion order.
|
## A generic hash set that remembers insertion order.
|
||||||
##
|
##
|
||||||
## Use `init() <#init,TOrderedSet[A],int>`_ or `initOrderedSet[type]()
|
## Use `init() <#init,OrderedSet[A],int>`_ or `initOrderedSet[type]()
|
||||||
## <#initOrderedSet>`_ before calling other procs on it.
|
## <#initOrderedSet>`_ before calling other procs on it.
|
||||||
data: TOrderedKeyValuePairSeq[A]
|
data: TOrderedKeyValuePairSeq[A]
|
||||||
counter, first, last: int
|
counter, first, last: int
|
||||||
|
|
||||||
proc isValid*[A](s: TOrderedSet[A]): bool =
|
{.deprecated: [TOrderedSet: OrderedSet].}
|
||||||
|
|
||||||
|
proc isValid*[A](s: OrderedSet[A]): bool =
|
||||||
## Returns `true` if the ordered set has been initialized with `initSet
|
## Returns `true` if the ordered set has been initialized with `initSet
|
||||||
## <#initOrderedSet>`_.
|
## <#initOrderedSet>`_.
|
||||||
##
|
##
|
||||||
|
|
@ -511,13 +514,13 @@ proc isValid*[A](s: TOrderedSet[A]): bool =
|
||||||
## in your own procs to verify that ordered sets passed to your procs are
|
## in your own procs to verify that ordered sets passed to your procs are
|
||||||
## correctly initialized. Example:
|
## correctly initialized. Example:
|
||||||
##
|
##
|
||||||
## .. code-block :: nimrod
|
## .. code-block::
|
||||||
## proc saveTarotCards(cards: TOrderedSet[int]) =
|
## proc saveTarotCards(cards: TOrderedSet[int]) =
|
||||||
## assert cards.isValid, "Pass an initialized set!"
|
## assert cards.isValid, "Pass an initialized set!"
|
||||||
## # Do stuff here, may crash in release builds!
|
## # Do stuff here, may crash in release builds!
|
||||||
result = not s.data.isNil
|
result = not s.data.isNil
|
||||||
|
|
||||||
proc len*[A](s: TOrderedSet[A]): int {.inline.} =
|
proc len*[A](s: OrderedSet[A]): int {.inline.} =
|
||||||
## Returns the number of keys in `s`.
|
## Returns the number of keys in `s`.
|
||||||
##
|
##
|
||||||
## Due to an implementation detail you can call this proc on variables which
|
## Due to an implementation detail you can call this proc on variables which
|
||||||
|
|
@ -734,14 +737,14 @@ proc `==`*[A](s, t: TOrderedSet[A]): bool =
|
||||||
proc testModule() =
|
proc testModule() =
|
||||||
## Internal micro test to validate docstrings and such.
|
## Internal micro test to validate docstrings and such.
|
||||||
block isValidTest:
|
block isValidTest:
|
||||||
var options: TSet[string]
|
var options: HashSet[string]
|
||||||
proc savePreferences(options: TSet[string]) =
|
proc savePreferences(options: TSet[string]) =
|
||||||
assert options.isValid, "Pass an initialized set!"
|
assert options.isValid, "Pass an initialized set!"
|
||||||
options = initSet[string]()
|
options = initSet[string]()
|
||||||
options.savePreferences
|
options.savePreferences
|
||||||
|
|
||||||
block lenTest:
|
block lenTest:
|
||||||
var values: TSet[int]
|
var values: HashSet[int]
|
||||||
assert(not values.isValid)
|
assert(not values.isValid)
|
||||||
assert values.len == 0
|
assert values.len == 0
|
||||||
assert values.card == 0
|
assert values.card == 0
|
||||||
|
|
@ -835,14 +838,14 @@ proc testModule() =
|
||||||
assert b == toSet(["1", "2", "3"])
|
assert b == toSet(["1", "2", "3"])
|
||||||
|
|
||||||
block isValidTest:
|
block isValidTest:
|
||||||
var cards: TOrderedSet[string]
|
var cards: OrderedSet[string]
|
||||||
proc saveTarotCards(cards: TOrderedSet[string]) =
|
proc saveTarotCards(cards: TOrderedSet[string]) =
|
||||||
assert cards.isValid, "Pass an initialized set!"
|
assert cards.isValid, "Pass an initialized set!"
|
||||||
cards = initOrderedSet[string]()
|
cards = initOrderedSet[string]()
|
||||||
cards.saveTarotCards
|
cards.saveTarotCards
|
||||||
|
|
||||||
block lenTest:
|
block lenTest:
|
||||||
var values: TOrderedSet[int]
|
var values: OrderedSet[int]
|
||||||
assert(not values.isValid)
|
assert(not values.isValid)
|
||||||
assert values.len == 0
|
assert values.len == 0
|
||||||
assert values.card == 0
|
assert values.card == 0
|
||||||
|
|
@ -879,7 +882,7 @@ proc testModule() =
|
||||||
assert(a == b) # https://github.com/Araq/Nimrod/issues/1413
|
assert(a == b) # https://github.com/Araq/Nimrod/issues/1413
|
||||||
|
|
||||||
block initBlocks:
|
block initBlocks:
|
||||||
var a: TOrderedSet[int]
|
var a: OrderedSet[int]
|
||||||
a.init(4)
|
a.init(4)
|
||||||
a.incl(2)
|
a.incl(2)
|
||||||
a.init
|
a.init
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2013 Andreas Rumpf
|
# (c) Copyright 2013 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
## you add such a proc for your custom type everything will work. See this
|
## you add such a proc for your custom type everything will work. See this
|
||||||
## example:
|
## example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block::
|
||||||
## type
|
## type
|
||||||
## Person = object
|
## Person = object
|
||||||
## firstName, lastName: string
|
## firstName, lastName: string
|
||||||
|
|
@ -61,13 +61,15 @@ import
|
||||||
{.pragma: myShallow.}
|
{.pragma: myShallow.}
|
||||||
|
|
||||||
type
|
type
|
||||||
TSlotEnum = enum seEmpty, seFilled, seDeleted
|
SlotEnum = enum seEmpty, seFilled, seDeleted
|
||||||
TKeyValuePair[A, B] = tuple[slot: TSlotEnum, key: A, val: B]
|
KeyValuePair[A, B] = tuple[slot: SlotEnum, key: A, val: B]
|
||||||
TKeyValuePairSeq[A, B] = seq[TKeyValuePair[A, B]]
|
KeyValuePairSeq[A, B] = seq[KeyValuePair[A, B]]
|
||||||
TTable* {.final, myShallow.}[A, B] = object ## generic hash table
|
Table* {.myShallow.}[A, B] = object ## generic hash table
|
||||||
data: TKeyValuePairSeq[A, B]
|
data: KeyValuePairSeq[A, B]
|
||||||
counter: int
|
counter: int
|
||||||
PTable*[A,B] = ref TTable[A, B]
|
TableRef*[A,B] = ref Table[A, B]
|
||||||
|
|
||||||
|
{.deprecated: [TTable: Table, PTable: TableRef].}
|
||||||
|
|
||||||
when not defined(nimhygiene):
|
when not defined(nimhygiene):
|
||||||
{.pragma: dirty.}
|
{.pragma: dirty.}
|
||||||
|
|
@ -158,12 +160,12 @@ proc hasKey*[A, B](t: TTable[A, B], key: A): bool =
|
||||||
## returns true iff `key` is in the table `t`.
|
## returns true iff `key` is in the table `t`.
|
||||||
result = rawGet(t, key) >= 0
|
result = rawGet(t, key) >= 0
|
||||||
|
|
||||||
proc rawInsert[A, B](t: var TTable[A, B], data: var TKeyValuePairSeq[A, B],
|
proc rawInsert[A, B](t: var TTable[A, B], data: var KeyValuePairSeq[A, B],
|
||||||
key: A, val: B) =
|
key: A, val: B) =
|
||||||
rawInsertImpl()
|
rawInsertImpl()
|
||||||
|
|
||||||
proc enlarge[A, B](t: var TTable[A, B]) =
|
proc enlarge[A, B](t: var TTable[A, B]) =
|
||||||
var n: TKeyValuePairSeq[A, B]
|
var n: KeyValuePairSeq[A, B]
|
||||||
newSeq(n, len(t.data) * growthFactor)
|
newSeq(n, len(t.data) * growthFactor)
|
||||||
for i in countup(0, high(t.data)):
|
for i in countup(0, high(t.data)):
|
||||||
if t.data[i].slot == seFilled: rawInsert(t, n, t.data[i].key, t.data[i].val)
|
if t.data[i].slot == seFilled: rawInsert(t, n, t.data[i].key, t.data[i].val)
|
||||||
|
|
@ -347,14 +349,16 @@ proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): PTable[C, B] =
|
||||||
# ------------------------------ ordered table ------------------------------
|
# ------------------------------ ordered table ------------------------------
|
||||||
|
|
||||||
type
|
type
|
||||||
TOrderedKeyValuePair[A, B] = tuple[
|
OrderedKeyValuePair[A, B] = tuple[
|
||||||
slot: TSlotEnum, next: int, key: A, val: B]
|
slot: SlotEnum, next: int, key: A, val: B]
|
||||||
TOrderedKeyValuePairSeq[A, B] = seq[TOrderedKeyValuePair[A, B]]
|
OrderedKeyValuePairSeq[A, B] = seq[OrderedKeyValuePair[A, B]]
|
||||||
TOrderedTable* {.
|
OrderedTable* {.
|
||||||
final, myShallow.}[A, B] = object ## table that remembers insertion order
|
myShallow.}[A, B] = object ## table that remembers insertion order
|
||||||
data: TOrderedKeyValuePairSeq[A, B]
|
data: OrderedKeyValuePairSeq[A, B]
|
||||||
counter, first, last: int
|
counter, first, last: int
|
||||||
POrderedTable*[A, B] = ref TOrderedTable[A, B]
|
OrderedTableRef*[A, B] = ref OrderedTable[A, B]
|
||||||
|
|
||||||
|
{.deprecated: [TOrderedTable: OrderedTable, POrderedTable: OrderedTableRef].}
|
||||||
|
|
||||||
proc len*[A, B](t: TOrderedTable[A, B]): int {.inline.} =
|
proc len*[A, B](t: TOrderedTable[A, B]): int {.inline.} =
|
||||||
## returns the number of keys in `t`.
|
## returns the number of keys in `t`.
|
||||||
|
|
@ -608,11 +612,13 @@ proc sort*[A, B](t: POrderedTable[A, B],
|
||||||
# ------------------------------ count tables -------------------------------
|
# ------------------------------ count tables -------------------------------
|
||||||
|
|
||||||
type
|
type
|
||||||
TCountTable* {.final, myShallow.}[
|
CountTable* {.myShallow.}[
|
||||||
A] = object ## table that counts the number of each key
|
A] = object ## table that counts the number of each key
|
||||||
data: seq[tuple[key: A, val: int]]
|
data: seq[tuple[key: A, val: int]]
|
||||||
counter: int
|
counter: int
|
||||||
PCountTable*[A] = ref TCountTable[A]
|
CountTableRef*[A] = ref CountTable[A]
|
||||||
|
|
||||||
|
{.deprecated: [TCountTable: CountTable, PCountTable: CountTableRef].}
|
||||||
|
|
||||||
proc len*[A](t: TCountTable[A]): int =
|
proc len*[A](t: TCountTable[A]): int =
|
||||||
## returns the number of keys in `t`.
|
## returns the number of keys in `t`.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2010 Andreas Rumpf
|
# (c) Copyright 2010 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -12,18 +12,20 @@
|
||||||
import strutils
|
import strutils
|
||||||
|
|
||||||
type
|
type
|
||||||
TColor* = distinct int ## a color stored as RGB
|
Color* = distinct int ## a color stored as RGB
|
||||||
|
|
||||||
proc `==` *(a, b: TColor): bool {.borrow.}
|
{.deprecated: [TColor: Color].}
|
||||||
|
|
||||||
|
proc `==` *(a, b: Color): bool {.borrow.}
|
||||||
## compares two colors.
|
## compares two colors.
|
||||||
|
|
||||||
template extract(a: TColor, r, g, b: expr) {.immediate.}=
|
template extract(a: Color, r, g, b: expr) {.immediate.}=
|
||||||
var r = a.int shr 16 and 0xff
|
var r = a.int shr 16 and 0xff
|
||||||
var g = a.int shr 8 and 0xff
|
var g = a.int shr 8 and 0xff
|
||||||
var b = a.int and 0xff
|
var b = a.int and 0xff
|
||||||
|
|
||||||
template rawRGB(r, g, b: int): expr =
|
template rawRGB(r, g, b: int): expr =
|
||||||
TColor(r shl 16 or g shl 8 or b)
|
Color(r shl 16 or g shl 8 or b)
|
||||||
|
|
||||||
template colorOp(op: expr) {.immediate.} =
|
template colorOp(op: expr) {.immediate.} =
|
||||||
extract(a, ar, ag, ab)
|
extract(a, ar, ag, ab)
|
||||||
|
|
@ -38,23 +40,23 @@ proc satMinus(a, b: int): int {.inline.} =
|
||||||
result = a -% b
|
result = a -% b
|
||||||
if result < 0: result = 0
|
if result < 0: result = 0
|
||||||
|
|
||||||
proc `+`*(a, b: TColor): TColor =
|
proc `+`*(a, b: Color): Color =
|
||||||
## adds two colors: This uses saturated artithmetic, so that each color
|
## adds two colors: This uses saturated artithmetic, so that each color
|
||||||
## component cannot overflow (255 is used as a maximum).
|
## component cannot overflow (255 is used as a maximum).
|
||||||
colorOp(satPlus)
|
colorOp(satPlus)
|
||||||
|
|
||||||
proc `-`*(a, b: TColor): TColor =
|
proc `-`*(a, b: Color): Color =
|
||||||
## substracts two colors: This uses saturated artithmetic, so that each color
|
## substracts two colors: This uses saturated artithmetic, so that each color
|
||||||
## component cannot overflow (255 is used as a maximum).
|
## component cannot overflow (255 is used as a maximum).
|
||||||
colorOp(satMinus)
|
colorOp(satMinus)
|
||||||
|
|
||||||
proc extractRGB*(a: TColor): tuple[r, g, b: range[0..255]] =
|
proc extractRGB*(a: Color): tuple[r, g, b: range[0..255]] =
|
||||||
## extracts the red/green/blue components of the color `a`.
|
## extracts the red/green/blue components of the color `a`.
|
||||||
result.r = a.int shr 16 and 0xff
|
result.r = a.int shr 16 and 0xff
|
||||||
result.g = a.int shr 8 and 0xff
|
result.g = a.int shr 8 and 0xff
|
||||||
result.b = a.int and 0xff
|
result.b = a.int and 0xff
|
||||||
|
|
||||||
proc intensity*(a: TColor, f: float): TColor =
|
proc intensity*(a: Color, f: float): Color =
|
||||||
## returns `a` with intensity `f`. `f` should be a float from 0.0 (completely
|
## returns `a` with intensity `f`. `f` should be a float from 0.0 (completely
|
||||||
## dark) to 1.0 (full color intensity).
|
## dark) to 1.0 (full color intensity).
|
||||||
var r = toInt(toFloat(a.int shr 16 and 0xff) * f)
|
var r = toInt(toFloat(a.int shr 16 and 0xff) * f)
|
||||||
|
|
@ -65,7 +67,7 @@ proc intensity*(a: TColor, f: float): TColor =
|
||||||
if b >% 255: b = 255
|
if b >% 255: b = 255
|
||||||
result = rawRGB(r, g, b)
|
result = rawRGB(r, g, b)
|
||||||
|
|
||||||
template mix*(a, b: TColor, fn: expr): expr =
|
template mix*(a, b: Color, fn: expr): expr =
|
||||||
## uses `fn` to mix the colors `a` and `b`. `fn` is invoked for each component
|
## uses `fn` to mix the colors `a` and `b`. `fn` is invoked for each component
|
||||||
## R, G, and B. This is a template because `fn` should be inlined and the
|
## R, G, and B. This is a template because `fn` should be inlined and the
|
||||||
## compiler cannot inline proc pointers yet. If `fn`'s result is not in the
|
## compiler cannot inline proc pointers yet. If `fn`'s result is not in the
|
||||||
|
|
@ -84,146 +86,146 @@ template mix*(a, b: TColor, fn: expr): expr =
|
||||||
|
|
||||||
|
|
||||||
const
|
const
|
||||||
colAliceBlue* = TColor(0xF0F8FF)
|
colAliceBlue* = Color(0xF0F8FF)
|
||||||
colAntiqueWhite* = TColor(0xFAEBD7)
|
colAntiqueWhite* = Color(0xFAEBD7)
|
||||||
colAqua* = TColor(0x00FFFF)
|
colAqua* = Color(0x00FFFF)
|
||||||
colAquamarine* = TColor(0x7FFFD4)
|
colAquamarine* = Color(0x7FFFD4)
|
||||||
colAzure* = TColor(0xF0FFFF)
|
colAzure* = Color(0xF0FFFF)
|
||||||
colBeige* = TColor(0xF5F5DC)
|
colBeige* = Color(0xF5F5DC)
|
||||||
colBisque* = TColor(0xFFE4C4)
|
colBisque* = Color(0xFFE4C4)
|
||||||
colBlack* = TColor(0x000000)
|
colBlack* = Color(0x000000)
|
||||||
colBlanchedAlmond* = TColor(0xFFEBCD)
|
colBlanchedAlmond* = Color(0xFFEBCD)
|
||||||
colBlue* = TColor(0x0000FF)
|
colBlue* = Color(0x0000FF)
|
||||||
colBlueViolet* = TColor(0x8A2BE2)
|
colBlueViolet* = Color(0x8A2BE2)
|
||||||
colBrown* = TColor(0xA52A2A)
|
colBrown* = Color(0xA52A2A)
|
||||||
colBurlyWood* = TColor(0xDEB887)
|
colBurlyWood* = Color(0xDEB887)
|
||||||
colCadetBlue* = TColor(0x5F9EA0)
|
colCadetBlue* = Color(0x5F9EA0)
|
||||||
colChartreuse* = TColor(0x7FFF00)
|
colChartreuse* = Color(0x7FFF00)
|
||||||
colChocolate* = TColor(0xD2691E)
|
colChocolate* = Color(0xD2691E)
|
||||||
colCoral* = TColor(0xFF7F50)
|
colCoral* = Color(0xFF7F50)
|
||||||
colCornflowerBlue* = TColor(0x6495ED)
|
colCornflowerBlue* = Color(0x6495ED)
|
||||||
colCornsilk* = TColor(0xFFF8DC)
|
colCornsilk* = Color(0xFFF8DC)
|
||||||
colCrimson* = TColor(0xDC143C)
|
colCrimson* = Color(0xDC143C)
|
||||||
colCyan* = TColor(0x00FFFF)
|
colCyan* = Color(0x00FFFF)
|
||||||
colDarkBlue* = TColor(0x00008B)
|
colDarkBlue* = Color(0x00008B)
|
||||||
colDarkCyan* = TColor(0x008B8B)
|
colDarkCyan* = Color(0x008B8B)
|
||||||
colDarkGoldenRod* = TColor(0xB8860B)
|
colDarkGoldenRod* = Color(0xB8860B)
|
||||||
colDarkGray* = TColor(0xA9A9A9)
|
colDarkGray* = Color(0xA9A9A9)
|
||||||
colDarkGreen* = TColor(0x006400)
|
colDarkGreen* = Color(0x006400)
|
||||||
colDarkKhaki* = TColor(0xBDB76B)
|
colDarkKhaki* = Color(0xBDB76B)
|
||||||
colDarkMagenta* = TColor(0x8B008B)
|
colDarkMagenta* = Color(0x8B008B)
|
||||||
colDarkOliveGreen* = TColor(0x556B2F)
|
colDarkOliveGreen* = Color(0x556B2F)
|
||||||
colDarkorange* = TColor(0xFF8C00)
|
colDarkorange* = Color(0xFF8C00)
|
||||||
colDarkOrchid* = TColor(0x9932CC)
|
colDarkOrchid* = Color(0x9932CC)
|
||||||
colDarkRed* = TColor(0x8B0000)
|
colDarkRed* = Color(0x8B0000)
|
||||||
colDarkSalmon* = TColor(0xE9967A)
|
colDarkSalmon* = Color(0xE9967A)
|
||||||
colDarkSeaGreen* = TColor(0x8FBC8F)
|
colDarkSeaGreen* = Color(0x8FBC8F)
|
||||||
colDarkSlateBlue* = TColor(0x483D8B)
|
colDarkSlateBlue* = Color(0x483D8B)
|
||||||
colDarkSlateGray* = TColor(0x2F4F4F)
|
colDarkSlateGray* = Color(0x2F4F4F)
|
||||||
colDarkTurquoise* = TColor(0x00CED1)
|
colDarkTurquoise* = Color(0x00CED1)
|
||||||
colDarkViolet* = TColor(0x9400D3)
|
colDarkViolet* = Color(0x9400D3)
|
||||||
colDeepPink* = TColor(0xFF1493)
|
colDeepPink* = Color(0xFF1493)
|
||||||
colDeepSkyBlue* = TColor(0x00BFFF)
|
colDeepSkyBlue* = Color(0x00BFFF)
|
||||||
colDimGray* = TColor(0x696969)
|
colDimGray* = Color(0x696969)
|
||||||
colDodgerBlue* = TColor(0x1E90FF)
|
colDodgerBlue* = Color(0x1E90FF)
|
||||||
colFireBrick* = TColor(0xB22222)
|
colFireBrick* = Color(0xB22222)
|
||||||
colFloralWhite* = TColor(0xFFFAF0)
|
colFloralWhite* = Color(0xFFFAF0)
|
||||||
colForestGreen* = TColor(0x228B22)
|
colForestGreen* = Color(0x228B22)
|
||||||
colFuchsia* = TColor(0xFF00FF)
|
colFuchsia* = Color(0xFF00FF)
|
||||||
colGainsboro* = TColor(0xDCDCDC)
|
colGainsboro* = Color(0xDCDCDC)
|
||||||
colGhostWhite* = TColor(0xF8F8FF)
|
colGhostWhite* = Color(0xF8F8FF)
|
||||||
colGold* = TColor(0xFFD700)
|
colGold* = Color(0xFFD700)
|
||||||
colGoldenRod* = TColor(0xDAA520)
|
colGoldenRod* = Color(0xDAA520)
|
||||||
colGray* = TColor(0x808080)
|
colGray* = Color(0x808080)
|
||||||
colGreen* = TColor(0x008000)
|
colGreen* = Color(0x008000)
|
||||||
colGreenYellow* = TColor(0xADFF2F)
|
colGreenYellow* = Color(0xADFF2F)
|
||||||
colHoneyDew* = TColor(0xF0FFF0)
|
colHoneyDew* = Color(0xF0FFF0)
|
||||||
colHotPink* = TColor(0xFF69B4)
|
colHotPink* = Color(0xFF69B4)
|
||||||
colIndianRed* = TColor(0xCD5C5C)
|
colIndianRed* = Color(0xCD5C5C)
|
||||||
colIndigo* = TColor(0x4B0082)
|
colIndigo* = Color(0x4B0082)
|
||||||
colIvory* = TColor(0xFFFFF0)
|
colIvory* = Color(0xFFFFF0)
|
||||||
colKhaki* = TColor(0xF0E68C)
|
colKhaki* = Color(0xF0E68C)
|
||||||
colLavender* = TColor(0xE6E6FA)
|
colLavender* = Color(0xE6E6FA)
|
||||||
colLavenderBlush* = TColor(0xFFF0F5)
|
colLavenderBlush* = Color(0xFFF0F5)
|
||||||
colLawnGreen* = TColor(0x7CFC00)
|
colLawnGreen* = Color(0x7CFC00)
|
||||||
colLemonChiffon* = TColor(0xFFFACD)
|
colLemonChiffon* = Color(0xFFFACD)
|
||||||
colLightBlue* = TColor(0xADD8E6)
|
colLightBlue* = Color(0xADD8E6)
|
||||||
colLightCoral* = TColor(0xF08080)
|
colLightCoral* = Color(0xF08080)
|
||||||
colLightCyan* = TColor(0xE0FFFF)
|
colLightCyan* = Color(0xE0FFFF)
|
||||||
colLightGoldenRodYellow* = TColor(0xFAFAD2)
|
colLightGoldenRodYellow* = Color(0xFAFAD2)
|
||||||
colLightGrey* = TColor(0xD3D3D3)
|
colLightGrey* = Color(0xD3D3D3)
|
||||||
colLightGreen* = TColor(0x90EE90)
|
colLightGreen* = Color(0x90EE90)
|
||||||
colLightPink* = TColor(0xFFB6C1)
|
colLightPink* = Color(0xFFB6C1)
|
||||||
colLightSalmon* = TColor(0xFFA07A)
|
colLightSalmon* = Color(0xFFA07A)
|
||||||
colLightSeaGreen* = TColor(0x20B2AA)
|
colLightSeaGreen* = Color(0x20B2AA)
|
||||||
colLightSkyBlue* = TColor(0x87CEFA)
|
colLightSkyBlue* = Color(0x87CEFA)
|
||||||
colLightSlateGray* = TColor(0x778899)
|
colLightSlateGray* = Color(0x778899)
|
||||||
colLightSteelBlue* = TColor(0xB0C4DE)
|
colLightSteelBlue* = Color(0xB0C4DE)
|
||||||
colLightYellow* = TColor(0xFFFFE0)
|
colLightYellow* = Color(0xFFFFE0)
|
||||||
colLime* = TColor(0x00FF00)
|
colLime* = Color(0x00FF00)
|
||||||
colLimeGreen* = TColor(0x32CD32)
|
colLimeGreen* = Color(0x32CD32)
|
||||||
colLinen* = TColor(0xFAF0E6)
|
colLinen* = Color(0xFAF0E6)
|
||||||
colMagenta* = TColor(0xFF00FF)
|
colMagenta* = Color(0xFF00FF)
|
||||||
colMaroon* = TColor(0x800000)
|
colMaroon* = Color(0x800000)
|
||||||
colMediumAquaMarine* = TColor(0x66CDAA)
|
colMediumAquaMarine* = Color(0x66CDAA)
|
||||||
colMediumBlue* = TColor(0x0000CD)
|
colMediumBlue* = Color(0x0000CD)
|
||||||
colMediumOrchid* = TColor(0xBA55D3)
|
colMediumOrchid* = Color(0xBA55D3)
|
||||||
colMediumPurple* = TColor(0x9370D8)
|
colMediumPurple* = Color(0x9370D8)
|
||||||
colMediumSeaGreen* = TColor(0x3CB371)
|
colMediumSeaGreen* = Color(0x3CB371)
|
||||||
colMediumSlateBlue* = TColor(0x7B68EE)
|
colMediumSlateBlue* = Color(0x7B68EE)
|
||||||
colMediumSpringGreen* = TColor(0x00FA9A)
|
colMediumSpringGreen* = Color(0x00FA9A)
|
||||||
colMediumTurquoise* = TColor(0x48D1CC)
|
colMediumTurquoise* = Color(0x48D1CC)
|
||||||
colMediumVioletRed* = TColor(0xC71585)
|
colMediumVioletRed* = Color(0xC71585)
|
||||||
colMidnightBlue* = TColor(0x191970)
|
colMidnightBlue* = Color(0x191970)
|
||||||
colMintCream* = TColor(0xF5FFFA)
|
colMintCream* = Color(0xF5FFFA)
|
||||||
colMistyRose* = TColor(0xFFE4E1)
|
colMistyRose* = Color(0xFFE4E1)
|
||||||
colMoccasin* = TColor(0xFFE4B5)
|
colMoccasin* = Color(0xFFE4B5)
|
||||||
colNavajoWhite* = TColor(0xFFDEAD)
|
colNavajoWhite* = Color(0xFFDEAD)
|
||||||
colNavy* = TColor(0x000080)
|
colNavy* = Color(0x000080)
|
||||||
colOldLace* = TColor(0xFDF5E6)
|
colOldLace* = Color(0xFDF5E6)
|
||||||
colOlive* = TColor(0x808000)
|
colOlive* = Color(0x808000)
|
||||||
colOliveDrab* = TColor(0x6B8E23)
|
colOliveDrab* = Color(0x6B8E23)
|
||||||
colOrange* = TColor(0xFFA500)
|
colOrange* = Color(0xFFA500)
|
||||||
colOrangeRed* = TColor(0xFF4500)
|
colOrangeRed* = Color(0xFF4500)
|
||||||
colOrchid* = TColor(0xDA70D6)
|
colOrchid* = Color(0xDA70D6)
|
||||||
colPaleGoldenRod* = TColor(0xEEE8AA)
|
colPaleGoldenRod* = Color(0xEEE8AA)
|
||||||
colPaleGreen* = TColor(0x98FB98)
|
colPaleGreen* = Color(0x98FB98)
|
||||||
colPaleTurquoise* = TColor(0xAFEEEE)
|
colPaleTurquoise* = Color(0xAFEEEE)
|
||||||
colPaleVioletRed* = TColor(0xD87093)
|
colPaleVioletRed* = Color(0xD87093)
|
||||||
colPapayaWhip* = TColor(0xFFEFD5)
|
colPapayaWhip* = Color(0xFFEFD5)
|
||||||
colPeachPuff* = TColor(0xFFDAB9)
|
colPeachPuff* = Color(0xFFDAB9)
|
||||||
colPeru* = TColor(0xCD853F)
|
colPeru* = Color(0xCD853F)
|
||||||
colPink* = TColor(0xFFC0CB)
|
colPink* = Color(0xFFC0CB)
|
||||||
colPlum* = TColor(0xDDA0DD)
|
colPlum* = Color(0xDDA0DD)
|
||||||
colPowderBlue* = TColor(0xB0E0E6)
|
colPowderBlue* = Color(0xB0E0E6)
|
||||||
colPurple* = TColor(0x800080)
|
colPurple* = Color(0x800080)
|
||||||
colRed* = TColor(0xFF0000)
|
colRed* = Color(0xFF0000)
|
||||||
colRosyBrown* = TColor(0xBC8F8F)
|
colRosyBrown* = Color(0xBC8F8F)
|
||||||
colRoyalBlue* = TColor(0x4169E1)
|
colRoyalBlue* = Color(0x4169E1)
|
||||||
colSaddleBrown* = TColor(0x8B4513)
|
colSaddleBrown* = Color(0x8B4513)
|
||||||
colSalmon* = TColor(0xFA8072)
|
colSalmon* = Color(0xFA8072)
|
||||||
colSandyBrown* = TColor(0xF4A460)
|
colSandyBrown* = Color(0xF4A460)
|
||||||
colSeaGreen* = TColor(0x2E8B57)
|
colSeaGreen* = Color(0x2E8B57)
|
||||||
colSeaShell* = TColor(0xFFF5EE)
|
colSeaShell* = Color(0xFFF5EE)
|
||||||
colSienna* = TColor(0xA0522D)
|
colSienna* = Color(0xA0522D)
|
||||||
colSilver* = TColor(0xC0C0C0)
|
colSilver* = Color(0xC0C0C0)
|
||||||
colSkyBlue* = TColor(0x87CEEB)
|
colSkyBlue* = Color(0x87CEEB)
|
||||||
colSlateBlue* = TColor(0x6A5ACD)
|
colSlateBlue* = Color(0x6A5ACD)
|
||||||
colSlateGray* = TColor(0x708090)
|
colSlateGray* = Color(0x708090)
|
||||||
colSnow* = TColor(0xFFFAFA)
|
colSnow* = Color(0xFFFAFA)
|
||||||
colSpringGreen* = TColor(0x00FF7F)
|
colSpringGreen* = Color(0x00FF7F)
|
||||||
colSteelBlue* = TColor(0x4682B4)
|
colSteelBlue* = Color(0x4682B4)
|
||||||
colTan* = TColor(0xD2B48C)
|
colTan* = Color(0xD2B48C)
|
||||||
colTeal* = TColor(0x008080)
|
colTeal* = Color(0x008080)
|
||||||
colThistle* = TColor(0xD8BFD8)
|
colThistle* = Color(0xD8BFD8)
|
||||||
colTomato* = TColor(0xFF6347)
|
colTomato* = Color(0xFF6347)
|
||||||
colTurquoise* = TColor(0x40E0D0)
|
colTurquoise* = Color(0x40E0D0)
|
||||||
colViolet* = TColor(0xEE82EE)
|
colViolet* = Color(0xEE82EE)
|
||||||
colWheat* = TColor(0xF5DEB3)
|
colWheat* = Color(0xF5DEB3)
|
||||||
colWhite* = TColor(0xFFFFFF)
|
colWhite* = Color(0xFFFFFF)
|
||||||
colWhiteSmoke* = TColor(0xF5F5F5)
|
colWhiteSmoke* = Color(0xF5F5F5)
|
||||||
colYellow* = TColor(0xFFFF00)
|
colYellow* = Color(0xFFFF00)
|
||||||
colYellowGreen* = TColor(0x9ACD32)
|
colYellowGreen* = Color(0x9ACD32)
|
||||||
|
|
||||||
colorNames = [
|
colorNames = [
|
||||||
("aliceblue", colAliceBlue),
|
("aliceblue", colAliceBlue),
|
||||||
|
|
@ -367,11 +369,11 @@ const
|
||||||
("yellow", colYellow),
|
("yellow", colYellow),
|
||||||
("yellowgreen", colYellowGreen)]
|
("yellowgreen", colYellowGreen)]
|
||||||
|
|
||||||
proc `$`*(c: TColor): string =
|
proc `$`*(c: Color): string =
|
||||||
## converts a color into its textual representation. Example: ``#00FF00``.
|
## converts a color into its textual representation. Example: ``#00FF00``.
|
||||||
result = '#' & toHex(int(c), 6)
|
result = '#' & toHex(int(c), 6)
|
||||||
|
|
||||||
proc binaryStrSearch(x: openarray[tuple[name: string, col: TColor]],
|
proc binaryStrSearch(x: openArray[tuple[name: string, col: Color]],
|
||||||
y: string): int =
|
y: string): int =
|
||||||
var a = 0
|
var a = 0
|
||||||
var b = len(x) - 1
|
var b = len(x) - 1
|
||||||
|
|
@ -383,14 +385,14 @@ proc binaryStrSearch(x: openarray[tuple[name: string, col: TColor]],
|
||||||
else: return mid
|
else: return mid
|
||||||
result = - 1
|
result = - 1
|
||||||
|
|
||||||
proc parseColor*(name: string): TColor =
|
proc parseColor*(name: string): Color =
|
||||||
## parses `name` to a color value. If no valid color could be
|
## parses `name` to a color value. If no valid color could be
|
||||||
## parsed ``EInvalidValue`` is raised.
|
## parsed ``EInvalidValue`` is raised.
|
||||||
if name[0] == '#':
|
if name[0] == '#':
|
||||||
result = TColor(parseHexInt(name))
|
result = Color(parseHexInt(name))
|
||||||
else:
|
else:
|
||||||
var idx = binaryStrSearch(colorNames, name)
|
var idx = binaryStrSearch(colorNames, name)
|
||||||
if idx < 0: raise newException(EInvalidValue, "unkown color: " & name)
|
if idx < 0: raise newException(ValueError, "unkown color: " & name)
|
||||||
result = colorNames[idx][1]
|
result = colorNames[idx][1]
|
||||||
|
|
||||||
proc isColor*(name: string): bool =
|
proc isColor*(name: string): bool =
|
||||||
|
|
@ -403,7 +405,7 @@ proc isColor*(name: string): bool =
|
||||||
else:
|
else:
|
||||||
result = binaryStrSearch(colorNames, name) >= 0
|
result = binaryStrSearch(colorNames, name) >= 0
|
||||||
|
|
||||||
proc rgb*(r, g, b: range[0..255]): TColor =
|
proc rgb*(r, g, b: range[0..255]): Color =
|
||||||
## constructs a color from RGB values.
|
## constructs a color from RGB values.
|
||||||
result = rawRGB(r, g, b)
|
result = rawRGB(r, g, b)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2010 Andreas Rumpf
|
# (c) Copyright 2010 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -25,9 +25,11 @@ const
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TComplex* = tuple[re, im: float]
|
Complex* = tuple[re, im: float]
|
||||||
## a complex number, consisting of a real and an imaginary part
|
## a complex number, consisting of a real and an imaginary part
|
||||||
|
|
||||||
|
{.deprecated: [TComplex: Complex].}
|
||||||
|
|
||||||
proc `==` *(x, y: TComplex): bool =
|
proc `==` *(x, y: TComplex): bool =
|
||||||
## Compare two complex numbers `x` and `y` for equality.
|
## Compare two complex numbers `x` and `y` for equality.
|
||||||
result = x.re == y.re and x.im == y.im
|
result = x.re == y.re and x.im == y.im
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2014 Andreas Rumpf
|
# (c) Copyright 2014 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2014 Andreas Rumpf
|
# (c) Copyright 2014 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## Implements Nimrod's 'spawn'.
|
## Implements Nim's 'spawn'.
|
||||||
|
|
||||||
when not compileOption("threads"):
|
when not compileOption("threads"):
|
||||||
{.error: "Threadpool requires --threads:on option.".}
|
{.error: "Threadpool requires --threads:on option.".}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
import strtabs, times
|
import strtabs, times
|
||||||
|
|
||||||
proc parseCookies*(s: string): PStringTable =
|
proc parseCookies*(s: string): StringTableRef =
|
||||||
## parses cookies into a string table.
|
## parses cookies into a string table.
|
||||||
result = newStringTable(modeCaseInsensitive)
|
result = newStringTable(modeCaseInsensitive)
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
@ -39,7 +39,7 @@ proc setCookie*(key, value: string, domain = "", path = "",
|
||||||
if path != "": result.add("; Path=" & path)
|
if path != "": result.add("; Path=" & path)
|
||||||
if expires != "": result.add("; Expires=" & expires)
|
if expires != "": result.add("; Expires=" & expires)
|
||||||
|
|
||||||
proc setCookie*(key, value: string, expires: TTimeInfo,
|
proc setCookie*(key, value: string, expires: TimeInfo,
|
||||||
domain = "", path = "", noName = false): string =
|
domain = "", path = "", noName = false): string =
|
||||||
## Creates a command in the format of
|
## Creates a command in the format of
|
||||||
## ``Set-Cookie: key=value; Domain=...; ...``
|
## ``Set-Cookie: key=value; Domain=...; ...``
|
||||||
|
|
@ -50,7 +50,7 @@ proc setCookie*(key, value: string, expires: TTimeInfo,
|
||||||
format(expires, "ddd',' dd MMM yyyy HH:mm:ss 'UTC'"), noName)
|
format(expires, "ddd',' dd MMM yyyy HH:mm:ss 'UTC'"), noName)
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
var tim = TTime(int(getTime()) + 76 * (60 * 60 * 24))
|
var tim = Time(int(getTime()) + 76 * (60 * 60 * 24))
|
||||||
|
|
||||||
echo(setCookie("test", "value", tim.getGMTime()))
|
echo(setCookie("test", "value", tim.getGMTime()))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -12,7 +12,9 @@
|
||||||
## Windows ``LoadLibrary``.
|
## Windows ``LoadLibrary``.
|
||||||
|
|
||||||
type
|
type
|
||||||
TLibHandle* = pointer ## a handle to a dynamically loaded library
|
LibHandle* = pointer ## a handle to a dynamically loaded library
|
||||||
|
|
||||||
|
{.deprecated: [TLibHandle: LibHandle].}
|
||||||
|
|
||||||
proc loadLib*(path: string, global_symbols=false): TLibHandle
|
proc loadLib*(path: string, global_symbols=false): TLibHandle
|
||||||
## loads a library from `path`. Returns nil if the library could not
|
## loads a library from `path`. Returns nil if the library could not
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2014 Andreas Rumpf
|
# (c) Copyright 2014 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -14,18 +14,20 @@ import os, parseutils, strutils
|
||||||
|
|
||||||
when not defined(windows):
|
when not defined(windows):
|
||||||
type
|
type
|
||||||
TConverter = object
|
ConverterObj = object
|
||||||
PConverter* = ptr TConverter ## can convert between two character sets
|
EncodingConverter* = ptr ConverterObj ## can convert between two character sets
|
||||||
|
|
||||||
else:
|
else:
|
||||||
type
|
type
|
||||||
TCodePage = distinct int32
|
CodePage = distinct int32
|
||||||
PConverter* = object
|
EncodingConverter* = object
|
||||||
dest, src: TCodePage
|
dest, src: CodePage
|
||||||
|
|
||||||
type
|
type
|
||||||
EInvalidEncoding* = object of EInvalidValue ## exception that is raised
|
EncodingError* = object of ValueError ## exception that is raised
|
||||||
## for encoding errors
|
## for encoding errors
|
||||||
|
|
||||||
|
{.deprecated: [EInvalidEncoding: EncodingError, PConverter: EncodingConverter].}
|
||||||
|
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
proc eqEncodingNames(a, b: string): bool =
|
proc eqEncodingNames(a, b: string): bool =
|
||||||
|
|
@ -214,26 +216,26 @@ when defined(windows):
|
||||||
defaultChar: array[0..1, char]
|
defaultChar: array[0..1, char]
|
||||||
leadByte: array[0..12-1, char]
|
leadByte: array[0..12-1, char]
|
||||||
|
|
||||||
proc getCPInfo(codePage: TCodePage, lpCPInfo: var TCpInfo): int32 {.
|
proc getCPInfo(codePage: CodePage, lpCPInfo: var TCpInfo): int32 {.
|
||||||
stdcall, importc: "GetCPInfo", dynlib: "kernel32".}
|
stdcall, importc: "GetCPInfo", dynlib: "kernel32".}
|
||||||
|
|
||||||
proc nameToCodePage(name: string): TCodePage =
|
proc nameToCodePage(name: string): CodePage =
|
||||||
var nameAsInt: int
|
var nameAsInt: int
|
||||||
if parseInt(name, nameAsInt) == 0: nameAsInt = -1
|
if parseInt(name, nameAsInt) == 0: nameAsInt = -1
|
||||||
for no, na in items(winEncodings):
|
for no, na in items(winEncodings):
|
||||||
if no == nameAsInt or eqEncodingNames(na, name): return TCodePage(no)
|
if no == nameAsInt or eqEncodingNames(na, name): return CodePage(no)
|
||||||
result = TCodePage(-1)
|
result = CodePage(-1)
|
||||||
|
|
||||||
proc codePageToName(c: TCodePage): string =
|
proc codePageToName(c: CodePage): string =
|
||||||
for no, na in items(winEncodings):
|
for no, na in items(winEncodings):
|
||||||
if no == int(c):
|
if no == int(c):
|
||||||
return if na.len != 0: na else: $no
|
return if na.len != 0: na else: $no
|
||||||
result = ""
|
result = ""
|
||||||
|
|
||||||
proc getACP(): TCodePage {.stdcall, importc: "GetACP", dynlib: "kernel32".}
|
proc getACP(): CodePage {.stdcall, importc: "GetACP", dynlib: "kernel32".}
|
||||||
|
|
||||||
proc multiByteToWideChar(
|
proc multiByteToWideChar(
|
||||||
codePage: TCodePage,
|
codePage: CodePage,
|
||||||
dwFlags: int32,
|
dwFlags: int32,
|
||||||
lpMultiByteStr: cstring,
|
lpMultiByteStr: cstring,
|
||||||
cbMultiByte: cint,
|
cbMultiByte: cint,
|
||||||
|
|
@ -242,7 +244,7 @@ when defined(windows):
|
||||||
stdcall, importc: "MultiByteToWideChar", dynlib: "kernel32".}
|
stdcall, importc: "MultiByteToWideChar", dynlib: "kernel32".}
|
||||||
|
|
||||||
proc wideCharToMultiByte(
|
proc wideCharToMultiByte(
|
||||||
codePage: TCodePage,
|
codePage: CodePage,
|
||||||
dwFlags: int32,
|
dwFlags: int32,
|
||||||
lpWideCharStr: cstring,
|
lpWideCharStr: cstring,
|
||||||
cchWideChar: cint,
|
cchWideChar: cint,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2011 Alex Mitchell
|
# (c) Copyright 2011 Alex Mitchell
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -14,10 +14,10 @@
|
||||||
## it was inspired by Python's PyEE module. There are two ways you can use
|
## it was inspired by Python's PyEE module. There are two ways you can use
|
||||||
## events: one is a python-inspired way; the other is more of a C-style way.
|
## events: one is a python-inspired way; the other is more of a C-style way.
|
||||||
##
|
##
|
||||||
## .. code-block:: Nimrod
|
## .. code-block:: Nim
|
||||||
## var ee = initEventEmitter()
|
## var ee = initEventEmitter()
|
||||||
## var genericargs: TEventArgs
|
## var genericargs: EventArgs
|
||||||
## proc handleevent(e: TEventArgs) =
|
## proc handleevent(e: EventArgs) =
|
||||||
## echo("Handled!")
|
## echo("Handled!")
|
||||||
##
|
##
|
||||||
## # Python way
|
## # Python way
|
||||||
|
|
@ -27,21 +27,24 @@
|
||||||
## # C/Java way
|
## # C/Java way
|
||||||
## # Declare a type
|
## # Declare a type
|
||||||
## type
|
## type
|
||||||
## TSomeObject = object of TObject
|
## SomeObject = object of RootObj
|
||||||
## SomeEvent: TEventHandler
|
## SomeEvent: EventHandler
|
||||||
## var myobj: TSomeObject
|
## var myobj: SomeObject
|
||||||
## myobj.SomeEvent = initEventHandler("SomeEvent")
|
## myobj.SomeEvent = initEventHandler("SomeEvent")
|
||||||
## myobj.SomeEvent.addHandler(handleevent)
|
## myobj.SomeEvent.addHandler(handleevent)
|
||||||
## ee.emit(myobj.SomeEvent, genericargs)
|
## ee.emit(myobj.SomeEvent, genericargs)
|
||||||
|
|
||||||
type
|
type
|
||||||
TEventArgs* = object of TObject ## Base object for event arguments that are passed to callback functions.
|
EventArgs* = object of RootObj ## Base object for event arguments that are passed to callback functions.
|
||||||
TEventHandler* = tuple[name: string, handlers: seq[proc(e:TEventArgs) {.closure.}]] ## An eventhandler for an event.
|
EventHandler* = tuple[name: string, handlers: seq[proc(e: EventArgs) {.closure.}]] ## An eventhandler for an event.
|
||||||
|
|
||||||
type
|
type
|
||||||
TEventEmitter* = object {.pure, final.} ## An object that fires events and holds event handlers for an object.
|
EventEmitter* = object ## An object that fires events and holds event handlers for an object.
|
||||||
s: seq[TEventHandler]
|
s: seq[EventHandler]
|
||||||
EInvalidEvent* = object of EInvalidValue
|
EventError* = object of ValueError
|
||||||
|
|
||||||
|
{.deprecated: [TEventArgs: EventArgs, TEventHandler: EventHandler,
|
||||||
|
TEventEmitter: EventEmitter, EInvalidEvent: EventError].}
|
||||||
|
|
||||||
proc initEventHandler*(name: string): TEventHandler =
|
proc initEventHandler*(name: string): TEventHandler =
|
||||||
## Initializes an EventHandler with the specified name and returns it.
|
## Initializes an EventHandler with the specified name and returns it.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Dominik Picheta
|
# (c) Copyright 2012 Dominik Picheta
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -25,13 +25,13 @@ else:
|
||||||
import inotify, os, asyncio, tables
|
import inotify, os, asyncio, tables
|
||||||
|
|
||||||
type
|
type
|
||||||
PFSMonitor* = ref TFSMonitor
|
FSMonitor* = ref FSMonitorObj
|
||||||
TFSMonitor = object of TObject
|
FSMonitorObj = object of RootObj
|
||||||
fd: cint
|
fd: cint
|
||||||
handleEvent: proc (m: PFSMonitor, ev: TMonitorEvent) {.closure.}
|
handleEvent: proc (m: FSMonitor, ev: MonitorEvent) {.closure.}
|
||||||
targets: TTable[cint, string]
|
targets: TTable[cint, string]
|
||||||
|
|
||||||
TMonitorEventType* = enum ## Monitor event type
|
MonitorEventType* = enum ## Monitor event type
|
||||||
MonitorAccess, ## File was accessed.
|
MonitorAccess, ## File was accessed.
|
||||||
MonitorAttrib, ## Metadata changed.
|
MonitorAttrib, ## Metadata changed.
|
||||||
MonitorCloseWrite, ## Writtable file was closed.
|
MonitorCloseWrite, ## Writtable file was closed.
|
||||||
|
|
@ -45,8 +45,8 @@ type
|
||||||
MonitorOpen, ## File was opened.
|
MonitorOpen, ## File was opened.
|
||||||
MonitorAll ## Filter for all event types.
|
MonitorAll ## Filter for all event types.
|
||||||
|
|
||||||
TMonitorEvent* = object
|
MonitorEvent* = object
|
||||||
case kind*: TMonitorEventType ## Type of the event.
|
case kind*: MonitorEventType ## Type of the event.
|
||||||
of MonitorMoveSelf, MonitorMoved:
|
of MonitorMoveSelf, MonitorMoved:
|
||||||
oldPath*: string ## Old absolute location
|
oldPath*: string ## Old absolute location
|
||||||
newPath*: string ## New absolute location
|
newPath*: string ## New absolute location
|
||||||
|
|
@ -58,6 +58,9 @@ type
|
||||||
## watched.
|
## watched.
|
||||||
wd*: cint ## Watch descriptor.
|
wd*: cint ## Watch descriptor.
|
||||||
|
|
||||||
|
{.deprecated: [PFSMonitor: FSMonitor, TFSMonitor: FSMonitorObj,
|
||||||
|
TMonitorEventType: MonitorEventType, TMonitorEvent: MonitorEvent].}
|
||||||
|
|
||||||
const
|
const
|
||||||
MaxEvents = 100
|
MaxEvents = 100
|
||||||
|
|
||||||
|
|
@ -67,7 +70,7 @@ proc newMonitor*(): PFSMonitor =
|
||||||
result.targets = initTable[cint, string]()
|
result.targets = initTable[cint, string]()
|
||||||
result.fd = inotifyInit()
|
result.fd = inotifyInit()
|
||||||
if result.fd < 0:
|
if result.fd < 0:
|
||||||
OSError(OSLastError())
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
proc add*(monitor: PFSMonitor, target: string,
|
proc add*(monitor: PFSMonitor, target: string,
|
||||||
filters = {MonitorAll}): cint {.discardable.} =
|
filters = {MonitorAll}): cint {.discardable.} =
|
||||||
|
|
@ -101,7 +104,7 @@ proc del*(monitor: PFSMonitor, wd: cint) =
|
||||||
##
|
##
|
||||||
## If ``wd`` is not a part of ``monitor`` an EOS error is raised.
|
## If ``wd`` is not a part of ``monitor`` an EOS error is raised.
|
||||||
if inotifyRmWatch(monitor.fd, wd) < 0:
|
if inotifyRmWatch(monitor.fd, wd) < 0:
|
||||||
OSError(OSLastError())
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
proc getEvent(m: PFSMonitor, fd: cint): seq[TMonitorEvent] =
|
proc getEvent(m: PFSMonitor, fd: cint): seq[TMonitorEvent] =
|
||||||
result = @[]
|
result = @[]
|
||||||
|
|
@ -197,18 +200,19 @@ proc register*(d: PDispatcher, monitor: PFSMonitor,
|
||||||
d.register(deleg)
|
d.register(deleg)
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
var disp = newDispatcher()
|
proc main =
|
||||||
var monitor = newMonitor()
|
var disp = newDispatcher()
|
||||||
echo monitor.add("/home/dom/inotifytests/")
|
var monitor = newMonitor()
|
||||||
disp.register(monitor,
|
echo monitor.add("/home/dom/inotifytests/")
|
||||||
proc (m: PFSMonitor, ev: TMonitorEvent) =
|
disp.register(monitor,
|
||||||
echo("Got event: ", ev.kind)
|
proc (m: PFSMonitor, ev: TMonitorEvent) =
|
||||||
if ev.kind == MonitorMoved:
|
echo("Got event: ", ev.kind)
|
||||||
echo("From ", ev.oldPath, " to ", ev.newPath)
|
if ev.kind == MonitorMoved:
|
||||||
echo("Name is ", ev.name)
|
echo("From ", ev.oldPath, " to ", ev.newPath)
|
||||||
else:
|
echo("Name is ", ev.name)
|
||||||
echo("Name ", ev.name, " fullname ", ev.fullName))
|
else:
|
||||||
|
echo("Name ", ev.name, " fullname ", ev.fullName))
|
||||||
while true:
|
|
||||||
if not disp.poll(): break
|
|
||||||
|
|
||||||
|
while true:
|
||||||
|
if not disp.poll(): break
|
||||||
|
main()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Dominik Picheta
|
# (c) Copyright 2012 Dominik Picheta
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
@ -14,8 +14,8 @@ import sockets, strutils, parseutils, times, os, asyncio
|
||||||
## by `RFC 959 <http://tools.ietf.org/html/rfc959>`_.
|
## by `RFC 959 <http://tools.ietf.org/html/rfc959>`_.
|
||||||
##
|
##
|
||||||
## This module provides both a synchronous and asynchronous implementation.
|
## This module provides both a synchronous and asynchronous implementation.
|
||||||
## The asynchronous implementation requires you to use the ``AsyncFTPClient``
|
## The asynchronous implementation requires you to use the ``asyncFTPClient``
|
||||||
## function. You are then required to register the ``PAsyncFTPClient`` with a
|
## function. You are then required to register the ``AsyncFTPClient`` with a
|
||||||
## asyncio dispatcher using the ``register`` function. Take a look at the
|
## asyncio dispatcher using the ``register`` function. Take a look at the
|
||||||
## asyncio module documentation for more information.
|
## asyncio module documentation for more information.
|
||||||
##
|
##
|
||||||
|
|
@ -24,64 +24,63 @@ import sockets, strutils, parseutils, times, os, asyncio
|
||||||
##
|
##
|
||||||
## Here is some example usage of this module:
|
## Here is some example usage of this module:
|
||||||
##
|
##
|
||||||
## .. code-block:: Nimrod
|
## .. code-block:: Nim
|
||||||
## var ftp = FTPClient("example.org", user = "user", pass = "pass")
|
## var ftp = ftpClient("example.org", user = "user", pass = "pass")
|
||||||
## ftp.connect()
|
## ftp.connect()
|
||||||
## ftp.retrFile("file.ext", "file.ext")
|
## ftp.retrFile("file.ext", "file.ext")
|
||||||
##
|
##
|
||||||
## **Warning:** The API of this module is unstable, and therefore is subject
|
## **Warning:** The API of this module is unstable, and therefore is subject
|
||||||
## to change.
|
## to change.
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TFTPClient* = object of TObject
|
FTPClientObj* = object of RootObj
|
||||||
case isAsync: bool
|
case isAsync: bool
|
||||||
of false:
|
of false:
|
||||||
csock: TSocket # Command connection socket
|
csock: Socket # Command connection socket
|
||||||
dsock: TSocket # Data connection socket
|
dsock: Socket # Data connection socket
|
||||||
else:
|
else:
|
||||||
dummyA, dummyB: pointer # workaround a Nimrod API issue
|
dummyA, dummyB: pointer # workaround a Nimrod API issue
|
||||||
asyncCSock: PAsyncSocket
|
asyncCSock: AsyncSocket
|
||||||
asyncDSock: PAsyncSocket
|
asyncDSock: AsyncSocket
|
||||||
handleEvent*: proc (ftp: PAsyncFTPClient, ev: TFTPEvent){.closure,gcsafe.}
|
handleEvent*: proc (ftp: AsyncFTPClient, ev: FTPEvent){.closure,gcsafe.}
|
||||||
disp: PDispatcher
|
disp: Dispatcher
|
||||||
asyncDSockID: PDelegate
|
asyncDSockID: Delegate
|
||||||
user, pass: string
|
user, pass: string
|
||||||
address: string
|
address: string
|
||||||
port: TPort
|
port: Port
|
||||||
|
|
||||||
jobInProgress: bool
|
jobInProgress: bool
|
||||||
job: ref TFTPJob
|
job: ref FTPJob
|
||||||
|
|
||||||
dsockConnected: bool
|
dsockConnected: bool
|
||||||
|
|
||||||
PFTPClient* = ref TFTPClient
|
FTPClient* = ref FTPClientObj
|
||||||
|
|
||||||
FTPJobType* = enum
|
FTPJobType* = enum
|
||||||
JRetrText, JRetr, JStore
|
JRetrText, JRetr, JStore
|
||||||
|
|
||||||
TFTPJob = object
|
FTPJob = object
|
||||||
prc: proc (ftp: PFTPClient, async: bool): bool {.nimcall, gcsafe.}
|
prc: proc (ftp: PFTPClient, async: bool): bool {.nimcall, gcsafe.}
|
||||||
case typ*: FTPJobType
|
case typ*: FTPJobType
|
||||||
of JRetrText:
|
of JRetrText:
|
||||||
lines: string
|
lines: string
|
||||||
of JRetr, JStore:
|
of JRetr, JStore:
|
||||||
file: TFile
|
file: File
|
||||||
filename: string
|
filename: string
|
||||||
total: biggestInt # In bytes.
|
total: BiggestInt # In bytes.
|
||||||
progress: biggestInt # In bytes.
|
progress: BiggestInt # In bytes.
|
||||||
oneSecond: biggestInt # Bytes transferred in one second.
|
oneSecond: BiggestInt # Bytes transferred in one second.
|
||||||
lastProgressReport: float # Time
|
lastProgressReport: float # Time
|
||||||
toStore: string # Data left to upload (Only used with async)
|
toStore: string # Data left to upload (Only used with async)
|
||||||
else: nil
|
else: nil
|
||||||
|
|
||||||
PAsyncFTPClient* = ref TAsyncFTPClient ## Async alternative to TFTPClient.
|
AsyncFTPClient* = ref AsyncFTPClientObj ## Async alternative to TFTPClient.
|
||||||
TAsyncFTPClient* = object of TFTPClient
|
AsyncFTPClientObj* = object of FTPClientObj
|
||||||
|
|
||||||
FTPEventType* = enum
|
FTPEventType* = enum
|
||||||
EvTransferProgress, EvLines, EvRetr, EvStore
|
EvTransferProgress, EvLines, EvRetr, EvStore
|
||||||
|
|
||||||
TFTPEvent* = object ## Event
|
FTPEvent* = object ## Event
|
||||||
filename*: string
|
filename*: string
|
||||||
case typ*: FTPEventType
|
case typ*: FTPEventType
|
||||||
of EvLines:
|
of EvLines:
|
||||||
|
|
@ -89,13 +88,19 @@ type
|
||||||
of EvRetr, EvStore: ## Retr/Store operation finished.
|
of EvRetr, EvStore: ## Retr/Store operation finished.
|
||||||
nil
|
nil
|
||||||
of EvTransferProgress:
|
of EvTransferProgress:
|
||||||
bytesTotal*: biggestInt ## Bytes total.
|
bytesTotal*: BiggestInt ## Bytes total.
|
||||||
bytesFinished*: biggestInt ## Bytes transferred.
|
bytesFinished*: BiggestInt ## Bytes transferred.
|
||||||
speed*: biggestInt ## Speed in bytes/s
|
speed*: BiggestInt ## Speed in bytes/s
|
||||||
currentJob*: FTPJobType ## The current job being performed.
|
currentJob*: FTPJobType ## The current job being performed.
|
||||||
|
|
||||||
EInvalidReply* = object of ESynch
|
ReplyError* = object of IOError
|
||||||
EFTP* = object of ESynch
|
FTPError* = object of IOError
|
||||||
|
|
||||||
|
{.deprecated: [
|
||||||
|
TFTPClient: FTPClientObj, TFTPJob: FTPJob, PAsyncFTPClient: AsyncFTPClient,
|
||||||
|
TAsyncFTPClient: AsyncFTPClientObj, TFTPEvent: FTPEvent,
|
||||||
|
EInvalidReply: ReplyError, EFTP: FTPError
|
||||||
|
].}
|
||||||
|
|
||||||
proc ftpClient*(address: string, port = TPort(21),
|
proc ftpClient*(address: string, port = TPort(21),
|
||||||
user, pass = ""): PFTPClient =
|
user, pass = ""): PFTPClient =
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2014 Dominik Picheta
|
# (c) Copyright 2014 Dominik Picheta
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -12,6 +12,8 @@
|
||||||
## may be any Nimrod or user defined type. This module supports matching
|
## may be any Nimrod or user defined type. This module supports matching
|
||||||
## of keys in case-sensitive, case-insensitive and style-insensitive modes.
|
## of keys in case-sensitive, case-insensitive and style-insensitive modes.
|
||||||
|
|
||||||
|
{.deprecated.}
|
||||||
|
|
||||||
import
|
import
|
||||||
os, hashes, strutils
|
os, hashes, strutils
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -8,13 +8,13 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
## This module implements efficient computations of hash values for diverse
|
## This module implements efficient computations of hash values for diverse
|
||||||
## Nimrod types. All the procs are based on these two building blocks: the `!&
|
## Nim types. All the procs are based on these two building blocks: the `!&
|
||||||
## proc <#!&>`_ used to start or mix a hash value, and the `!$ proc <#!$>`_
|
## proc <#!&>`_ used to start or mix a hash value, and the `!$ proc <#!$>`_
|
||||||
## used to *finish* the hash value. If you want to implement hash procs for
|
## used to *finish* the hash value. If you want to implement hash procs for
|
||||||
## your custom types you will end up writing the following kind of skeleton of
|
## your custom types you will end up writing the following kind of skeleton of
|
||||||
## code:
|
## code:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: Nim
|
||||||
## proc hash(x: Something): THash =
|
## proc hash(x: Something): THash =
|
||||||
## ## Computes a THash from `x`.
|
## ## Computes a THash from `x`.
|
||||||
## var h: THash = 0
|
## var h: THash = 0
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
## like for example objects made up of ``strings``, you can simply hash
|
## like for example objects made up of ``strings``, you can simply hash
|
||||||
## together the hash value of the individual fields:
|
## together the hash value of the individual fields:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: Nim
|
||||||
## proc hash(x: Something): THash =
|
## proc hash(x: Something): THash =
|
||||||
## ## Computes a THash from `x`.
|
## ## Computes a THash from `x`.
|
||||||
## var h: THash = 0
|
## var h: THash = 0
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2014 Andreas Rumpf
|
# (c) Copyright 2014 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -18,13 +18,13 @@
|
||||||
##
|
##
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: Nim
|
||||||
## var nim = "Nimrod"
|
## var nim = "Nim"
|
||||||
## echo h1(a(href="http://nimrod-lang.org", nim))
|
## echo h1(a(href="http://nim-lang.org", nim))
|
||||||
##
|
##
|
||||||
## Writes the string::
|
## Writes the string::
|
||||||
##
|
##
|
||||||
## <h1><a href="http://nimrod-lang.org">Nimrod</a></h1>
|
## <h1><a href="http://nim-lang.org">Nim</a></h1>
|
||||||
##
|
##
|
||||||
|
|
||||||
import
|
import
|
||||||
|
|
@ -53,8 +53,7 @@ proc delete[T](s: var seq[T], attr: T): bool =
|
||||||
setLen(s, L-1)
|
setLen(s, L-1)
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc xmlCheckedTag*(e: PNimrodNode, tag: string,
|
proc xmlCheckedTag*(e: PNimrodNode, tag: string, optAttr = "", reqAttr = "",
|
||||||
optAttr = "", reqAttr = "",
|
|
||||||
isLeaf = false): PNimrodNode {.compileTime.} =
|
isLeaf = false): PNimrodNode {.compileTime.} =
|
||||||
## use this procedure to define a new XML tag
|
## use this procedure to define a new XML tag
|
||||||
|
|
||||||
|
|
@ -484,7 +483,7 @@ macro `var`*(e: expr): expr {.immediate.} =
|
||||||
result = xmlCheckedTag(e, "var", commonAttr)
|
result = xmlCheckedTag(e, "var", commonAttr)
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
var nim = "Nimrod"
|
var nim = "Nim"
|
||||||
echo h1(a(href="http://nimrod-code.org", nim))
|
echo h1(a(href="http://nim-lang.org", nim))
|
||||||
echo form(action="test", `accept-charset` = "Content-Type")
|
echo form(action="test", `accept-charset` = "Content-Type")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2013 Andreas Rumpf
|
# (c) Copyright 2013 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
## It can be used to parse a wild HTML document and output it as valid XHTML
|
## It can be used to parse a wild HTML document and output it as valid XHTML
|
||||||
## document (well, if you are lucky):
|
## document (well, if you are lucky):
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: Nim
|
||||||
##
|
##
|
||||||
## echo loadHtml("mydirty.html")
|
## echo loadHtml("mydirty.html")
|
||||||
##
|
##
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
## and write back the modified version. In this case we look for hyperlinks
|
## and write back the modified version. In this case we look for hyperlinks
|
||||||
## ending with the extension ``.rst`` and convert them to ``.html``.
|
## ending with the extension ``.rst`` and convert them to ``.html``.
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: Nim
|
||||||
##
|
##
|
||||||
## import htmlparser
|
## import htmlparser
|
||||||
## import xmltree # To use '$' for PXmlNode
|
## import xmltree # To use '$' for PXmlNode
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2010 Dominik Picheta, Andreas Rumpf
|
# (c) Copyright 2010 Dominik Picheta, Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
## This example uses HTTP GET to retrieve
|
## This example uses HTTP GET to retrieve
|
||||||
## ``http://google.com``
|
## ``http://google.com``
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: Nim
|
||||||
## echo(getContent("http://google.com"))
|
## echo(getContent("http://google.com"))
|
||||||
##
|
##
|
||||||
## Using HTTP POST
|
## Using HTTP POST
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
## uses ``multipart/form-data`` as the ``Content-Type`` to send the HTML to
|
## uses ``multipart/form-data`` as the ``Content-Type`` to send the HTML to
|
||||||
## the server.
|
## the server.
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: Nim
|
||||||
## var headers: string = "Content-Type: multipart/form-data; boundary=xyz\c\L"
|
## var headers: string = "Content-Type: multipart/form-data; boundary=xyz\c\L"
|
||||||
## var body: string = "--xyz\c\L"
|
## var body: string = "--xyz\c\L"
|
||||||
## # soap 1.2 output
|
## # soap 1.2 output
|
||||||
|
|
@ -54,7 +54,7 @@
|
||||||
## on many operating systems. httpclient will use SSL automatically if you give
|
## on many operating systems. httpclient will use SSL automatically if you give
|
||||||
## any of the functions a url with the ``https`` schema, for example:
|
## any of the functions a url with the ``https`` schema, for example:
|
||||||
## ``https://github.com/``, you also have to compile with ``ssl`` defined like so:
|
## ``https://github.com/``, you also have to compile with ``ssl`` defined like so:
|
||||||
## ``nimrod c -d:ssl ...``.
|
## ``nim c -d:ssl ...``.
|
||||||
##
|
##
|
||||||
## Timeouts
|
## Timeouts
|
||||||
## ========
|
## ========
|
||||||
|
|
@ -80,34 +80,38 @@ import asyncnet, asyncdispatch
|
||||||
import rawsockets
|
import rawsockets
|
||||||
|
|
||||||
type
|
type
|
||||||
TResponse* = tuple[
|
Response* = tuple[
|
||||||
version: string,
|
version: string,
|
||||||
status: string,
|
status: string,
|
||||||
headers: PStringTable,
|
headers: StringTableRef,
|
||||||
body: string]
|
body: string]
|
||||||
|
|
||||||
PProxy* = ref object
|
Proxy* = ref object
|
||||||
url*: TUrl
|
url*: TUrl
|
||||||
auth*: string
|
auth*: string
|
||||||
|
|
||||||
EInvalidProtocol* = object of ESynch ## exception that is raised when server
|
ProtocolError* = object of IOError ## exception that is raised when server
|
||||||
## does not conform to the implemented
|
## does not conform to the implemented
|
||||||
## protocol
|
## protocol
|
||||||
|
|
||||||
EHttpRequestErr* = object of ESynch ## Thrown in the ``getContent`` proc
|
HttpRequestError* = object of IOError ## Thrown in the ``getContent`` proc
|
||||||
## and ``postContent`` proc,
|
## and ``postContent`` proc,
|
||||||
## when the server returns an error
|
## when the server returns an error
|
||||||
|
|
||||||
const defUserAgent* = "Nimrod httpclient/0.1"
|
{.deprecated: [TResponse: Response, PProxy: Proxy,
|
||||||
|
EInvalidProtocol: ProtocolError, EHttpRequestErr: HttpRequestError
|
||||||
|
].}
|
||||||
|
|
||||||
|
const defUserAgent* = "Nim httpclient/0.1"
|
||||||
|
|
||||||
proc httpError(msg: string) =
|
proc httpError(msg: string) =
|
||||||
var e: ref EInvalidProtocol
|
var e: ref ProtocolError
|
||||||
new(e)
|
new(e)
|
||||||
e.msg = msg
|
e.msg = msg
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
proc fileError(msg: string) =
|
proc fileError(msg: string) =
|
||||||
var e: ref EIO
|
var e: ref IOError
|
||||||
new(e)
|
new(e)
|
||||||
e.msg = msg
|
e.msg = msg
|
||||||
raise e
|
raise e
|
||||||
|
|
@ -232,7 +236,7 @@ proc parseResponse(s: TSocket, getBody: bool, timeout: int): TResponse =
|
||||||
result.body = ""
|
result.body = ""
|
||||||
|
|
||||||
type
|
type
|
||||||
THttpMethod* = enum ## the requested HttpMethod
|
HttpMethod* = enum ## the requested HttpMethod
|
||||||
httpHEAD, ## Asks for the response identical to the one that would
|
httpHEAD, ## Asks for the response identical to the one that would
|
||||||
## correspond to a GET request, but without the response
|
## correspond to a GET request, but without the response
|
||||||
## body.
|
## body.
|
||||||
|
|
@ -250,6 +254,8 @@ type
|
||||||
httpCONNECT ## Converts the request connection to a transparent
|
httpCONNECT ## Converts the request connection to a transparent
|
||||||
## TCP/IP tunnel, usually used for proxies.
|
## TCP/IP tunnel, usually used for proxies.
|
||||||
|
|
||||||
|
{.deprecated: [THttpMethod: HttpMethod].}
|
||||||
|
|
||||||
when not defined(ssl):
|
when not defined(ssl):
|
||||||
type PSSLContext = ref object
|
type PSSLContext = ref object
|
||||||
let defaultSSLContext: PSSLContext = nil
|
let defaultSSLContext: PSSLContext = nil
|
||||||
|
|
@ -288,7 +294,7 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
|
||||||
add(headers, "\c\L")
|
add(headers, "\c\L")
|
||||||
|
|
||||||
var s = socket()
|
var s = socket()
|
||||||
if s == InvalidSocket: osError(osLastError())
|
if s == InvalidSocket: raiseOSError(osLastError())
|
||||||
var port = sockets.TPort(80)
|
var port = sockets.TPort(80)
|
||||||
if r.scheme == "https":
|
if r.scheme == "https":
|
||||||
when defined(ssl):
|
when defined(ssl):
|
||||||
|
|
@ -431,16 +437,18 @@ proc generateHeaders(r: TURL, httpMethod: THttpMethod,
|
||||||
add(result, "\c\L")
|
add(result, "\c\L")
|
||||||
|
|
||||||
type
|
type
|
||||||
PAsyncHttpClient* = ref object
|
AsyncHttpClient* = ref object
|
||||||
socket: PAsyncSocket
|
socket: AsyncSocket
|
||||||
connected: bool
|
connected: bool
|
||||||
currentURL: TURL ## Where we are currently connected.
|
currentURL: TURL ## Where we are currently connected.
|
||||||
headers: PStringTable
|
headers: StringTableRef
|
||||||
maxRedirects: int
|
maxRedirects: int
|
||||||
userAgent: string
|
userAgent: string
|
||||||
|
|
||||||
|
{.deprecated: [PAsyncHttpClient: AsyncHttpClient].}
|
||||||
|
|
||||||
proc newAsyncHttpClient*(userAgent = defUserAgent,
|
proc newAsyncHttpClient*(userAgent = defUserAgent,
|
||||||
maxRedirects = 5): PAsyncHttpClient =
|
maxRedirects = 5): AsyncHttpClient =
|
||||||
## Creates a new PAsyncHttpClient instance.
|
## Creates a new PAsyncHttpClient instance.
|
||||||
##
|
##
|
||||||
## ``userAgent`` specifies the user agent that will be used when making
|
## ``userAgent`` specifies the user agent that will be used when making
|
||||||
|
|
@ -453,7 +461,7 @@ proc newAsyncHttpClient*(userAgent = defUserAgent,
|
||||||
result.userAgent = defUserAgent
|
result.userAgent = defUserAgent
|
||||||
result.maxRedirects = maxRedirects
|
result.maxRedirects = maxRedirects
|
||||||
|
|
||||||
proc close*(client: PAsyncHttpClient) =
|
proc close*(client: AsyncHttpClient) =
|
||||||
## Closes any connections held by the HTTP client.
|
## Closes any connections held by the HTTP client.
|
||||||
if client.connected:
|
if client.connected:
|
||||||
client.socket.close()
|
client.socket.close()
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,7 @@ when false:
|
||||||
var buf = alloc(contentLength)
|
var buf = alloc(contentLength)
|
||||||
if recv(client, buf, contentLength) != contentLength:
|
if recv(client, buf, contentLength) != contentLength:
|
||||||
dealloc(buf)
|
dealloc(buf)
|
||||||
OSError()
|
raiseOSError()
|
||||||
var inp = process.inputStream
|
var inp = process.inputStream
|
||||||
inp.writeData(buf, contentLength)
|
inp.writeData(buf, contentLength)
|
||||||
dealloc(buf)
|
dealloc(buf)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf, Dominik Picheta
|
# (c) Copyright 2014 Andreas Rumpf, Dominik Picheta
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
@ -35,7 +35,7 @@ import
|
||||||
hashes, strutils, lexbase, streams, unicode
|
hashes, strutils, lexbase, streams, unicode
|
||||||
|
|
||||||
type
|
type
|
||||||
TJsonEventKind* = enum ## enumeration of all events that may occur when parsing
|
JsonEventKind* = enum ## enumeration of all events that may occur when parsing
|
||||||
jsonError, ## an error ocurred during parsing
|
jsonError, ## an error ocurred during parsing
|
||||||
jsonEof, ## end of file reached
|
jsonEof, ## end of file reached
|
||||||
jsonString, ## a string literal
|
jsonString, ## a string literal
|
||||||
|
|
@ -65,7 +65,7 @@ type
|
||||||
tkColon,
|
tkColon,
|
||||||
tkComma
|
tkComma
|
||||||
|
|
||||||
TJsonError* = enum ## enumeration that lists all errors that can occur
|
JsonError* = enum ## enumeration that lists all errors that can occur
|
||||||
errNone, ## no error
|
errNone, ## no error
|
||||||
errInvalidToken, ## invalid token
|
errInvalidToken, ## invalid token
|
||||||
errStringExpected, ## string expected
|
errStringExpected, ## string expected
|
||||||
|
|
@ -78,20 +78,23 @@ type
|
||||||
errEofExpected, ## EOF expected
|
errEofExpected, ## EOF expected
|
||||||
errExprExpected ## expr expected
|
errExprExpected ## expr expected
|
||||||
|
|
||||||
TParserState = enum
|
ParserState = enum
|
||||||
stateEof, stateStart, stateObject, stateArray, stateExpectArrayComma,
|
stateEof, stateStart, stateObject, stateArray, stateExpectArrayComma,
|
||||||
stateExpectObjectComma, stateExpectColon, stateExpectValue
|
stateExpectObjectComma, stateExpectColon, stateExpectValue
|
||||||
|
|
||||||
TJsonParser* = object of TBaseLexer ## the parser object.
|
JsonParser* = object of TBaseLexer ## the parser object.
|
||||||
a: string
|
a: string
|
||||||
tok: TTokKind
|
tok: TTokKind
|
||||||
kind: TJsonEventKind
|
kind: JsonEventKind
|
||||||
err: TJsonError
|
err: JsonError
|
||||||
state: seq[TParserState]
|
state: seq[ParserState]
|
||||||
filename: string
|
filename: string
|
||||||
|
|
||||||
|
{.deprecated: [TJsonEventKind: JsonEventKind, TJsonError: JsonError,
|
||||||
|
TJsonParser: JsonParser].}
|
||||||
|
|
||||||
const
|
const
|
||||||
errorMessages: array [TJsonError, string] = [
|
errorMessages: array [JsonError, string] = [
|
||||||
"no error",
|
"no error",
|
||||||
"invalid token",
|
"invalid token",
|
||||||
"string expected",
|
"string expected",
|
||||||
|
|
@ -116,7 +119,7 @@ const
|
||||||
"{", "}", "[", "]", ":", ","
|
"{", "}", "[", "]", ":", ","
|
||||||
]
|
]
|
||||||
|
|
||||||
proc open*(my: var TJsonParser, input: PStream, filename: string) =
|
proc open*(my: var JsonParser, input: PStream, filename: string) =
|
||||||
## initializes the parser with an input stream. `Filename` is only used
|
## initializes the parser with an input stream. `Filename` is only used
|
||||||
## for nice error messages.
|
## for nice error messages.
|
||||||
lexbase.open(my, input)
|
lexbase.open(my, input)
|
||||||
|
|
@ -125,49 +128,49 @@ proc open*(my: var TJsonParser, input: PStream, filename: string) =
|
||||||
my.kind = jsonError
|
my.kind = jsonError
|
||||||
my.a = ""
|
my.a = ""
|
||||||
|
|
||||||
proc close*(my: var TJsonParser) {.inline.} =
|
proc close*(my: var JsonParser) {.inline.} =
|
||||||
## closes the parser `my` and its associated input stream.
|
## closes the parser `my` and its associated input stream.
|
||||||
lexbase.close(my)
|
lexbase.close(my)
|
||||||
|
|
||||||
proc str*(my: TJsonParser): string {.inline.} =
|
proc str*(my: JsonParser): string {.inline.} =
|
||||||
## returns the character data for the events: ``jsonInt``, ``jsonFloat``,
|
## returns the character data for the events: ``jsonInt``, ``jsonFloat``,
|
||||||
## ``jsonString``
|
## ``jsonString``
|
||||||
assert(my.kind in {jsonInt, jsonFloat, jsonString})
|
assert(my.kind in {jsonInt, jsonFloat, jsonString})
|
||||||
return my.a
|
return my.a
|
||||||
|
|
||||||
proc getInt*(my: TJsonParser): BiggestInt {.inline.} =
|
proc getInt*(my: JsonParser): BiggestInt {.inline.} =
|
||||||
## returns the number for the event: ``jsonInt``
|
## returns the number for the event: ``jsonInt``
|
||||||
assert(my.kind == jsonInt)
|
assert(my.kind == jsonInt)
|
||||||
return parseBiggestInt(my.a)
|
return parseBiggestInt(my.a)
|
||||||
|
|
||||||
proc getFloat*(my: TJsonParser): float {.inline.} =
|
proc getFloat*(my: JsonParser): float {.inline.} =
|
||||||
## returns the number for the event: ``jsonFloat``
|
## returns the number for the event: ``jsonFloat``
|
||||||
assert(my.kind == jsonFloat)
|
assert(my.kind == jsonFloat)
|
||||||
return parseFloat(my.a)
|
return parseFloat(my.a)
|
||||||
|
|
||||||
proc kind*(my: TJsonParser): TJsonEventKind {.inline.} =
|
proc kind*(my: JsonParser): JsonEventKind {.inline.} =
|
||||||
## returns the current event type for the JSON parser
|
## returns the current event type for the JSON parser
|
||||||
return my.kind
|
return my.kind
|
||||||
|
|
||||||
proc getColumn*(my: TJsonParser): int {.inline.} =
|
proc getColumn*(my: JsonParser): int {.inline.} =
|
||||||
## get the current column the parser has arrived at.
|
## get the current column the parser has arrived at.
|
||||||
result = getColNumber(my, my.bufpos)
|
result = getColNumber(my, my.bufpos)
|
||||||
|
|
||||||
proc getLine*(my: TJsonParser): int {.inline.} =
|
proc getLine*(my: JsonParser): int {.inline.} =
|
||||||
## get the current line the parser has arrived at.
|
## get the current line the parser has arrived at.
|
||||||
result = my.lineNumber
|
result = my.lineNumber
|
||||||
|
|
||||||
proc getFilename*(my: TJsonParser): string {.inline.} =
|
proc getFilename*(my: JsonParser): string {.inline.} =
|
||||||
## get the filename of the file that the parser processes.
|
## get the filename of the file that the parser processes.
|
||||||
result = my.filename
|
result = my.filename
|
||||||
|
|
||||||
proc errorMsg*(my: TJsonParser): string =
|
proc errorMsg*(my: JsonParser): string =
|
||||||
## returns a helpful error message for the event ``jsonError``
|
## returns a helpful error message for the event ``jsonError``
|
||||||
assert(my.kind == jsonError)
|
assert(my.kind == jsonError)
|
||||||
result = "$1($2, $3) Error: $4" % [
|
result = "$1($2, $3) Error: $4" % [
|
||||||
my.filename, $getLine(my), $getColumn(my), errorMessages[my.err]]
|
my.filename, $getLine(my), $getColumn(my), errorMessages[my.err]]
|
||||||
|
|
||||||
proc errorMsgExpected*(my: TJsonParser, e: string): string =
|
proc errorMsgExpected*(my: JsonParser, e: string): string =
|
||||||
## returns an error message "`e` expected" in the same format as the
|
## returns an error message "`e` expected" in the same format as the
|
||||||
## other error messages
|
## other error messages
|
||||||
result = "$1($2, $3) Error: $4" % [
|
result = "$1($2, $3) Error: $4" % [
|
||||||
|
|
@ -181,7 +184,7 @@ proc handleHexChar(c: char, x: var int): bool =
|
||||||
of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
|
of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
|
||||||
else: result = false # error
|
else: result = false # error
|
||||||
|
|
||||||
proc parseString(my: var TJsonParser): TTokKind =
|
proc parseString(my: var JsonParser): TTokKind =
|
||||||
result = tkString
|
result = tkString
|
||||||
var pos = my.bufpos + 1
|
var pos = my.bufpos + 1
|
||||||
var buf = my.buf
|
var buf = my.buf
|
||||||
|
|
@ -239,7 +242,7 @@ proc parseString(my: var TJsonParser): TTokKind =
|
||||||
inc(pos)
|
inc(pos)
|
||||||
my.bufpos = pos # store back
|
my.bufpos = pos # store back
|
||||||
|
|
||||||
proc skip(my: var TJsonParser) =
|
proc skip(my: var JsonParser) =
|
||||||
var pos = my.bufpos
|
var pos = my.bufpos
|
||||||
var buf = my.buf
|
var buf = my.buf
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -297,7 +300,7 @@ proc skip(my: var TJsonParser) =
|
||||||
break
|
break
|
||||||
my.bufpos = pos
|
my.bufpos = pos
|
||||||
|
|
||||||
proc parseNumber(my: var TJsonParser) =
|
proc parseNumber(my: var JsonParser) =
|
||||||
var pos = my.bufpos
|
var pos = my.bufpos
|
||||||
var buf = my.buf
|
var buf = my.buf
|
||||||
if buf[pos] == '-':
|
if buf[pos] == '-':
|
||||||
|
|
@ -328,7 +331,7 @@ proc parseNumber(my: var TJsonParser) =
|
||||||
inc(pos)
|
inc(pos)
|
||||||
my.bufpos = pos
|
my.bufpos = pos
|
||||||
|
|
||||||
proc parseName(my: var TJsonParser) =
|
proc parseName(my: var JsonParser) =
|
||||||
var pos = my.bufpos
|
var pos = my.bufpos
|
||||||
var buf = my.buf
|
var buf = my.buf
|
||||||
if buf[pos] in IdentStartChars:
|
if buf[pos] in IdentStartChars:
|
||||||
|
|
@ -337,7 +340,7 @@ proc parseName(my: var TJsonParser) =
|
||||||
inc(pos)
|
inc(pos)
|
||||||
my.bufpos = pos
|
my.bufpos = pos
|
||||||
|
|
||||||
proc getTok(my: var TJsonParser): TTokKind =
|
proc getTok(my: var JsonParser): TTokKind =
|
||||||
setLen(my.a, 0)
|
setLen(my.a, 0)
|
||||||
skip(my) # skip whitespace, comments
|
skip(my) # skip whitespace, comments
|
||||||
case my.buf[my.bufpos]
|
case my.buf[my.bufpos]
|
||||||
|
|
@ -381,7 +384,7 @@ proc getTok(my: var TJsonParser): TTokKind =
|
||||||
result = tkError
|
result = tkError
|
||||||
my.tok = result
|
my.tok = result
|
||||||
|
|
||||||
proc next*(my: var TJsonParser) =
|
proc next*(my: var JsonParser) =
|
||||||
## retrieves the first/next event. This controls the parser.
|
## retrieves the first/next event. This controls the parser.
|
||||||
var tk = getTok(my)
|
var tk = getTok(my)
|
||||||
var i = my.state.len-1
|
var i = my.state.len-1
|
||||||
|
|
@ -529,7 +532,10 @@ type
|
||||||
of JArray:
|
of JArray:
|
||||||
elems*: seq[PJsonNode]
|
elems*: seq[PJsonNode]
|
||||||
|
|
||||||
EJsonParsingError* = object of ValueError ## is raised for a JSON error
|
JsonParsingError* = object of ValueError ## is raised for a JSON error
|
||||||
|
|
||||||
|
{.deprecated: [EJsonParsingError: JsonParsingError, TJsonNode: JsonNodeObj,
|
||||||
|
PJsonNode: JsonNode, TJsonNodeKind: JsonNodeKind].}
|
||||||
|
|
||||||
proc raiseParseErr*(p: TJsonParser, msg: string) {.noinline, noreturn.} =
|
proc raiseParseErr*(p: TJsonParser, msg: string) {.noinline, noreturn.} =
|
||||||
## raises an `EJsonParsingError` exception.
|
## raises an `EJsonParsingError` exception.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# The Nimrod Compiler
|
# The Nim Compiler
|
||||||
# (c) Copyright 2009 Andreas Rumpf
|
# (c) Copyright 2009 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -36,6 +36,8 @@ type
|
||||||
lineStart: int # index of last line start in buffer
|
lineStart: int # index of last line start in buffer
|
||||||
fileOpened: bool
|
fileOpened: bool
|
||||||
|
|
||||||
|
{.deprecated: [TBaseLexer: BaseLexer].}
|
||||||
|
|
||||||
proc open*(L: var TBaseLexer, input: PStream, bufLen: int = 8192)
|
proc open*(L: var TBaseLexer, input: PStream, bufLen: int = 8192)
|
||||||
## inits the TBaseLexer with a stream to read from
|
## inits the TBaseLexer with a stream to read from
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2014 Andreas Rumpf, Dominik Picheta
|
# (c) Copyright 2014 Andreas Rumpf, Dominik Picheta
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
## The following example demonstrates logging to three different handlers
|
## The following example demonstrates logging to three different handlers
|
||||||
## simultaneously:
|
## simultaneously:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
##
|
##
|
||||||
## var L = newConsoleLogger()
|
## var L = newConsoleLogger()
|
||||||
## var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
|
## var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
|
||||||
|
|
@ -42,7 +42,7 @@
|
||||||
import strutils, os, times
|
import strutils, os, times
|
||||||
|
|
||||||
type
|
type
|
||||||
TLevel* = enum ## logging level
|
Level* = enum ## logging level
|
||||||
lvlAll, ## all levels active
|
lvlAll, ## all levels active
|
||||||
lvlDebug, ## debug level (and any above) active
|
lvlDebug, ## debug level (and any above) active
|
||||||
lvlInfo, ## info level (and any above) active
|
lvlInfo, ## info level (and any above) active
|
||||||
|
|
@ -52,7 +52,7 @@ type
|
||||||
lvlNone ## no levels active
|
lvlNone ## no levels active
|
||||||
|
|
||||||
const
|
const
|
||||||
LevelNames*: array [TLevel, string] = [
|
LevelNames*: array [Level, string] = [
|
||||||
"DEBUG", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "NONE"
|
"DEBUG", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "NONE"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -60,26 +60,29 @@ const
|
||||||
verboseFmtStr* = "$date $time "
|
verboseFmtStr* = "$date $time "
|
||||||
|
|
||||||
type
|
type
|
||||||
PLogger* = ref object of PObject ## abstract logger; the base type of all loggers
|
Logger* = ref object of RootObj ## abstract logger; the base type of all loggers
|
||||||
levelThreshold*: TLevel ## only messages of level >= levelThreshold
|
levelThreshold*: Leve l ## only messages of level >= levelThreshold
|
||||||
## should be processed
|
## should be processed
|
||||||
fmtStr: string ## = defaultFmtStr by default, see substituteLog for $date etc.
|
fmtStr: string ## = defaultFmtStr by default, see substituteLog for $date etc.
|
||||||
|
|
||||||
PConsoleLogger* = ref object of PLogger ## logger that writes the messages to the
|
ConsoleLogger* = ref object of Logger ## logger that writes the messages to the
|
||||||
## console
|
## console
|
||||||
|
|
||||||
PFileLogger* = ref object of PLogger ## logger that writes the messages to a file
|
FileLogger* = ref object of Logger ## logger that writes the messages to a file
|
||||||
f: TFile
|
f: TFile
|
||||||
|
|
||||||
PRollingFileLogger* = ref object of PFileLogger ## logger that writes the
|
RollingFileLogger* = ref object of FileLogger ## logger that writes the
|
||||||
## messages to a file and
|
## messages to a file and
|
||||||
## performs log rotation
|
## performs log rotation
|
||||||
maxLines: int # maximum number of lines
|
maxLines: int # maximum number of lines
|
||||||
curLine : int
|
curLine : int
|
||||||
baseName: string # initial filename
|
baseName: string # initial filename
|
||||||
baseMode: TFileMode # initial file mode
|
baseMode: FileMode # initial file mode
|
||||||
logFiles: int # how many log files already created, e.g. basename.1, basename.2...
|
logFiles: int # how many log files already created, e.g. basename.1, basename.2...
|
||||||
|
|
||||||
|
{.deprecated: [TLevel: Level, PLogger: Logger, PConsoleLogger: ConsoleLogger,
|
||||||
|
PFileLogger: FileLogger, PRollingFileLogger: RollingFileLogger].}
|
||||||
|
|
||||||
proc substituteLog(frmt: string): string =
|
proc substituteLog(frmt: string): string =
|
||||||
## converts $date to the current date
|
## converts $date to the current date
|
||||||
## converts $time to the current time
|
## converts $time to the current time
|
||||||
|
|
@ -105,7 +108,7 @@ proc substituteLog(frmt: string): string =
|
||||||
of "appdir": result.add(app.splitFile.dir)
|
of "appdir": result.add(app.splitFile.dir)
|
||||||
of "appname": result.add(app.splitFile.name)
|
of "appname": result.add(app.splitFile.name)
|
||||||
|
|
||||||
method log*(logger: PLogger, level: TLevel,
|
method log*(logger: Logger, level: Level,
|
||||||
frmt: string, args: varargs[string, `$`]) {.raises: [EBase], tags: [FTime, FWriteIO, FReadIO].} =
|
frmt: string, args: varargs[string, `$`]) {.raises: [EBase], tags: [FTime, FWriteIO, FReadIO].} =
|
||||||
## Override this method in custom loggers. Default implementation does
|
## Override this method in custom loggers. Default implementation does
|
||||||
## nothing.
|
## nothing.
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2013 Andreas Rumpf
|
# (c) Copyright 2014 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## This module contains procs for serialization and deseralization of
|
## This module contains procs for serialization and deseralization of
|
||||||
## arbitrary Nimrod data structures. The serialization format uses JSON.
|
## arbitrary Nim data structures. The serialization format uses JSON.
|
||||||
##
|
##
|
||||||
## **Restriction**: For objects their type is **not** serialized. This means
|
## **Restriction**: For objects their type is **not** serialized. This means
|
||||||
## essentially that it does not work if the object has some other runtime
|
## essentially that it does not work if the object has some other runtime
|
||||||
## type than its compiletime type:
|
## type than its compiletime type:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
##
|
##
|
||||||
## type
|
## type
|
||||||
## TA = object
|
## TA = object
|
||||||
|
|
@ -211,7 +211,7 @@ proc loadAny(p: var TJsonParser, a: TAny, t: var TTable[biggestInt, pointer]) =
|
||||||
raiseParseErr(p, "float expected")
|
raiseParseErr(p, "float expected")
|
||||||
of akRange: loadAny(p, a.skipRange, t)
|
of akRange: loadAny(p, a.skipRange, t)
|
||||||
|
|
||||||
proc loadAny(s: PStream, a: TAny, t: var TTable[biggestInt, pointer]) =
|
proc loadAny(s: PStream, a: TAny, t: var TTable[BiggestInt, pointer]) =
|
||||||
var p: TJsonParser
|
var p: TJsonParser
|
||||||
open(p, s, "unknown file")
|
open(p, s, "unknown file")
|
||||||
next(p)
|
next(p)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2014 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
@ -44,7 +44,7 @@ proc validEmailAddress*(s: string): bool {.noSideEffect,
|
||||||
"aero", "jobs", "museum": return true
|
"aero", "jobs", "museum": return true
|
||||||
return false
|
return false
|
||||||
|
|
||||||
proc parseInt*(s: string, value: var int, validRange: TSlice[int]) {.
|
proc parseInt*(s: string, value: var int, validRange: Slice[int]) {.
|
||||||
noSideEffect, rtl, extern: "nmatchParseInt".} =
|
noSideEffect, rtl, extern: "nmatchParseInt".} =
|
||||||
## parses `s` into an integer in the range `validRange`. If successful,
|
## parses `s` into an integer in the range `validRange`. If successful,
|
||||||
## `value` is modified to contain the result. Otherwise no exception is
|
## `value` is modified to contain the result. Otherwise no exception is
|
||||||
|
|
@ -53,7 +53,7 @@ proc parseInt*(s: string, value: var int, validRange: TSlice[int]) {.
|
||||||
var x = value
|
var x = value
|
||||||
try:
|
try:
|
||||||
discard parseutils.parseInt(s, x, 0)
|
discard parseutils.parseInt(s, x, 0)
|
||||||
except EOverflow:
|
except OverflowError:
|
||||||
discard
|
discard
|
||||||
if x in validRange: value = x
|
if x in validRange: value = x
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2014 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
## Constructive mathematics is naturally typed. -- Simon Thompson
|
## Constructive mathematics is naturally typed. -- Simon Thompson
|
||||||
##
|
##
|
||||||
## Basic math routines for Nimrod.
|
## Basic math routines for Nim.
|
||||||
## This module is available for the `JavaScript target
|
## This module is available for the `JavaScript target
|
||||||
## <backends.html#the-javascript-target>`_.
|
## <backends.html#the-javascript-target>`_.
|
||||||
|
|
||||||
|
|
@ -41,8 +41,8 @@ const
|
||||||
## for Nimrod's ``float`` type.
|
## for Nimrod's ``float`` type.
|
||||||
|
|
||||||
type
|
type
|
||||||
TFloatClass* = enum ## describes the class a floating point value belongs to.
|
FloatClass* = enum ## describes the class a floating point value belongs to.
|
||||||
## This is the type that is returned by `classify`.
|
## This is the type that is returned by `classify`.
|
||||||
fcNormal, ## value is an ordinary nonzero floating point value
|
fcNormal, ## value is an ordinary nonzero floating point value
|
||||||
fcSubnormal, ## value is a subnormal (a very small) floating point value
|
fcSubnormal, ## value is a subnormal (a very small) floating point value
|
||||||
fcZero, ## value is zero
|
fcZero, ## value is zero
|
||||||
|
|
@ -51,9 +51,9 @@ type
|
||||||
fcInf, ## value is positive infinity
|
fcInf, ## value is positive infinity
|
||||||
fcNegInf ## value is negative infinity
|
fcNegInf ## value is negative infinity
|
||||||
|
|
||||||
proc classify*(x: float): TFloatClass =
|
proc classify*(x: float): FloatClass =
|
||||||
## classifies a floating point value. Returns `x`'s class as specified by
|
## classifies a floating point value. Returns `x`'s class as specified by
|
||||||
## `TFloatClass`.
|
## `FloatClass`.
|
||||||
|
|
||||||
# JavaScript and most C compilers have no classify:
|
# JavaScript and most C compilers have no classify:
|
||||||
if x == 0.0:
|
if x == 0.0:
|
||||||
|
|
@ -287,12 +287,14 @@ proc random[T](a: openArray[T]): T =
|
||||||
result = a[random(a.low..a.len)]
|
result = a[random(a.low..a.len)]
|
||||||
|
|
||||||
type
|
type
|
||||||
TRunningStat* {.pure,final.} = object ## an accumulator for statistical data
|
RunningStat* = object ## an accumulator for statistical data
|
||||||
n*: int ## number of pushed data
|
n*: int ## number of pushed data
|
||||||
sum*, min*, max*, mean*: float ## self-explaining
|
sum*, min*, max*, mean*: float ## self-explaining
|
||||||
oldM, oldS, newS: float
|
oldM, oldS, newS: float
|
||||||
|
|
||||||
proc push*(s: var TRunningStat, x: float) =
|
{.deprecated: [TFloatClass: FloatClass, TRunningStat: RunningStat].}
|
||||||
|
|
||||||
|
proc push*(s: var RunningStat, x: float) =
|
||||||
## pushes a value `x` for processing
|
## pushes a value `x` for processing
|
||||||
inc(s.n)
|
inc(s.n)
|
||||||
# See Knuth TAOCP vol 2, 3rd edition, page 232
|
# See Knuth TAOCP vol 2, 3rd edition, page 232
|
||||||
|
|
@ -313,16 +315,16 @@ proc push*(s: var TRunningStat, x: float) =
|
||||||
s.oldS = s.newS
|
s.oldS = s.newS
|
||||||
s.sum = s.sum + x
|
s.sum = s.sum + x
|
||||||
|
|
||||||
proc push*(s: var TRunningStat, x: int) =
|
proc push*(s: var RunningStat, x: int) =
|
||||||
## pushes a value `x` for processing. `x` is simply converted to ``float``
|
## pushes a value `x` for processing. `x` is simply converted to ``float``
|
||||||
## and the other push operation is called.
|
## and the other push operation is called.
|
||||||
push(s, toFloat(x))
|
push(s, toFloat(x))
|
||||||
|
|
||||||
proc variance*(s: TRunningStat): float =
|
proc variance*(s: RunningStat): float =
|
||||||
## computes the current variance of `s`
|
## computes the current variance of `s`
|
||||||
if s.n > 1: result = s.newS / (toFloat(s.n - 1))
|
if s.n > 1: result = s.newS / (toFloat(s.n - 1))
|
||||||
|
|
||||||
proc standardDeviation*(s: TRunningStat): float =
|
proc standardDeviation*(s: RunningStat): float =
|
||||||
## computes the current standard deviation of `s`
|
## computes the current standard deviation of `s`
|
||||||
result = sqrt(variance(s))
|
result = sqrt(variance(s))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2010 Andreas Rumpf
|
# (c) Copyright 2010 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2014 Nimrod Contributors
|
# (c) Copyright 2014 Nimrod Contributors
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -22,7 +22,7 @@ else:
|
||||||
import os
|
import os
|
||||||
|
|
||||||
type
|
type
|
||||||
TMemFile* = object {.pure.} ## represents a memory mapped file
|
MemFile* = object ## represents a memory mapped file
|
||||||
mem*: pointer ## a pointer to the memory mapped file. The pointer
|
mem*: pointer ## a pointer to the memory mapped file. The pointer
|
||||||
## can be used directly to change the contents of the
|
## can be used directly to change the contents of the
|
||||||
## file, if it was opened with write access.
|
## file, if it was opened with write access.
|
||||||
|
|
@ -34,8 +34,9 @@ type
|
||||||
else:
|
else:
|
||||||
handle: cint
|
handle: cint
|
||||||
|
|
||||||
|
{.deprecated: [TMemFile: MemFile].}
|
||||||
|
|
||||||
proc mapMem*(m: var TMemFile, mode: FileMode = fmRead,
|
proc mapMem*(m: var MemFile, mode: FileMode = fmRead,
|
||||||
mappedSize = -1, offset = 0): pointer =
|
mappedSize = -1, offset = 0): pointer =
|
||||||
var readonly = mode == fmRead
|
var readonly = mode == fmRead
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
|
|
@ -60,27 +61,27 @@ proc mapMem*(m: var TMemFile, mode: FileMode = fmRead,
|
||||||
osError(osLastError())
|
osError(osLastError())
|
||||||
|
|
||||||
|
|
||||||
proc unmapMem*(f: var TMemFile, p: pointer, size: int) =
|
proc unmapMem*(f: var MemFile, p: pointer, size: int) =
|
||||||
## unmaps the memory region ``(p, <p+size)`` of the mapped file `f`.
|
## unmaps the memory region ``(p, <p+size)`` of the mapped file `f`.
|
||||||
## All changes are written back to the file system, if `f` was opened
|
## All changes are written back to the file system, if `f` was opened
|
||||||
## with write access. ``size`` must be of exactly the size that was requested
|
## with write access. ``size`` must be of exactly the size that was requested
|
||||||
## via ``mapMem``.
|
## via ``mapMem``.
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
if unmapViewOfFile(p) == 0: osError(osLastError())
|
if unmapViewOfFile(p) == 0: raiseOSError(osLastError())
|
||||||
else:
|
else:
|
||||||
if munmap(p, size) != 0: osError(osLastError())
|
if munmap(p, size) != 0: raiseOSError(osLastError())
|
||||||
|
|
||||||
|
|
||||||
proc open*(filename: string, mode: FileMode = fmRead,
|
proc open*(filename: string, mode: FileMode = fmRead,
|
||||||
mappedSize = -1, offset = 0, newFileSize = -1): TMemFile =
|
mappedSize = -1, offset = 0, newFileSize = -1): MemFile =
|
||||||
## opens a memory mapped file. If this fails, ``EOS`` is raised.
|
## opens a memory mapped file. If this fails, ``EOS`` is raised.
|
||||||
## `newFileSize` can only be set if the file does not exist and is opened
|
## `newFileSize` can only be set if the file does not exist and is opened
|
||||||
## with write access (e.g., with fmReadWrite). `mappedSize` and `offset`
|
## with write access (e.g., with fmReadWrite). `mappedSize` and `offset`
|
||||||
## can be used to map only a slice of the file. Example:
|
## can be used to map only a slice of the file. Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## var
|
## var
|
||||||
## mm, mm_full, mm_half: TMemFile
|
## mm, mm_full, mm_half: MemFile
|
||||||
##
|
##
|
||||||
## mm = memfiles.open("/tmp/test.mmap", mode = fmWrite, newFileSize = 1024) # Create a new file
|
## mm = memfiles.open("/tmp/test.mmap", mode = fmWrite, newFileSize = 1024) # Create a new file
|
||||||
## mm.close()
|
## mm.close()
|
||||||
|
|
@ -100,11 +101,11 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||||
result.size = 0
|
result.size = 0
|
||||||
|
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
template fail(errCode: TOSErrorCode, msg: expr) =
|
template fail(errCode: OSErrorCode, msg: expr) =
|
||||||
rollback()
|
rollback()
|
||||||
if result.fHandle != 0: discard closeHandle(result.fHandle)
|
if result.fHandle != 0: discard closeHandle(result.fHandle)
|
||||||
if result.mapHandle != 0: discard closeHandle(result.mapHandle)
|
if result.mapHandle != 0: discard closeHandle(result.mapHandle)
|
||||||
osError(errCode)
|
raiseOSError(errCode)
|
||||||
# return false
|
# return false
|
||||||
#raise newException(EIO, msg)
|
#raise newException(EIO, msg)
|
||||||
|
|
||||||
|
|
@ -169,10 +170,10 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||||
else: result.size = fileSize.int
|
else: result.size = fileSize.int
|
||||||
|
|
||||||
else:
|
else:
|
||||||
template fail(errCode: TOSErrorCode, msg: expr) =
|
template fail(errCode: OSErrorCode, msg: expr) =
|
||||||
rollback()
|
rollback()
|
||||||
if result.handle != 0: discard close(result.handle)
|
if result.handle != 0: discard close(result.handle)
|
||||||
osError(errCode)
|
raiseOSError(errCode)
|
||||||
|
|
||||||
var flags = if readonly: O_RDONLY else: O_RDWR
|
var flags = if readonly: O_RDONLY else: O_RDWR
|
||||||
|
|
||||||
|
|
@ -214,12 +215,12 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||||
if result.mem == cast[pointer](MAP_FAILED):
|
if result.mem == cast[pointer](MAP_FAILED):
|
||||||
fail(osLastError(), "file mapping failed")
|
fail(osLastError(), "file mapping failed")
|
||||||
|
|
||||||
proc close*(f: var TMemFile) =
|
proc close*(f: var MemFile) =
|
||||||
## closes the memory mapped file `f`. All changes are written back to the
|
## closes the memory mapped file `f`. All changes are written back to the
|
||||||
## file system, if `f` was opened with write access.
|
## file system, if `f` was opened with write access.
|
||||||
|
|
||||||
var error = false
|
var error = false
|
||||||
var lastErr: TOSErrorCode
|
var lastErr: OSErrorCode
|
||||||
|
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
if f.fHandle != INVALID_HANDLE_VALUE:
|
if f.fHandle != INVALID_HANDLE_VALUE:
|
||||||
|
|
@ -242,5 +243,5 @@ proc close*(f: var TMemFile) =
|
||||||
else:
|
else:
|
||||||
f.handle = 0
|
f.handle = 0
|
||||||
|
|
||||||
if error: osError(lastErr)
|
if error: raiseOSError(lastErr)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,26 @@
|
||||||
import unsigned
|
import unsigned
|
||||||
|
|
||||||
type
|
type
|
||||||
TMersenneTwister* = object
|
MersenneTwister* = object
|
||||||
mt: array[0..623, uint32]
|
mt: array[0..623, uint32]
|
||||||
index: int
|
index: int
|
||||||
|
|
||||||
proc newMersenneTwister*(seed: int): TMersenneTwister =
|
{.deprecated: [TMersenneTwister: MersenneTwister].}
|
||||||
|
|
||||||
|
proc newMersenneTwister*(seed: int): MersenneTwister =
|
||||||
result.index = 0
|
result.index = 0
|
||||||
result.mt[0]= uint32(seed)
|
result.mt[0]= uint32(seed)
|
||||||
for i in 1..623'u32:
|
for i in 1..623'u32:
|
||||||
result.mt[i]= (0x6c078965'u32 * (result.mt[i-1] xor (result.mt[i-1] shr 30'u32)) + i)
|
result.mt[i]= (0x6c078965'u32 * (result.mt[i-1] xor (result.mt[i-1] shr 30'u32)) + i)
|
||||||
|
|
||||||
proc generateNumbers(m: var TMersenneTwister) =
|
proc generateNumbers(m: var MersenneTwister) =
|
||||||
for i in 0..623:
|
for i in 0..623:
|
||||||
var y = (m.mt[i] and 0x80000000'u32) + (m.mt[(i+1) mod 624] and 0x7fffffff'u32)
|
var y = (m.mt[i] and 0x80000000'u32) + (m.mt[(i+1) mod 624] and 0x7fffffff'u32)
|
||||||
m.mt[i] = m.mt[(i+397) mod 624] xor uint32(y shr 1'u32)
|
m.mt[i] = m.mt[(i+397) mod 624] xor uint32(y shr 1'u32)
|
||||||
if (y mod 2'u32) != 0:
|
if (y mod 2'u32) != 0:
|
||||||
m.mt[i] = m.mt[i] xor 0x9908b0df'u32
|
m.mt[i] = m.mt[i] xor 0x9908b0df'u32
|
||||||
|
|
||||||
proc getNum*(m: var TMersenneTwister): int =
|
proc getNum*(m: var MersenneTwister): int =
|
||||||
if m.index == 0:
|
if m.index == 0:
|
||||||
generateNumbers(m)
|
generateNumbers(m)
|
||||||
var y = m.mt[m.index]
|
var y = m.mt[m.index]
|
||||||
|
|
@ -29,8 +31,6 @@ proc getNum*(m: var TMersenneTwister): int =
|
||||||
m.index = (m.index+1) mod 624
|
m.index = (m.index+1) mod 624
|
||||||
return int(y)
|
return int(y)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Test
|
# Test
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
var mt = newMersenneTwister(2525)
|
var mt = newMersenneTwister(2525)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Dominik Picheta
|
# (c) Copyright 2012 Dominik Picheta
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -10,8 +10,10 @@
|
||||||
## This module implements a mimetypes database
|
## This module implements a mimetypes database
|
||||||
import strtabs
|
import strtabs
|
||||||
type
|
type
|
||||||
TMimeDB* = object
|
MimeDB* = object
|
||||||
mimes: PStringTable
|
mimes: StringTableRef
|
||||||
|
|
||||||
|
{.deprecated: [TMimeDB: MimeDB].}
|
||||||
|
|
||||||
const mimes* = {
|
const mimes* = {
|
||||||
"ez": "application/andrew-inset",
|
"ez": "application/andrew-inset",
|
||||||
|
|
@ -489,20 +491,19 @@ const mimes* = {
|
||||||
"vrml": "x-world/x-vrml",
|
"vrml": "x-world/x-vrml",
|
||||||
"wrl": "x-world/x-vrml"}
|
"wrl": "x-world/x-vrml"}
|
||||||
|
|
||||||
proc newMimetypes*(): TMimeDB =
|
proc newMimetypes*(): MimeDB =
|
||||||
## Creates a new Mimetypes database. The database will contain the most
|
## Creates a new Mimetypes database. The database will contain the most
|
||||||
## common mimetypes.
|
## common mimetypes.
|
||||||
|
|
||||||
result.mimes = mimes.newStringTable()
|
result.mimes = mimes.newStringTable()
|
||||||
|
|
||||||
proc getMimetype*(mimedb: TMimeDB, ext: string, default = "text/plain"): string =
|
proc getMimetype*(mimedb: MimeDB, ext: string, default = "text/plain"): string =
|
||||||
## Gets mimetype which corresponds to ``ext``. Returns ``default`` if ``ext``
|
## Gets mimetype which corresponds to ``ext``. Returns ``default`` if ``ext``
|
||||||
## could not be found.
|
## could not be found.
|
||||||
result = mimedb.mimes[ext]
|
result = mimedb.mimes[ext]
|
||||||
if result == "":
|
if result == "":
|
||||||
return default
|
return default
|
||||||
|
|
||||||
proc getExt*(mimedb: TMimeDB, mimetype: string, default = "txt"): string =
|
proc getExt*(mimedb: MimeDB, mimetype: string, default = "txt"): string =
|
||||||
## Gets extension which corresponds to ``mimetype``. Returns ``default`` if
|
## Gets extension which corresponds to ``mimetype``. Returns ``default`` if
|
||||||
## ``mimetype`` could not be found. Extensions are returned without the
|
## ``mimetype`` could not be found. Extensions are returned without the
|
||||||
## leading dot.
|
## leading dot.
|
||||||
|
|
@ -511,7 +512,7 @@ proc getExt*(mimedb: TMimeDB, mimetype: string, default = "txt"): string =
|
||||||
if m == mimetype:
|
if m == mimetype:
|
||||||
result = e
|
result = e
|
||||||
|
|
||||||
proc register*(mimedb: var TMimeDB, ext: string, mimetype: string) =
|
proc register*(mimedb: var MimeDB, ext: string, mimetype: string) =
|
||||||
## Adds ``mimetype`` to the ``mimedb``.
|
## Adds ``mimetype`` to the ``mimedb``.
|
||||||
mimedb.mimes[ext] = mimetype
|
mimedb.mimes[ext] = mimetype
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2014 Dominik Picheta
|
# (c) Copyright 2014 Dominik Picheta
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
{.deadCodeElim: on.}
|
{.deadCodeElim: on.}
|
||||||
import rawsockets, os, strutils, unsigned, parseutils, times
|
import rawsockets, os, strutils, unsigned, parseutils, times
|
||||||
export TPort, `$`, `==`
|
export Port, `$`, `==`
|
||||||
|
|
||||||
const useWinVersion = defined(Windows) or defined(nimdoc)
|
const useWinVersion = defined(Windows) or defined(nimdoc)
|
||||||
|
|
||||||
|
|
@ -22,25 +22,25 @@ when defined(ssl):
|
||||||
|
|
||||||
when defined(ssl):
|
when defined(ssl):
|
||||||
type
|
type
|
||||||
ESSL* = object of ESynch
|
SSLError* = object of Exception
|
||||||
|
|
||||||
TSSLCVerifyMode* = enum
|
SSLCVerifyMode* = enum
|
||||||
CVerifyNone, CVerifyPeer
|
CVerifyNone, CVerifyPeer
|
||||||
|
|
||||||
TSSLProtVersion* = enum
|
SSLProtVersion* = enum
|
||||||
protSSLv2, protSSLv3, protTLSv1, protSSLv23
|
protSSLv2, protSSLv3, protTLSv1, protSSLv23
|
||||||
|
|
||||||
PSSLContext* = distinct PSSLCTX
|
SSLContext* = distinct PSSLCTX
|
||||||
|
|
||||||
TSSLAcceptResult* = enum
|
SSLAcceptResult* = enum
|
||||||
AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess
|
AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess
|
||||||
|
|
||||||
const
|
const
|
||||||
BufferSize*: int = 4000 ## size of a buffered socket's buffer
|
BufferSize*: int = 4000 ## size of a buffered socket's buffer
|
||||||
|
|
||||||
type
|
type
|
||||||
TSocketImpl* = object ## socket type
|
SocketImpl* = object ## socket type
|
||||||
fd*: TSocketHandle
|
fd*: SocketHandle
|
||||||
case isBuffered*: bool # determines whether this socket is buffered.
|
case isBuffered*: bool # determines whether this socket is buffered.
|
||||||
of true:
|
of true:
|
||||||
buffer*: array[0..BufferSize, char]
|
buffer*: array[0..BufferSize, char]
|
||||||
|
|
@ -57,34 +57,40 @@ type
|
||||||
sslPeekChar*: char
|
sslPeekChar*: char
|
||||||
of false: nil
|
of false: nil
|
||||||
|
|
||||||
PSocket* = ref TSocketImpl
|
Socket* = ref SocketImpl
|
||||||
|
|
||||||
TSOBool* = enum ## Boolean socket options.
|
SOBool* = enum ## Boolean socket options.
|
||||||
OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive,
|
OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive,
|
||||||
OptOOBInline, OptReuseAddr
|
OptOOBInline, OptReuseAddr
|
||||||
|
|
||||||
TReadLineResult* = enum ## result for readLineAsync
|
ReadLineResult* = enum ## result for readLineAsync
|
||||||
ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone
|
ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone
|
||||||
|
|
||||||
ETimeout* = object of ESynch
|
TimeoutError* = object of Exception
|
||||||
|
|
||||||
TSocketFlags* {.pure.} = enum
|
SocketFlag* {.pure.} = enum
|
||||||
Peek,
|
Peek,
|
||||||
SafeDisconn ## Ensures disconnection exceptions (ECONNRESET, EPIPE etc) are not thrown.
|
SafeDisconn ## Ensures disconnection exceptions (ECONNRESET, EPIPE etc) are not thrown.
|
||||||
|
|
||||||
proc isDisconnectionError*(flags: set[TSocketFlags],
|
{.deprecated: [TSocketFlags: SocketFlag, ETimeout: TimeoutError,
|
||||||
lastError: TOSErrorCode): bool =
|
TReadLineResult: ReadLineResult, TSOBool: SOBool, PSocket: Socket,
|
||||||
|
TSocketImpl: SocketImpl, ESSL: SSLError, TSSLCVerifyMode: SSLCVerifyMode,
|
||||||
|
TSSLProtVersion: SSLProtVersion, PSSLContext: SSLContext,
|
||||||
|
TSSLAcceptResult: SSLAcceptResult].}
|
||||||
|
|
||||||
|
proc isDisconnectionError*(flags: set[SocketFlag],
|
||||||
|
lastError: OSErrorCode): bool =
|
||||||
## Determines whether ``lastError`` is a disconnection error. Only does this
|
## Determines whether ``lastError`` is a disconnection error. Only does this
|
||||||
## if flags contains ``SafeDisconn``.
|
## if flags contains ``SafeDisconn``.
|
||||||
when useWinVersion:
|
when useWinVersion:
|
||||||
TSocketFlags.SafeDisconn in flags and
|
SocketFlag.SafeDisconn in flags and
|
||||||
lastError.int32 in {WSAECONNRESET, WSAECONNABORTED, WSAENETRESET,
|
lastError.int32 in {WSAECONNRESET, WSAECONNABORTED, WSAENETRESET,
|
||||||
WSAEDISCON, ERROR_NETNAME_DELETED}
|
WSAEDISCON, ERROR_NETNAME_DELETED}
|
||||||
else:
|
else:
|
||||||
TSocketFlags.SafeDisconn in flags and
|
SocketFlag.SafeDisconn in flags and
|
||||||
lastError.int32 in {ECONNRESET, EPIPE, ENETRESET}
|
lastError.int32 in {ECONNRESET, EPIPE, ENETRESET}
|
||||||
|
|
||||||
proc toOSFlags*(socketFlags: set[TSocketFlags]): cint =
|
proc toOSFlags*(socketFlags: set[SocketFlag]): cint =
|
||||||
## Converts the flags into the underlying OS representation.
|
## Converts the flags into the underlying OS representation.
|
||||||
for f in socketFlags:
|
for f in socketFlags:
|
||||||
case f
|
case f
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## Profiling support for Nimrod. This is an embedded profiler that requires
|
## Profiling support for Nim. This is an embedded profiler that requires
|
||||||
## ``--profiler:on``. You only need to import this module to get a profiling
|
## ``--profiler:on``. You only need to import this module to get a profiling
|
||||||
## report at program exit.
|
## report at program exit.
|
||||||
|
|
||||||
|
|
@ -64,7 +64,7 @@ when withThreads:
|
||||||
var
|
var
|
||||||
profilingLock: TLock
|
profilingLock: TLock
|
||||||
|
|
||||||
InitLock profilingLock
|
initLock profilingLock
|
||||||
|
|
||||||
proc hookAux(st: TStackTrace, costs: int) =
|
proc hookAux(st: TStackTrace, costs: int) =
|
||||||
# this is quite performance sensitive!
|
# this is quite performance sensitive!
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,17 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2013 Robert Persson
|
# (c) Copyright 2013 Robert Persson
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
type OneVarFunction* = proc (x: float): float
|
||||||
|
|
||||||
type TOneVarFunction* =proc (x:float):float
|
{.deprecated: [TOneVarFunction: OneVarFunction].}
|
||||||
|
|
||||||
proc brent*(xmin,xmax:float ,function:TOneVarFunction, tol:float,maxiter=1000):
|
proc brent*(xmin,xmax:float, function:OneVarFunction, tol:float,maxiter=1000):
|
||||||
tuple[rootx, rooty: float, success: bool]=
|
tuple[rootx, rooty: float, success: bool]=
|
||||||
## Searches `function` for a root between `xmin` and `xmax`
|
## Searches `function` for a root between `xmin` and `xmax`
|
||||||
## using brents method. If the function value at `xmin`and `xmax` has the
|
## using brents method. If the function value at `xmin`and `xmax` has the
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2013 Andreas Rumpf
|
# (c) Copyright 2013 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## Nimrod OID support. An OID is a global ID that consists of a timestamp,
|
## Nim OID support. An OID is a global ID that consists of a timestamp,
|
||||||
## a unique counter and a random value. This combination should suffice to
|
## a unique counter and a random value. This combination should suffice to
|
||||||
## produce a globally distributed unique ID. This implementation was extracted
|
## produce a globally distributed unique ID. This implementation was extracted
|
||||||
## from the Mongodb interface and it thus binary compatible with a Mongo OID.
|
## from the Mongodb interface and it thus binary compatible with a Mongo OID.
|
||||||
|
|
@ -18,11 +18,13 @@
|
||||||
import times, endians
|
import times, endians
|
||||||
|
|
||||||
type
|
type
|
||||||
Toid* {.pure, final.} = object ## an OID
|
Oid* = object ## an OID
|
||||||
time: int32 ##
|
time: int32 ##
|
||||||
fuzz: int32 ##
|
fuzz: int32 ##
|
||||||
count: int32 ##
|
count: int32 ##
|
||||||
|
|
||||||
|
{.deprecated: [Toid: Oid].}
|
||||||
|
|
||||||
proc hexbyte*(hex: char): int =
|
proc hexbyte*(hex: char): int =
|
||||||
case hex
|
case hex
|
||||||
of '0'..'9': result = (ord(hex) - ord('0'))
|
of '0'..'9': result = (ord(hex) - ord('0'))
|
||||||
|
|
@ -30,7 +32,7 @@ proc hexbyte*(hex: char): int =
|
||||||
of 'A'..'F': result = (ord(hex) - ord('A') + 10)
|
of 'A'..'F': result = (ord(hex) - ord('A') + 10)
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc parseOid*(str: cstring): TOid =
|
proc parseOid*(str: cstring): Oid =
|
||||||
## parses an OID.
|
## parses an OID.
|
||||||
var bytes = cast[cstring](addr(result.time))
|
var bytes = cast[cstring](addr(result.time))
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
@ -38,7 +40,7 @@ proc parseOid*(str: cstring): TOid =
|
||||||
bytes[i] = chr((hexbyte(str[2 * i]) shl 4) or hexbyte(str[2 * i + 1]))
|
bytes[i] = chr((hexbyte(str[2 * i]) shl 4) or hexbyte(str[2 * i + 1]))
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
||||||
proc oidToString*(oid: TOid, str: cstring) =
|
proc oidToString*(oid: Oid, str: cstring) =
|
||||||
const hex = "0123456789abcdef"
|
const hex = "0123456789abcdef"
|
||||||
# work around a compiler bug:
|
# work around a compiler bug:
|
||||||
var str = str
|
var str = str
|
||||||
|
|
@ -52,7 +54,7 @@ proc oidToString*(oid: TOid, str: cstring) =
|
||||||
inc(i)
|
inc(i)
|
||||||
str[24] = '\0'
|
str[24] = '\0'
|
||||||
|
|
||||||
proc `$`*(oid: TOid): string =
|
proc `$`*(oid: Oid): string =
|
||||||
result = newString(25)
|
result = newString(25)
|
||||||
oidToString(oid, result)
|
oidToString(oid, result)
|
||||||
|
|
||||||
|
|
@ -60,7 +62,7 @@ var
|
||||||
incr: int
|
incr: int
|
||||||
fuzz: int32
|
fuzz: int32
|
||||||
|
|
||||||
proc genOid*(): TOid =
|
proc genOid*(): Oid =
|
||||||
## generates a new OID.
|
## generates a new OID.
|
||||||
proc rand(): cint {.importc: "rand", header: "<stdlib.h>", nodecl.}
|
proc rand(): cint {.importc: "rand", header: "<stdlib.h>", nodecl.}
|
||||||
proc gettime(dummy: ptr cint): cint {.importc: "time", header: "<time.h>".}
|
proc gettime(dummy: ptr cint): cint {.importc: "time", header: "<time.h>".}
|
||||||
|
|
@ -79,12 +81,12 @@ proc genOid*(): TOid =
|
||||||
result.fuzz = fuzz
|
result.fuzz = fuzz
|
||||||
bigEndian32(addr result.count, addr(i))
|
bigEndian32(addr result.count, addr(i))
|
||||||
|
|
||||||
proc generatedTime*(oid: TOid): TTime =
|
proc generatedTime*(oid: Oid): Time =
|
||||||
## returns the generated timestamp of the OID.
|
## returns the generated timestamp of the OID.
|
||||||
var tmp: int32
|
var tmp: int32
|
||||||
var dummy = oid.time
|
var dummy = oid.time
|
||||||
bigEndian32(addr(tmp), addr(dummy))
|
bigEndian32(addr(tmp), addr(dummy))
|
||||||
result = TTime(tmp)
|
result = Time(tmp)
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
let xo = genOID()
|
let xo = genOID()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2014 Andreas Rumpf
|
# (c) Copyright 2014 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -29,18 +29,23 @@ else:
|
||||||
include "system/ansi_c"
|
include "system/ansi_c"
|
||||||
|
|
||||||
type
|
type
|
||||||
FReadEnv* = object of ReadIOEffect ## effect that denotes a read
|
ReadEnvEffect* = object of ReadIOEffect ## effect that denotes a read
|
||||||
## from an environment variable
|
## from an environment variable
|
||||||
FWriteEnv* = object of WriteIOEffect ## effect that denotes a write
|
WriteEnvEffect* = object of WriteIOEffect ## effect that denotes a write
|
||||||
## to an environment variable
|
## to an environment variable
|
||||||
|
|
||||||
FReadDir* = object of ReadIOEffect ## effect that denotes a write operation to
|
ReadDirEffect* = object of ReadIOEffect ## effect that denotes a write
|
||||||
## the directory structure
|
## operation to the directory structure
|
||||||
FWriteDir* = object of WriteIOEffect ## effect that denotes a write operation to
|
WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write operation to
|
||||||
## the directory structure
|
## the directory structure
|
||||||
|
|
||||||
TOSErrorCode* = distinct int32 ## Specifies an OS Error Code.
|
OSErrorCode* = distinct int32 ## Specifies an OS Error Code.
|
||||||
|
|
||||||
|
{.deprecated: [FReadEnv: ReadEnvEffect, FWriteEnv: WriteEnvEffect,
|
||||||
|
FReadDir: ReadDirEffect,
|
||||||
|
FWriteDir: WriteDirEffect,
|
||||||
|
TOSErrorCode: OSErrorCode
|
||||||
|
].}
|
||||||
const
|
const
|
||||||
doslike = defined(windows) or defined(OS2) or defined(DOS)
|
doslike = defined(windows) or defined(OS2) or defined(DOS)
|
||||||
# DOS-like filesystem
|
# DOS-like filesystem
|
||||||
|
|
@ -204,7 +209,8 @@ proc osErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} =
|
||||||
result = $os.strerror(errno)
|
result = $os.strerror(errno)
|
||||||
|
|
||||||
{.push warning[deprecated]: off.}
|
{.push warning[deprecated]: off.}
|
||||||
proc osError*(msg: string = "") {.noinline, rtl, extern: "nos$1", deprecated.} =
|
proc raiseOSError*(msg: string = "") {.noinline, rtl, extern: "nos$1",
|
||||||
|
deprecated.} =
|
||||||
## raises an EOS exception with the given message ``msg``.
|
## raises an EOS exception with the given message ``msg``.
|
||||||
## If ``msg == ""``, the operating system's error flag
|
## If ``msg == ""``, the operating system's error flag
|
||||||
## (``errno``) is converted to a readable error message. On Windows
|
## (``errno``) is converted to a readable error message. On Windows
|
||||||
|
|
@ -219,10 +225,12 @@ proc osError*(msg: string = "") {.noinline, rtl, extern: "nos$1", deprecated.} =
|
||||||
raise newException(OSError, msg)
|
raise newException(OSError, msg)
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
proc `==`*(err1, err2: TOSErrorCode): bool {.borrow.}
|
{.deprecated: [osError: raiseOSError].}
|
||||||
proc `$`*(err: TOSErrorCode): string {.borrow.}
|
|
||||||
|
|
||||||
proc osErrorMsg*(errorCode: TOSErrorCode): string =
|
proc `==`*(err1, err2: OSErrorCode): bool {.borrow.}
|
||||||
|
proc `$`*(err: OSErrorCode): string {.borrow.}
|
||||||
|
|
||||||
|
proc osErrorMsg*(errorCode: OSErrorCode): string =
|
||||||
## Converts an OS error code into a human readable string.
|
## Converts an OS error code into a human readable string.
|
||||||
##
|
##
|
||||||
## The error code can be retrieved using the ``OSLastError`` proc.
|
## The error code can be retrieved using the ``OSLastError`` proc.
|
||||||
|
|
@ -249,10 +257,10 @@ proc osErrorMsg*(errorCode: TOSErrorCode): string =
|
||||||
result = $msgbuf
|
result = $msgbuf
|
||||||
if msgbuf != nil: localFree(msgbuf)
|
if msgbuf != nil: localFree(msgbuf)
|
||||||
else:
|
else:
|
||||||
if errorCode != TOSErrorCode(0'i32):
|
if errorCode != OSErrorCode(0'i32):
|
||||||
result = $os.strerror(errorCode.int32)
|
result = $os.strerror(errorCode.int32)
|
||||||
|
|
||||||
proc osError*(errorCode: TOSErrorCode) =
|
proc raiseOSError*(errorCode: OSErrorCode) =
|
||||||
## Raises an ``EOS`` exception. The ``errorCode`` will determine the
|
## Raises an ``EOS`` exception. The ``errorCode`` will determine the
|
||||||
## message, ``OSErrorMsg`` will be used to get this message.
|
## message, ``OSErrorMsg`` will be used to get this message.
|
||||||
##
|
##
|
||||||
|
|
@ -268,7 +276,7 @@ proc osError*(errorCode: TOSErrorCode) =
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
{.push stackTrace:off.}
|
{.push stackTrace:off.}
|
||||||
proc osLastError*(): TOSErrorCode =
|
proc osLastError*(): OSErrorCode =
|
||||||
## Retrieves the last operating system error code.
|
## Retrieves the last operating system error code.
|
||||||
##
|
##
|
||||||
## This procedure is useful in the event when an OS call fails. In that case
|
## This procedure is useful in the event when an OS call fails. In that case
|
||||||
|
|
@ -285,7 +293,7 @@ proc osLastError*(): TOSErrorCode =
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
result = TOSErrorCode(getLastError())
|
result = TOSErrorCode(getLastError())
|
||||||
else:
|
else:
|
||||||
result = TOSErrorCode(errno)
|
result = OSErrorCode(errno)
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
proc unixToNativePath*(path: string, drive=""): string {.
|
proc unixToNativePath*(path: string, drive=""): string {.
|
||||||
|
|
@ -371,7 +379,7 @@ when defined(windows):
|
||||||
f.cFileName[1].int == dot and f.cFileName[2].int == 0)
|
f.cFileName[1].int == dot and f.cFileName[2].int == 0)
|
||||||
|
|
||||||
proc existsFile*(filename: string): bool {.rtl, extern: "nos$1",
|
proc existsFile*(filename: string): bool {.rtl, extern: "nos$1",
|
||||||
tags: [FReadDir].} =
|
tags: [ReadDirEffect].} =
|
||||||
## Returns true if the file exists, false otherwise.
|
## Returns true if the file exists, false otherwise.
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
when useWinUnicode:
|
when useWinUnicode:
|
||||||
|
|
@ -384,7 +392,7 @@ proc existsFile*(filename: string): bool {.rtl, extern: "nos$1",
|
||||||
var res: TStat
|
var res: TStat
|
||||||
return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode)
|
return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode)
|
||||||
|
|
||||||
proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [FReadDir].} =
|
proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect].} =
|
||||||
## Returns true iff the directory `dir` exists. If `dir` is a file, false
|
## Returns true iff the directory `dir` exists. If `dir` is a file, false
|
||||||
## is returned.
|
## is returned.
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
|
|
@ -399,7 +407,7 @@ proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [FReadDir].} =
|
||||||
return stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode)
|
return stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode)
|
||||||
|
|
||||||
proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
|
proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
|
||||||
tags: [FReadDir].} =
|
tags: [ReadDirEffect].} =
|
||||||
## Returns true iff the symlink `link` exists. Will return true
|
## Returns true iff the symlink `link` exists. Will return true
|
||||||
## regardless of whether the link points to a directory or file.
|
## regardless of whether the link points to a directory or file.
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
|
|
@ -701,7 +709,7 @@ proc extractFilename*(path: string): string {.
|
||||||
result = splitPath(path).tail
|
result = splitPath(path).tail
|
||||||
|
|
||||||
proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
|
proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
|
||||||
tags: [FReadDir].} =
|
tags: [ReadDirEffect].} =
|
||||||
## Returns the full path of `filename`, raises EOS in case of an error.
|
## Returns the full path of `filename`, raises EOS in case of an error.
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
const bufsize = 3072'i32
|
const bufsize = 3072'i32
|
||||||
|
|
@ -800,7 +808,7 @@ when defined(Windows):
|
||||||
)
|
)
|
||||||
|
|
||||||
proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
|
proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
|
||||||
tags: [FReadDir].} =
|
tags: [ReadDirEffect].} =
|
||||||
## Returns True if both pathname arguments refer to the same physical
|
## Returns True if both pathname arguments refer to the same physical
|
||||||
## file or directory. Raises an exception if any of the files does not
|
## file or directory. Raises an exception if any of the files does not
|
||||||
## exist or information about it can not be obtained.
|
## exist or information about it can not be obtained.
|
||||||
|
|
@ -883,7 +891,7 @@ type
|
||||||
fpOthersRead ## read access for others
|
fpOthersRead ## read access for others
|
||||||
|
|
||||||
proc getFilePermissions*(filename: string): set[TFilePermission] {.
|
proc getFilePermissions*(filename: string): set[TFilePermission] {.
|
||||||
rtl, extern: "nos$1", tags: [FReadDir].} =
|
rtl, extern: "nos$1", tags: [ReadDirEffect].} =
|
||||||
## retrieves file permissions for `filename`. `OSError` is raised in case of
|
## retrieves file permissions for `filename`. `OSError` is raised in case of
|
||||||
## an error. On Windows, only the ``readonly`` flag is checked, every other
|
## an error. On Windows, only the ``readonly`` flag is checked, every other
|
||||||
## permission is available in any case.
|
## permission is available in any case.
|
||||||
|
|
@ -915,7 +923,7 @@ proc getFilePermissions*(filename: string): set[TFilePermission] {.
|
||||||
result = {fpUserExec..fpOthersRead}
|
result = {fpUserExec..fpOthersRead}
|
||||||
|
|
||||||
proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {.
|
proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {.
|
||||||
rtl, extern: "nos$1", tags: [FWriteDir].} =
|
rtl, extern: "nos$1", tags: [WriteDirEffect].} =
|
||||||
## sets the file permissions for `filename`. `OSError` is raised in case of
|
## sets the file permissions for `filename`. `OSError` is raised in case of
|
||||||
## an error. On Windows, only the ``readonly`` flag is changed, depending on
|
## an error. On Windows, only the ``readonly`` flag is changed, depending on
|
||||||
## ``fpUserWrite``.
|
## ``fpUserWrite``.
|
||||||
|
|
@ -1014,7 +1022,7 @@ when defined(Windows):
|
||||||
template setFileAttributes(file, attrs: expr): expr {.immediate.} =
|
template setFileAttributes(file, attrs: expr): expr {.immediate.} =
|
||||||
setFileAttributesA(file, attrs)
|
setFileAttributesA(file, attrs)
|
||||||
|
|
||||||
proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} =
|
proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} =
|
||||||
## Removes the `file`. If this fails, `EOS` is raised. This does not fail
|
## Removes the `file`. If this fails, `EOS` is raised. This does not fail
|
||||||
## if the file never existed in the first place.
|
## if the file never existed in the first place.
|
||||||
## On Windows, ignores the read-only attribute.
|
## On Windows, ignores the read-only attribute.
|
||||||
|
|
@ -1076,7 +1084,7 @@ when defined(windows):
|
||||||
while true:
|
while true:
|
||||||
var eend = strEnd(e)
|
var eend = strEnd(e)
|
||||||
add(environment, $e)
|
add(environment, $e)
|
||||||
e = cast[WideCString](cast[ByteAddress](eend)+2)
|
e = cast[WideCString](cast[TAddress](eend)+2)
|
||||||
if eend[1].int == 0: break
|
if eend[1].int == 0: break
|
||||||
discard freeEnvironmentStringsW(env)
|
discard freeEnvironmentStringsW(env)
|
||||||
else:
|
else:
|
||||||
|
|
@ -1130,7 +1138,7 @@ proc findEnvVar(key: string): int =
|
||||||
if startsWith(environment[i], temp): return i
|
if startsWith(environment[i], temp): return i
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
proc getEnv*(key: string): TaintedString {.tags: [FReadEnv].} =
|
proc getEnv*(key: string): TaintedString {.tags: [ReadEnvEffect].} =
|
||||||
## Returns the value of the `environment variable`:idx: named `key`.
|
## Returns the value of the `environment variable`:idx: named `key`.
|
||||||
##
|
##
|
||||||
## If the variable does not exist, "" is returned. To distinguish
|
## If the variable does not exist, "" is returned. To distinguish
|
||||||
|
|
@ -1144,13 +1152,13 @@ proc getEnv*(key: string): TaintedString {.tags: [FReadEnv].} =
|
||||||
if env == nil: return TaintedString("")
|
if env == nil: return TaintedString("")
|
||||||
result = TaintedString($env)
|
result = TaintedString($env)
|
||||||
|
|
||||||
proc existsEnv*(key: string): bool {.tags: [FReadEnv].} =
|
proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
|
||||||
## Checks whether the environment variable named `key` exists.
|
## Checks whether the environment variable named `key` exists.
|
||||||
## Returns true if it exists, false otherwise.
|
## Returns true if it exists, false otherwise.
|
||||||
if c_getenv(key) != nil: return true
|
if c_getenv(key) != nil: return true
|
||||||
else: return findEnvVar(key) >= 0
|
else: return findEnvVar(key) >= 0
|
||||||
|
|
||||||
proc putEnv*(key, val: string) {.tags: [FWriteEnv].} =
|
proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
|
||||||
## Sets the value of the `environment variable`:idx: named `key` to `val`.
|
## Sets the value of the `environment variable`:idx: named `key` to `val`.
|
||||||
## If an error occurs, `EInvalidEnvVar` is raised.
|
## If an error occurs, `EInvalidEnvVar` is raised.
|
||||||
|
|
||||||
|
|
@ -1175,7 +1183,7 @@ proc putEnv*(key, val: string) {.tags: [FWriteEnv].} =
|
||||||
else:
|
else:
|
||||||
if setEnvironmentVariableA(key, val) == 0'i32: osError(osLastError())
|
if setEnvironmentVariableA(key, val) == 0'i32: osError(osLastError())
|
||||||
|
|
||||||
iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [FReadEnv].} =
|
iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} =
|
||||||
## Iterate over all `environments variables`:idx:. In the first component
|
## Iterate over all `environments variables`:idx:. In the first component
|
||||||
## of the tuple is the name of the current variable stored, in the second
|
## of the tuple is the name of the current variable stored, in the second
|
||||||
## its value.
|
## its value.
|
||||||
|
|
@ -1185,7 +1193,7 @@ iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [FReadEnv].} =
|
||||||
yield (TaintedString(substr(environment[i], 0, p-1)),
|
yield (TaintedString(substr(environment[i], 0, p-1)),
|
||||||
TaintedString(substr(environment[i], p+1)))
|
TaintedString(substr(environment[i], p+1)))
|
||||||
|
|
||||||
iterator walkFiles*(pattern: string): string {.tags: [FReadDir].} =
|
iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect].} =
|
||||||
## Iterate over all the files that match the `pattern`. On POSIX this uses
|
## Iterate over all the files that match the `pattern`. On POSIX this uses
|
||||||
## the `glob`:idx: call.
|
## the `glob`:idx: call.
|
||||||
##
|
##
|
||||||
|
|
@ -1225,7 +1233,7 @@ type
|
||||||
pcLinkToDir ## path refers to a symbolic link to a directory
|
pcLinkToDir ## path refers to a symbolic link to a directory
|
||||||
|
|
||||||
iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] {.
|
iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] {.
|
||||||
tags: [FReadDir].} =
|
tags: [ReadDirEffect].} =
|
||||||
## walks over the directory `dir` and yields for each directory or file in
|
## walks over the directory `dir` and yields for each directory or file in
|
||||||
## `dir`. The component type and full path for each item is returned.
|
## `dir`. The component type and full path for each item is returned.
|
||||||
## Walking is not recursive.
|
## Walking is not recursive.
|
||||||
|
|
@ -1278,7 +1286,7 @@ iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] {.
|
||||||
discard closedir(d)
|
discard closedir(d)
|
||||||
|
|
||||||
iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string {.
|
iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string {.
|
||||||
tags: [FReadDir].} =
|
tags: [ReadDirEffect].} =
|
||||||
## walks over the directory `dir` and yields for each file in `dir`. The
|
## walks over the directory `dir` and yields for each file in `dir`. The
|
||||||
## full path for each file is returned.
|
## full path for each file is returned.
|
||||||
## **Warning**:
|
## **Warning**:
|
||||||
|
|
@ -1318,7 +1326,7 @@ proc rawRemoveDir(dir: string) =
|
||||||
if rmdir(dir) != 0'i32 and errno != ENOENT: osError(osLastError())
|
if rmdir(dir) != 0'i32 and errno != ENOENT: osError(osLastError())
|
||||||
|
|
||||||
proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [
|
proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [
|
||||||
FWriteDir, FReadDir].} =
|
WriteDirEffect, ReadDirEffect].} =
|
||||||
## Removes the directory `dir` including all subdirectories and files
|
## Removes the directory `dir` including all subdirectories and files
|
||||||
## in `dir` (recursively).
|
## in `dir` (recursively).
|
||||||
##
|
##
|
||||||
|
|
@ -1345,7 +1353,7 @@ proc rawCreateDir(dir: string) =
|
||||||
if res == 0'i32 and getLastError() != 183'i32:
|
if res == 0'i32 and getLastError() != 183'i32:
|
||||||
osError(osLastError())
|
osError(osLastError())
|
||||||
|
|
||||||
proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} =
|
proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} =
|
||||||
## Creates the `directory`:idx: `dir`.
|
## Creates the `directory`:idx: `dir`.
|
||||||
##
|
##
|
||||||
## The directory may contain several subdirectories that do not exist yet.
|
## The directory may contain several subdirectories that do not exist yet.
|
||||||
|
|
@ -1568,7 +1576,7 @@ proc copyDirWithPermissions*(source, dest: string,
|
||||||
|
|
||||||
proc inclFilePermissions*(filename: string,
|
proc inclFilePermissions*(filename: string,
|
||||||
permissions: set[TFilePermission]) {.
|
permissions: set[TFilePermission]) {.
|
||||||
rtl, extern: "nos$1", tags: [FReadDir, FWriteDir].} =
|
rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect].} =
|
||||||
## a convenience procedure for:
|
## a convenience procedure for:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nimrod
|
||||||
|
|
@ -1577,14 +1585,14 @@ proc inclFilePermissions*(filename: string,
|
||||||
|
|
||||||
proc exclFilePermissions*(filename: string,
|
proc exclFilePermissions*(filename: string,
|
||||||
permissions: set[TFilePermission]) {.
|
permissions: set[TFilePermission]) {.
|
||||||
rtl, extern: "nos$1", tags: [FReadDir, FWriteDir].} =
|
rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect].} =
|
||||||
## a convenience procedure for:
|
## a convenience procedure for:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nimrod
|
||||||
## setFilePermissions(filename, getFilePermissions(filename)-permissions)
|
## setFilePermissions(filename, getFilePermissions(filename)-permissions)
|
||||||
setFilePermissions(filename, getFilePermissions(filename)-permissions)
|
setFilePermissions(filename, getFilePermissions(filename)-permissions)
|
||||||
|
|
||||||
proc getHomeDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} =
|
proc getHomeDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect].} =
|
||||||
## Returns the home directory of the current user.
|
## Returns the home directory of the current user.
|
||||||
##
|
##
|
||||||
## This proc is wrapped by the expandTilde proc for the convenience of
|
## This proc is wrapped by the expandTilde proc for the convenience of
|
||||||
|
|
@ -1592,12 +1600,12 @@ proc getHomeDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} =
|
||||||
when defined(windows): return string(getEnv("USERPROFILE")) & "\\"
|
when defined(windows): return string(getEnv("USERPROFILE")) & "\\"
|
||||||
else: return string(getEnv("HOME")) & "/"
|
else: return string(getEnv("HOME")) & "/"
|
||||||
|
|
||||||
proc getConfigDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} =
|
proc getConfigDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect].} =
|
||||||
## Returns the config directory of the current user for applications.
|
## Returns the config directory of the current user for applications.
|
||||||
when defined(windows): return string(getEnv("APPDATA")) & "\\"
|
when defined(windows): return string(getEnv("APPDATA")) & "\\"
|
||||||
else: return string(getEnv("HOME")) & "/.config/"
|
else: return string(getEnv("HOME")) & "/.config/"
|
||||||
|
|
||||||
proc getTempDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} =
|
proc getTempDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect].} =
|
||||||
## Returns the temporary directory of the current user for applications to
|
## Returns the temporary directory of the current user for applications to
|
||||||
## save temporary files in.
|
## save temporary files in.
|
||||||
when defined(windows): return string(getEnv("TEMP")) & "\\"
|
when defined(windows): return string(getEnv("TEMP")) & "\\"
|
||||||
|
|
@ -1605,7 +1613,7 @@ proc getTempDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} =
|
||||||
|
|
||||||
when defined(nimdoc):
|
when defined(nimdoc):
|
||||||
# Common forward declaration docstring block for parameter retrieval procs.
|
# Common forward declaration docstring block for parameter retrieval procs.
|
||||||
proc paramCount*(): int {.tags: [FReadIO].} =
|
proc paramCount*(): int {.tags: [ReadIOEffect].} =
|
||||||
## Returns the number of `command line arguments`:idx: given to the
|
## Returns the number of `command line arguments`:idx: given to the
|
||||||
## application.
|
## application.
|
||||||
##
|
##
|
||||||
|
|
@ -1625,7 +1633,7 @@ when defined(nimdoc):
|
||||||
## else:
|
## else:
|
||||||
## # Do something else!
|
## # Do something else!
|
||||||
|
|
||||||
proc paramStr*(i: int): TaintedString {.tags: [FReadIO].} =
|
proc paramStr*(i: int): TaintedString {.tags: [ReadIOEffect].} =
|
||||||
## Returns the `i`-th `command line argument`:idx: given to the application.
|
## Returns the `i`-th `command line argument`:idx: given to the application.
|
||||||
##
|
##
|
||||||
## `i` should be in the range `1..paramCount()`, the `EInvalidIndex`
|
## `i` should be in the range `1..paramCount()`, the `EInvalidIndex`
|
||||||
|
|
@ -1826,7 +1834,7 @@ proc getFileSize*(file: string): BiggestInt {.rtl, extern: "nos$1",
|
||||||
close(f)
|
close(f)
|
||||||
else: osError(osLastError())
|
else: osError(osLastError())
|
||||||
|
|
||||||
proc findExe*(exe: string): string {.tags: [FReadDir, FReadEnv].} =
|
proc findExe*(exe: string): string {.tags: [ReadDirEffect, ReadEnvEffect].} =
|
||||||
## Searches for `exe` in the current working directory and then
|
## Searches for `exe` in the current working directory and then
|
||||||
## in directories listed in the ``PATH`` environment variable.
|
## in directories listed in the ``PATH`` environment variable.
|
||||||
## Returns "" if the `exe` cannot be found. On DOS-like platforms, `exe`
|
## Returns "" if the `exe` cannot be found. On DOS-like platforms, `exe`
|
||||||
|
|
@ -1854,7 +1862,6 @@ proc expandTilde*(path: string): string =
|
||||||
## let configFile = expandTilde("~" / "appname.cfg")
|
## let configFile = expandTilde("~" / "appname.cfg")
|
||||||
## echo configFile
|
## echo configFile
|
||||||
## # --> C:\Users\amber\appname.cfg
|
## # --> C:\Users\amber\appname.cfg
|
||||||
|
|
||||||
if len(path) > 1 and path[0] == '~' and (path[1] == '/' or path[1] == '\\'):
|
if len(path) > 1 and path[0] == '~' and (path[1] == '/' or path[1] == '\\'):
|
||||||
result = getHomeDir() / path[2..len(path)-1]
|
result = getHomeDir() / path[2..len(path)-1]
|
||||||
else:
|
else:
|
||||||
|
|
@ -1961,7 +1968,7 @@ proc getFileInfo*(handle: FileHandle): FileInfo =
|
||||||
rawToFormalFileInfo(rawInfo, result)
|
rawToFormalFileInfo(rawInfo, result)
|
||||||
|
|
||||||
proc getFileInfo*(file: File): FileInfo =
|
proc getFileInfo*(file: File): FileInfo =
|
||||||
result = getFileInfo(file.fileHandle())
|
result = getFileInfo(file.getFileHandle())
|
||||||
|
|
||||||
proc getFileInfo*(path: string, followSymlink = true): FileInfo =
|
proc getFileInfo*(path: string, followSymlink = true): FileInfo =
|
||||||
## Retrieves file information for the file object pointed to by `path`.
|
## Retrieves file information for the file object pointed to by `path`.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2014 Andreas Rumpf
|
# (c) Copyright 2014 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -24,7 +24,7 @@ when defined(linux):
|
||||||
import linux
|
import linux
|
||||||
|
|
||||||
type
|
type
|
||||||
TProcess = object of RootObj
|
ProcessObj = object of RootObj
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
fProcessHandle: THandle
|
fProcessHandle: THandle
|
||||||
inHandle, outHandle, errHandle: FileHandle
|
inHandle, outHandle, errHandle: FileHandle
|
||||||
|
|
@ -35,9 +35,9 @@ type
|
||||||
id: TPid
|
id: TPid
|
||||||
exitCode: cint
|
exitCode: cint
|
||||||
|
|
||||||
PProcess* = ref TProcess ## represents an operating system process
|
Process* = ref ProcessObj ## represents an operating system process
|
||||||
|
|
||||||
TProcessOption* = enum ## options that can be passed `startProcess`
|
ProcessOption* = enum ## options that can be passed `startProcess`
|
||||||
poEchoCmd, ## echo the command before execution
|
poEchoCmd, ## echo the command before execution
|
||||||
poUsePath, ## Asks system to search for executable using PATH environment
|
poUsePath, ## Asks system to search for executable using PATH environment
|
||||||
## variable.
|
## variable.
|
||||||
|
|
@ -47,6 +47,9 @@ type
|
||||||
poStdErrToStdOut, ## merge stdout and stderr to the stdout stream
|
poStdErrToStdOut, ## merge stdout and stderr to the stdout stream
|
||||||
poParentStreams ## use the parent's streams
|
poParentStreams ## use the parent's streams
|
||||||
|
|
||||||
|
{.deprecated: [TProcess: ProcessObj, PProcess: Process,
|
||||||
|
TProcessOption: ProcessOption].}
|
||||||
|
|
||||||
const poUseShell* {.deprecated.} = poUsePath
|
const poUseShell* {.deprecated.} = poUsePath
|
||||||
## Deprecated alias for poUsePath.
|
## Deprecated alias for poUsePath.
|
||||||
|
|
||||||
|
|
@ -125,7 +128,7 @@ proc startProcess*(command: string,
|
||||||
args: openArray[string] = [],
|
args: openArray[string] = [],
|
||||||
env: PStringTable = nil,
|
env: PStringTable = nil,
|
||||||
options: set[TProcessOption] = {poStdErrToStdOut}):
|
options: set[TProcessOption] = {poStdErrToStdOut}):
|
||||||
PProcess {.rtl, extern: "nosp$1", tags: [ExecIOEffect, FReadEnv].}
|
PProcess {.rtl, extern: "nosp$1", tags: [ExecIOEffect, ReadEnvEffect].}
|
||||||
## Starts a process. `Command` is the executable file, `workingDir` is the
|
## Starts a process. `Command` is the executable file, `workingDir` is the
|
||||||
## process's working directory. If ``workingDir == ""`` the current directory
|
## process's working directory. If ``workingDir == ""`` the current directory
|
||||||
## is used. `args` are the command line arguments that are passed to the
|
## is used. `args` are the command line arguments that are passed to the
|
||||||
|
|
@ -150,7 +153,7 @@ proc startProcess*(command: string,
|
||||||
|
|
||||||
proc startCmd*(command: string, options: set[TProcessOption] = {
|
proc startCmd*(command: string, options: set[TProcessOption] = {
|
||||||
poStdErrToStdOut, poUsePath}): PProcess {.
|
poStdErrToStdOut, poUsePath}): PProcess {.
|
||||||
tags: [ExecIOEffect, FReadEnv], deprecated.} =
|
tags: [ExecIOEffect, ReadEnvEffect], deprecated.} =
|
||||||
## Deprecated - use `startProcess` directly.
|
## Deprecated - use `startProcess` directly.
|
||||||
result = startProcess(command=command, options=options + {poEvalCommand})
|
result = startProcess(command=command, options=options + {poEvalCommand})
|
||||||
|
|
||||||
|
|
@ -233,7 +236,7 @@ proc countProcessors*(): int {.rtl, extern: "nosp$1".} =
|
||||||
proc execProcesses*(cmds: openArray[string],
|
proc execProcesses*(cmds: openArray[string],
|
||||||
options = {poStdErrToStdOut, poParentStreams},
|
options = {poStdErrToStdOut, poParentStreams},
|
||||||
n = countProcessors()): int {.rtl, extern: "nosp$1",
|
n = countProcessors()): int {.rtl, extern: "nosp$1",
|
||||||
tags: [ExecIOEffect, TimeEffect, FReadEnv]} =
|
tags: [ExecIOEffect, TimeEffect, ReadEnvEffect]} =
|
||||||
## executes the commands `cmds` in parallel. Creates `n` processes
|
## executes the commands `cmds` in parallel. Creates `n` processes
|
||||||
## that execute in parallel. The highest return value of all processes
|
## that execute in parallel. The highest return value of all processes
|
||||||
## is returned.
|
## is returned.
|
||||||
|
|
@ -584,12 +587,12 @@ elif not defined(useNimRtl):
|
||||||
|
|
||||||
when not defined(useFork):
|
when not defined(useFork):
|
||||||
proc startProcessAuxSpawn(data: TStartProcessData): TPid {.
|
proc startProcessAuxSpawn(data: TStartProcessData): TPid {.
|
||||||
tags: [ExecIOEffect, FReadEnv], gcsafe.}
|
tags: [ExecIOEffect, ReadEnvEffect], gcsafe.}
|
||||||
proc startProcessAuxFork(data: TStartProcessData): TPid {.
|
proc startProcessAuxFork(data: TStartProcessData): TPid {.
|
||||||
tags: [ExecIOEffect, FReadEnv], gcsafe.}
|
tags: [ExecIOEffect, ReadEnvEffect], gcsafe.}
|
||||||
{.push stacktrace: off, profiler: off.}
|
{.push stacktrace: off, profiler: off.}
|
||||||
proc startProcessAfterFork(data: ptr TStartProcessData) {.
|
proc startProcessAfterFork(data: ptr TStartProcessData) {.
|
||||||
tags: [ExecIOEffect, FReadEnv], cdecl, gcsafe.}
|
tags: [ExecIOEffect, ReadEnvEffect], cdecl, gcsafe.}
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
proc startProcess(command: string,
|
proc startProcess(command: string,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2010 Andreas Rumpf
|
# (c) Copyright 2010 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
## The file ``examples/parsecfgex.nim`` demonstrates how to use the
|
## The file ``examples/parsecfgex.nim`` demonstrates how to use the
|
||||||
## configuration file parser:
|
## configuration file parser:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## :file: examples/parsecfgex.nim
|
## :file: examples/parsecfgex.nim
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -30,14 +30,14 @@ import
|
||||||
include "system/inclrtl"
|
include "system/inclrtl"
|
||||||
|
|
||||||
type
|
type
|
||||||
TCfgEventKind* = enum ## enumeration of all events that may occur when parsing
|
CfgEventKind* = enum ## enumeration of all events that may occur when parsing
|
||||||
cfgEof, ## end of file reached
|
cfgEof, ## end of file reached
|
||||||
cfgSectionStart, ## a ``[section]`` has been parsed
|
cfgSectionStart, ## a ``[section]`` has been parsed
|
||||||
cfgKeyValuePair, ## a ``key=value`` pair has been detected
|
cfgKeyValuePair, ## a ``key=value`` pair has been detected
|
||||||
cfgOption, ## a ``--key=value`` command line option
|
cfgOption, ## a ``--key=value`` command line option
|
||||||
cfgError ## an error ocurred during parsing
|
cfgError ## an error ocurred during parsing
|
||||||
|
|
||||||
TCfgEvent* = object of TObject ## describes a parsing event
|
CfgEvent* = object of RootObj ## describes a parsing event
|
||||||
case kind*: TCfgEventKind ## the kind of the event
|
case kind*: TCfgEventKind ## the kind of the event
|
||||||
of cfgEof: nil
|
of cfgEof: nil
|
||||||
of cfgSectionStart:
|
of cfgSectionStart:
|
||||||
|
|
@ -53,28 +53,30 @@ type
|
||||||
msg*: string ## contains the error message. No exceptions
|
msg*: string ## contains the error message. No exceptions
|
||||||
## are thrown if a parse error occurs.
|
## are thrown if a parse error occurs.
|
||||||
|
|
||||||
TTokKind = enum
|
TokKind = enum
|
||||||
tkInvalid, tkEof,
|
tkInvalid, tkEof,
|
||||||
tkSymbol, tkEquals, tkColon, tkBracketLe, tkBracketRi, tkDashDash
|
tkSymbol, tkEquals, tkColon, tkBracketLe, tkBracketRi, tkDashDash
|
||||||
TToken {.final.} = object # a token
|
Token = object # a token
|
||||||
kind: TTokKind # the type of the token
|
kind: TokKind # the type of the token
|
||||||
literal: string # the parsed (string) literal
|
literal: string # the parsed (string) literal
|
||||||
|
|
||||||
TCfgParser* = object of TBaseLexer ## the parser object.
|
CfgParser* = object of BaseLexer ## the parser object.
|
||||||
tok: TToken
|
tok: Token
|
||||||
filename: string
|
filename: string
|
||||||
|
|
||||||
|
{.deprecated: [TCfgEventKind: CfgEventKind, TCfgEvent: CfgEvent,
|
||||||
|
TTokKind: TokKind, TToken: Token, TCfgParser: CfgParser].}
|
||||||
|
|
||||||
# implementation
|
# implementation
|
||||||
|
|
||||||
const
|
const
|
||||||
SymChars: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF', '.',
|
SymChars: CharSet = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF', '.',
|
||||||
'/', '\\'}
|
'/', '\\'}
|
||||||
|
|
||||||
proc rawGetTok(c: var TCfgParser, tok: var TToken) {.gcsafe.}
|
proc rawGetTok(c: var CfgParser, tok: var Token) {.gcsafe.}
|
||||||
|
|
||||||
proc open*(c: var TCfgParser, input: PStream, filename: string,
|
proc open*(c: var CfgParser, input: PStream, filename: string,
|
||||||
lineOffset = 0) {.
|
lineOffset = 0) {.rtl, extern: "npc$1".} =
|
||||||
rtl, extern: "npc$1".} =
|
|
||||||
## initializes the parser with an input stream. `Filename` is only used
|
## initializes the parser with an input stream. `Filename` is only used
|
||||||
## for nice error messages. `lineOffset` can be used to influence the line
|
## for nice error messages. `lineOffset` can be used to influence the line
|
||||||
## number information in the generated error messages.
|
## number information in the generated error messages.
|
||||||
|
|
@ -85,23 +87,23 @@ proc open*(c: var TCfgParser, input: PStream, filename: string,
|
||||||
inc(c.lineNumber, lineOffset)
|
inc(c.lineNumber, lineOffset)
|
||||||
rawGetTok(c, c.tok)
|
rawGetTok(c, c.tok)
|
||||||
|
|
||||||
proc close*(c: var TCfgParser) {.rtl, extern: "npc$1".} =
|
proc close*(c: var CfgParser) {.rtl, extern: "npc$1".} =
|
||||||
## closes the parser `c` and its associated input stream.
|
## closes the parser `c` and its associated input stream.
|
||||||
lexbase.close(c)
|
lexbase.close(c)
|
||||||
|
|
||||||
proc getColumn*(c: TCfgParser): int {.rtl, extern: "npc$1".} =
|
proc getColumn*(c: CfgParser): int {.rtl, extern: "npc$1".} =
|
||||||
## get the current column the parser has arrived at.
|
## get the current column the parser has arrived at.
|
||||||
result = getColNumber(c, c.bufpos)
|
result = getColNumber(c, c.bufpos)
|
||||||
|
|
||||||
proc getLine*(c: TCfgParser): int {.rtl, extern: "npc$1".} =
|
proc getLine*(c: CfgParser): int {.rtl, extern: "npc$1".} =
|
||||||
## get the current line the parser has arrived at.
|
## get the current line the parser has arrived at.
|
||||||
result = c.lineNumber
|
result = c.lineNumber
|
||||||
|
|
||||||
proc getFilename*(c: TCfgParser): string {.rtl, extern: "npc$1".} =
|
proc getFilename*(c: CfgParser): string {.rtl, extern: "npc$1".} =
|
||||||
## get the filename of the file that the parser processes.
|
## get the filename of the file that the parser processes.
|
||||||
result = c.filename
|
result = c.filename
|
||||||
|
|
||||||
proc handleHexChar(c: var TCfgParser, xi: var int) =
|
proc handleHexChar(c: var CfgParser, xi: var int) =
|
||||||
case c.buf[c.bufpos]
|
case c.buf[c.bufpos]
|
||||||
of '0'..'9':
|
of '0'..'9':
|
||||||
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0'))
|
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0'))
|
||||||
|
|
@ -115,12 +117,12 @@ proc handleHexChar(c: var TCfgParser, xi: var int) =
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc handleDecChars(c: var TCfgParser, xi: var int) =
|
proc handleDecChars(c: var CfgParser, xi: var int) =
|
||||||
while c.buf[c.bufpos] in {'0'..'9'}:
|
while c.buf[c.bufpos] in {'0'..'9'}:
|
||||||
xi = (xi * 10) + (ord(c.buf[c.bufpos]) - ord('0'))
|
xi = (xi * 10) + (ord(c.buf[c.bufpos]) - ord('0'))
|
||||||
inc(c.bufpos)
|
inc(c.bufpos)
|
||||||
|
|
||||||
proc getEscapedChar(c: var TCfgParser, tok: var TToken) =
|
proc getEscapedChar(c: var CfgParser, tok: var TToken) =
|
||||||
inc(c.bufpos) # skip '\'
|
inc(c.bufpos) # skip '\'
|
||||||
case c.buf[c.bufpos]
|
case c.buf[c.bufpos]
|
||||||
of 'n', 'N':
|
of 'n', 'N':
|
||||||
|
|
@ -169,13 +171,13 @@ proc getEscapedChar(c: var TCfgParser, tok: var TToken) =
|
||||||
else: tok.kind = tkInvalid
|
else: tok.kind = tkInvalid
|
||||||
else: tok.kind = tkInvalid
|
else: tok.kind = tkInvalid
|
||||||
|
|
||||||
proc handleCRLF(c: var TCfgParser, pos: int): int =
|
proc handleCRLF(c: var CfgParser, pos: int): int =
|
||||||
case c.buf[pos]
|
case c.buf[pos]
|
||||||
of '\c': result = lexbase.handleCR(c, pos)
|
of '\c': result = lexbase.handleCR(c, pos)
|
||||||
of '\L': result = lexbase.handleLF(c, pos)
|
of '\L': result = lexbase.handleLF(c, pos)
|
||||||
else: result = pos
|
else: result = pos
|
||||||
|
|
||||||
proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
|
proc getString(c: var CfgParser, tok: var Token, rawMode: bool) =
|
||||||
var pos = c.bufpos + 1 # skip "
|
var pos = c.bufpos + 1 # skip "
|
||||||
var buf = c.buf # put `buf` in a register
|
var buf = c.buf # put `buf` in a register
|
||||||
tok.kind = tkSymbol
|
tok.kind = tkSymbol
|
||||||
|
|
@ -221,7 +223,7 @@ proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
|
||||||
inc(pos)
|
inc(pos)
|
||||||
c.bufpos = pos
|
c.bufpos = pos
|
||||||
|
|
||||||
proc getSymbol(c: var TCfgParser, tok: var TToken) =
|
proc getSymbol(c: var CfgParser, tok: var Token) =
|
||||||
var pos = c.bufpos
|
var pos = c.bufpos
|
||||||
var buf = c.buf
|
var buf = c.buf
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -231,7 +233,7 @@ proc getSymbol(c: var TCfgParser, tok: var TToken) =
|
||||||
c.bufpos = pos
|
c.bufpos = pos
|
||||||
tok.kind = tkSymbol
|
tok.kind = tkSymbol
|
||||||
|
|
||||||
proc skip(c: var TCfgParser) =
|
proc skip(c: var CfgParser) =
|
||||||
var pos = c.bufpos
|
var pos = c.bufpos
|
||||||
var buf = c.buf
|
var buf = c.buf
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -247,7 +249,7 @@ proc skip(c: var TCfgParser) =
|
||||||
break # EndOfFile also leaves the loop
|
break # EndOfFile also leaves the loop
|
||||||
c.bufpos = pos
|
c.bufpos = pos
|
||||||
|
|
||||||
proc rawGetTok(c: var TCfgParser, tok: var TToken) =
|
proc rawGetTok(c: var CfgParser, tok: var Token) =
|
||||||
tok.kind = tkInvalid
|
tok.kind = tkInvalid
|
||||||
setLen(tok.literal, 0)
|
setLen(tok.literal, 0)
|
||||||
skip(c)
|
skip(c)
|
||||||
|
|
@ -286,19 +288,19 @@ proc rawGetTok(c: var TCfgParser, tok: var TToken) =
|
||||||
tok.literal = "[EOF]"
|
tok.literal = "[EOF]"
|
||||||
else: getSymbol(c, tok)
|
else: getSymbol(c, tok)
|
||||||
|
|
||||||
proc errorStr*(c: TCfgParser, msg: string): string {.rtl, extern: "npc$1".} =
|
proc errorStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} =
|
||||||
## returns a properly formated error message containing current line and
|
## returns a properly formated error message containing current line and
|
||||||
## column information.
|
## column information.
|
||||||
result = `%`("$1($2, $3) Error: $4",
|
result = `%`("$1($2, $3) Error: $4",
|
||||||
[c.filename, $getLine(c), $getColumn(c), msg])
|
[c.filename, $getLine(c), $getColumn(c), msg])
|
||||||
|
|
||||||
proc warningStr*(c: TCfgParser, msg: string): string {.rtl, extern: "npc$1".} =
|
proc warningStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} =
|
||||||
## returns a properly formated warning message containing current line and
|
## returns a properly formated warning message containing current line and
|
||||||
## column information.
|
## column information.
|
||||||
result = `%`("$1($2, $3) Warning: $4",
|
result = `%`("$1($2, $3) Warning: $4",
|
||||||
[c.filename, $getLine(c), $getColumn(c), msg])
|
[c.filename, $getLine(c), $getColumn(c), msg])
|
||||||
|
|
||||||
proc ignoreMsg*(c: TCfgParser, e: TCfgEvent): string {.rtl, extern: "npc$1".} =
|
proc ignoreMsg*(c: CfgParser, e: CfgEvent): string {.rtl, extern: "npc$1".} =
|
||||||
## returns a properly formated warning message containing that
|
## returns a properly formated warning message containing that
|
||||||
## an entry is ignored.
|
## an entry is ignored.
|
||||||
case e.kind
|
case e.kind
|
||||||
|
|
@ -309,7 +311,7 @@ proc ignoreMsg*(c: TCfgParser, e: TCfgEvent): string {.rtl, extern: "npc$1".} =
|
||||||
of cfgError: result = e.msg
|
of cfgError: result = e.msg
|
||||||
of cfgEof: result = ""
|
of cfgEof: result = ""
|
||||||
|
|
||||||
proc getKeyValPair(c: var TCfgParser, kind: TCfgEventKind): TCfgEvent =
|
proc getKeyValPair(c: var CfgParser, kind: CfgEventKind): CfgEvent =
|
||||||
if c.tok.kind == tkSymbol:
|
if c.tok.kind == tkSymbol:
|
||||||
result.kind = kind
|
result.kind = kind
|
||||||
result.key = c.tok.literal
|
result.key = c.tok.literal
|
||||||
|
|
@ -329,7 +331,7 @@ proc getKeyValPair(c: var TCfgParser, kind: TCfgEventKind): TCfgEvent =
|
||||||
result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
|
result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
|
||||||
rawGetTok(c, c.tok)
|
rawGetTok(c, c.tok)
|
||||||
|
|
||||||
proc next*(c: var TCfgParser): TCfgEvent {.rtl, extern: "npc$1".} =
|
proc next*(c: var CfgParser): CfgEvent {.rtl, extern: "npc$1".} =
|
||||||
## retrieves the first/next event. This controls the parser.
|
## retrieves the first/next event. This controls the parser.
|
||||||
case c.tok.kind
|
case c.tok.kind
|
||||||
of tkEof:
|
of tkEof:
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2009 Andreas Rumpf
|
# (c) Copyright 2009 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -13,16 +13,16 @@
|
||||||
## Example: How to use the parser
|
## Example: How to use the parser
|
||||||
## ==============================
|
## ==============================
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## import os, parsecsv, streams
|
## import os, parsecsv, streams
|
||||||
## var s = newFileStream(ParamStr(1), fmRead)
|
## var s = newFileStream(paramStr(1), fmRead)
|
||||||
## if s == nil: quit("cannot open the file" & ParamStr(1))
|
## if s == nil: quit("cannot open the file" & paramStr(1))
|
||||||
## var x: TCsvParser
|
## var x: CsvParser
|
||||||
## open(x, s, ParamStr(1))
|
## open(x, s, paramStr(1))
|
||||||
## while readRow(x):
|
## while readRow(x):
|
||||||
## Echo "new row: "
|
## echo "new row: "
|
||||||
## for val in items(x.row):
|
## for val in items(x.row):
|
||||||
## Echo "##", val, "##"
|
## echo "##", val, "##"
|
||||||
## close(x)
|
## close(x)
|
||||||
##
|
##
|
||||||
|
|
||||||
|
|
@ -30,28 +30,30 @@ import
|
||||||
lexbase, streams
|
lexbase, streams
|
||||||
|
|
||||||
type
|
type
|
||||||
TCsvRow* = seq[string] ## a row in a CSV file
|
CsvRow* = seq[string] ## a row in a CSV file
|
||||||
TCsvParser* = object of TBaseLexer ## the parser object.
|
CsvParser* = object of BaseLexer ## the parser object.
|
||||||
row*: TCsvRow ## the current row
|
row*: CsvRow ## the current row
|
||||||
filename: string
|
filename: string
|
||||||
sep, quote, esc: char
|
sep, quote, esc: char
|
||||||
skipWhite: bool
|
skipWhite: bool
|
||||||
currRow: int
|
currRow: int
|
||||||
|
|
||||||
EInvalidCsv* = object of EIO ## exception that is raised if
|
CsvError* = object of IOError ## exception that is raised if
|
||||||
## a parsing error occurs
|
## a parsing error occurs
|
||||||
|
|
||||||
|
{.deprecated: [TCsvRow: CsvRow, TCsvParser: CsvParser, EInvalidCsv: CsvError].}
|
||||||
|
|
||||||
proc raiseEInvalidCsv(filename: string, line, col: int,
|
proc raiseEInvalidCsv(filename: string, line, col: int,
|
||||||
msg: string) {.noreturn.} =
|
msg: string) {.noreturn.} =
|
||||||
var e: ref EInvalidCsv
|
var e: ref CsvError
|
||||||
new(e)
|
new(e)
|
||||||
e.msg = filename & "(" & $line & ", " & $col & ") Error: " & msg
|
e.msg = filename & "(" & $line & ", " & $col & ") Error: " & msg
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
proc error(my: TCsvParser, pos: int, msg: string) =
|
proc error(my: CsvParser, pos: int, msg: string) =
|
||||||
raiseEInvalidCsv(my.filename, my.LineNumber, getColNumber(my, pos), msg)
|
raiseEInvalidCsv(my.filename, my.LineNumber, getColNumber(my, pos), msg)
|
||||||
|
|
||||||
proc open*(my: var TCsvParser, input: PStream, filename: string,
|
proc open*(my: var CsvParser, input: Stream, filename: string,
|
||||||
separator = ',', quote = '"', escape = '\0',
|
separator = ',', quote = '"', escape = '\0',
|
||||||
skipInitialSpace = false) =
|
skipInitialSpace = false) =
|
||||||
## initializes the parser with an input stream. `Filename` is only used
|
## initializes the parser with an input stream. `Filename` is only used
|
||||||
|
|
@ -75,7 +77,7 @@ proc open*(my: var TCsvParser, input: PStream, filename: string,
|
||||||
my.row = @[]
|
my.row = @[]
|
||||||
my.currRow = 0
|
my.currRow = 0
|
||||||
|
|
||||||
proc parseField(my: var TCsvParser, a: var string) =
|
proc parseField(my: var CsvParser, a: var string) =
|
||||||
var pos = my.bufpos
|
var pos = my.bufpos
|
||||||
var buf = my.buf
|
var buf = my.buf
|
||||||
if my.skipWhite:
|
if my.skipWhite:
|
||||||
|
|
@ -121,11 +123,11 @@ proc parseField(my: var TCsvParser, a: var string) =
|
||||||
inc(pos)
|
inc(pos)
|
||||||
my.bufpos = pos
|
my.bufpos = pos
|
||||||
|
|
||||||
proc processedRows*(my: var TCsvParser): int =
|
proc processedRows*(my: var CsvParser): int =
|
||||||
## returns number of the processed rows
|
## returns number of the processed rows
|
||||||
return my.currRow
|
return my.currRow
|
||||||
|
|
||||||
proc readRow*(my: var TCsvParser, columns = 0): bool =
|
proc readRow*(my: var CsvParser, columns = 0): bool =
|
||||||
## reads the next row; if `columns` > 0, it expects the row to have
|
## reads the next row; if `columns` > 0, it expects the row to have
|
||||||
## exactly this many columns. Returns false if the end of the file
|
## exactly this many columns. Returns false if the end of the file
|
||||||
## has been encountered else true.
|
## has been encountered else true.
|
||||||
|
|
@ -160,19 +162,19 @@ proc readRow*(my: var TCsvParser, columns = 0): bool =
|
||||||
$col & " columns")
|
$col & " columns")
|
||||||
inc(my.currRow)
|
inc(my.currRow)
|
||||||
|
|
||||||
proc close*(my: var TCsvParser) {.inline.} =
|
proc close*(my: var CsvParser) {.inline.} =
|
||||||
## closes the parser `my` and its associated input stream.
|
## closes the parser `my` and its associated input stream.
|
||||||
lexbase.close(my)
|
lexbase.close(my)
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
import os
|
import os
|
||||||
var s = newFileStream(ParamStr(1), fmRead)
|
var s = newFileStream(paramStr(1), fmRead)
|
||||||
if s == nil: quit("cannot open the file" & ParamStr(1))
|
if s == nil: quit("cannot open the file" & paramStr(1))
|
||||||
var x: TCsvParser
|
var x: CsvParser
|
||||||
open(x, s, ParamStr(1))
|
open(x, s, paramStr(1))
|
||||||
while readRow(x):
|
while readRow(x):
|
||||||
Echo "new row: "
|
echo "new row: "
|
||||||
for val in items(x.row):
|
for val in items(x.row):
|
||||||
Echo "##", val, "##"
|
echo "##", val, "##"
|
||||||
close(x)
|
close(x)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2014 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## This module provides the standard Nimrod command line parser.
|
## This module provides the standard Nim command line parser.
|
||||||
## It supports one convenience iterator over all command line options and some
|
## It supports one convenience iterator over all command line options and some
|
||||||
## lower-level features.
|
## lower-level features.
|
||||||
##
|
##
|
||||||
|
|
@ -22,26 +22,28 @@ import
|
||||||
os, strutils
|
os, strutils
|
||||||
|
|
||||||
type
|
type
|
||||||
TCmdLineKind* = enum ## the detected command line token
|
CmdLineKind* = enum ## the detected command line token
|
||||||
cmdEnd, ## end of command line reached
|
cmdEnd, ## end of command line reached
|
||||||
cmdArgument, ## argument detected
|
cmdArgument, ## argument detected
|
||||||
cmdLongoption, ## a long option ``--option`` detected
|
cmdLongoption, ## a long option ``--option`` detected
|
||||||
cmdShortOption ## a short option ``-c`` detected
|
cmdShortOption ## a short option ``-c`` detected
|
||||||
TOptParser* =
|
OptParser* =
|
||||||
object of RootObj ## this object implements the command line parser
|
object of RootObj ## this object implements the command line parser
|
||||||
cmd: string
|
cmd: string
|
||||||
pos: int
|
pos: int
|
||||||
inShortState: bool
|
inShortState: bool
|
||||||
kind*: TCmdLineKind ## the dected command line token
|
kind*: CmdLineKind ## the dected command line token
|
||||||
key*, val*: TaintedString ## key and value pair; ``key`` is the option
|
key*, val*: TaintedString ## key and value pair; ``key`` is the option
|
||||||
## or the argument, ``value`` is not "" if
|
## or the argument, ``value`` is not "" if
|
||||||
## the option was given a value
|
## the option was given a value
|
||||||
|
|
||||||
|
{.deprecated: [TCmdLineKind: CmdLineKind, TOptParser: OptParser].}
|
||||||
|
|
||||||
when declared(os.paramCount):
|
when declared(os.paramCount):
|
||||||
# we cannot provide this for NimRtl creation on Posix, because we can't
|
# we cannot provide this for NimRtl creation on Posix, because we can't
|
||||||
# access the command line arguments then!
|
# access the command line arguments then!
|
||||||
|
|
||||||
proc initOptParser*(cmdline = ""): TOptParser =
|
proc initOptParser*(cmdline = ""): OptParser =
|
||||||
## inits the option parser. If ``cmdline == ""``, the real command line
|
## inits the option parser. If ``cmdline == ""``, the real command line
|
||||||
## (as provided by the ``OS`` module) is taken.
|
## (as provided by the ``OS`` module) is taken.
|
||||||
result.pos = 0
|
result.pos = 0
|
||||||
|
|
@ -57,7 +59,7 @@ when declared(os.paramCount):
|
||||||
result.val = TaintedString""
|
result.val = TaintedString""
|
||||||
|
|
||||||
proc parseWord(s: string, i: int, w: var string,
|
proc parseWord(s: string, i: int, w: var string,
|
||||||
delim: TCharSet = {'\x09', ' ', '\0'}): int =
|
delim: CharSet = {'\x09', ' ', '\0'}): int =
|
||||||
result = i
|
result = i
|
||||||
if s[result] == '\"':
|
if s[result] == '\"':
|
||||||
inc(result)
|
inc(result)
|
||||||
|
|
@ -70,7 +72,7 @@ proc parseWord(s: string, i: int, w: var string,
|
||||||
add(w, s[result])
|
add(w, s[result])
|
||||||
inc(result)
|
inc(result)
|
||||||
|
|
||||||
proc handleShortOption(p: var TOptParser) =
|
proc handleShortOption(p: var OptParser) =
|
||||||
var i = p.pos
|
var i = p.pos
|
||||||
p.kind = cmdShortOption
|
p.kind = cmdShortOption
|
||||||
add(p.key.string, p.cmd[i])
|
add(p.key.string, p.cmd[i])
|
||||||
|
|
@ -87,8 +89,7 @@ proc handleShortOption(p: var TOptParser) =
|
||||||
if p.cmd[i] == '\0': p.inShortState = false
|
if p.cmd[i] == '\0': p.inShortState = false
|
||||||
p.pos = i
|
p.pos = i
|
||||||
|
|
||||||
proc next*(p: var TOptParser) {.
|
proc next*(p: var OptParser) {.rtl, extern: "npo$1".} =
|
||||||
rtl, extern: "npo$1".} =
|
|
||||||
## parses the first or next option; ``p.kind`` describes what token has been
|
## parses the first or next option; ``p.kind`` describes what token has been
|
||||||
## parsed. ``p.key`` and ``p.val`` are set accordingly.
|
## parsed. ``p.key`` and ``p.val`` are set accordingly.
|
||||||
var i = p.pos
|
var i = p.pos
|
||||||
|
|
@ -122,14 +123,12 @@ proc next*(p: var TOptParser) {.
|
||||||
p.kind = cmdArgument
|
p.kind = cmdArgument
|
||||||
p.pos = parseWord(p.cmd, i, p.key.string)
|
p.pos = parseWord(p.cmd, i, p.key.string)
|
||||||
|
|
||||||
proc cmdLineRest*(p: TOptParser): TaintedString {.
|
proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo$1".} =
|
||||||
rtl, extern: "npo$1".} =
|
|
||||||
## retrieves the rest of the command line that has not been parsed yet.
|
## retrieves the rest of the command line that has not been parsed yet.
|
||||||
result = strip(substr(p.cmd, p.pos, len(p.cmd) - 1)).TaintedString
|
result = strip(substr(p.cmd, p.pos, len(p.cmd) - 1)).TaintedString
|
||||||
|
|
||||||
when declared(initOptParser):
|
when declared(initOptParser):
|
||||||
|
iterator getopt*(): tuple[kind: CmdLineKind, key, val: TaintedString] =
|
||||||
iterator getopt*(): tuple[kind: TCmdLineKind, key, val: TaintedString] =
|
|
||||||
## This is an convenience iterator for iterating over the command line.
|
## This is an convenience iterator for iterating over the command line.
|
||||||
## This uses the TOptParser object. Example:
|
## This uses the TOptParser object. Example:
|
||||||
##
|
##
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2014 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## This module provides the standard Nimrod command line parser.
|
## This module provides the standard Nim command line parser.
|
||||||
## It supports one convenience iterator over all command line options and some
|
## It supports one convenience iterator over all command line options and some
|
||||||
## lower-level features.
|
## lower-level features.
|
||||||
##
|
##
|
||||||
|
|
@ -25,22 +25,24 @@ import
|
||||||
os, strutils
|
os, strutils
|
||||||
|
|
||||||
type
|
type
|
||||||
TCmdLineKind* = enum ## the detected command line token
|
CmdLineKind* = enum ## the detected command line token
|
||||||
cmdEnd, ## end of command line reached
|
cmdEnd, ## end of command line reached
|
||||||
cmdArgument, ## argument detected
|
cmdArgument, ## argument detected
|
||||||
cmdLongOption, ## a long option ``--option`` detected
|
cmdLongOption, ## a long option ``--option`` detected
|
||||||
cmdShortOption ## a short option ``-c`` detected
|
cmdShortOption ## a short option ``-c`` detected
|
||||||
TOptParser* =
|
OptParser* =
|
||||||
object of TObject ## this object implements the command line parser
|
object of TObject ## this object implements the command line parser
|
||||||
cmd: seq[string]
|
cmd: seq[string]
|
||||||
pos: int
|
pos: int
|
||||||
remainingShortOptions: string
|
remainingShortOptions: string
|
||||||
kind*: TCmdLineKind ## the dected command line token
|
kind*: CmdLineKind ## the dected command line token
|
||||||
key*, val*: TaintedString ## key and value pair; ``key`` is the option
|
key*, val*: TaintedString ## key and value pair; ``key`` is the option
|
||||||
## or the argument, ``value`` is not "" if
|
## or the argument, ``value`` is not "" if
|
||||||
## the option was given a value
|
## the option was given a value
|
||||||
|
|
||||||
proc initOptParser*(cmdline: seq[string]): TOptParser {.rtl.} =
|
{.deprecated: [TCmdLineKind: CmdLineKind, TOptParser: OptParser].}
|
||||||
|
|
||||||
|
proc initOptParser*(cmdline: seq[string]): OptParser {.rtl.} =
|
||||||
## Initalizes option parses with cmdline. cmdline should not contain
|
## Initalizes option parses with cmdline. cmdline should not contain
|
||||||
## argument 0 - program name.
|
## argument 0 - program name.
|
||||||
## If cmdline == nil default to current command line arguments.
|
## If cmdline == nil default to current command line arguments.
|
||||||
|
|
@ -54,7 +56,7 @@ proc initOptParser*(cmdline: seq[string]): TOptParser {.rtl.} =
|
||||||
|
|
||||||
result.cmd = @cmdline
|
result.cmd = @cmdline
|
||||||
|
|
||||||
proc initOptParser*(cmdline: string): TOptParser {.rtl, deprecated.} =
|
proc initOptParser*(cmdline: string): OptParser {.rtl, deprecated.} =
|
||||||
## Initalizes option parses with cmdline. Splits cmdline in on spaces
|
## Initalizes option parses with cmdline. Splits cmdline in on spaces
|
||||||
## and calls initOptParser(openarray[string])
|
## and calls initOptParser(openarray[string])
|
||||||
## Do not use.
|
## Do not use.
|
||||||
|
|
@ -64,13 +66,13 @@ proc initOptParser*(cmdline: string): TOptParser {.rtl, deprecated.} =
|
||||||
return initOptParser(cmdline.split)
|
return initOptParser(cmdline.split)
|
||||||
|
|
||||||
when not defined(createNimRtl):
|
when not defined(createNimRtl):
|
||||||
proc initOptParser*(): TOptParser =
|
proc initOptParser*(): OptParser =
|
||||||
## Initializes option parser from current command line arguments.
|
## Initializes option parser from current command line arguments.
|
||||||
return initOptParser(commandLineParams())
|
return initOptParser(commandLineParams())
|
||||||
|
|
||||||
proc next*(p: var TOptParser) {.rtl, extern: "npo$1".}
|
proc next*(p: var OptParser) {.rtl, extern: "npo$1".}
|
||||||
|
|
||||||
proc nextOption(p: var TOptParser, token: string, allowEmpty: bool) =
|
proc nextOption(p: var OptParser, token: string, allowEmpty: bool) =
|
||||||
for splitchar in [':', '=']:
|
for splitchar in [':', '=']:
|
||||||
if splitchar in token:
|
if splitchar in token:
|
||||||
let pos = token.find(splitchar)
|
let pos = token.find(splitchar)
|
||||||
|
|
@ -85,7 +87,7 @@ proc nextOption(p: var TOptParser, token: string, allowEmpty: bool) =
|
||||||
p.remainingShortOptions = token[0..token.len-1]
|
p.remainingShortOptions = token[0..token.len-1]
|
||||||
p.next()
|
p.next()
|
||||||
|
|
||||||
proc next(p: var TOptParser) =
|
proc next(p: var OptParser) =
|
||||||
if p.remainingShortOptions.len != 0:
|
if p.remainingShortOptions.len != 0:
|
||||||
p.kind = cmdShortOption
|
p.kind = cmdShortOption
|
||||||
p.key = TaintedString(p.remainingShortOptions[0..0])
|
p.key = TaintedString(p.remainingShortOptions[0..0])
|
||||||
|
|
@ -111,20 +113,22 @@ proc next(p: var TOptParser) =
|
||||||
p.key = token
|
p.key = token
|
||||||
p.val = ""
|
p.val = ""
|
||||||
|
|
||||||
proc cmdLineRest*(p: TOptParser): TaintedString {.rtl, extern: "npo$1", deprecated.} =
|
proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo$1", deprecated.} =
|
||||||
## Returns part of command line string that has not been parsed yet.
|
## Returns part of command line string that has not been parsed yet.
|
||||||
## Do not use - does not correctly handle whitespace.
|
## Do not use - does not correctly handle whitespace.
|
||||||
return p.cmd[p.pos..p.cmd.len-1].join(" ")
|
return p.cmd[p.pos..p.cmd.len-1].join(" ")
|
||||||
|
|
||||||
type
|
type
|
||||||
TGetoptResult* = tuple[kind: TCmdLineKind, key, val: TaintedString]
|
GetoptResult* = tuple[kind: CmdLineKind, key, val: TaintedString]
|
||||||
|
|
||||||
|
{.deprecated: [TGetoptResult: GetoptResult].}
|
||||||
|
|
||||||
when declared(paramCount):
|
when declared(paramCount):
|
||||||
iterator getopt*(): TGetoptResult =
|
iterator getopt*(): GetoptResult =
|
||||||
## This is an convenience iterator for iterating over the command line.
|
## This is an convenience iterator for iterating over the command line.
|
||||||
## This uses the TOptParser object. Example:
|
## This uses the OptParser object. Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## var
|
## var
|
||||||
## filename = ""
|
## filename = ""
|
||||||
## for kind, key, val in getopt():
|
## for kind, key, val in getopt():
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2009 Andreas Rumpf
|
# (c) Copyright 2009 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -16,7 +16,7 @@ import
|
||||||
# ------------------- scanner -------------------------------------------------
|
# ------------------- scanner -------------------------------------------------
|
||||||
|
|
||||||
type
|
type
|
||||||
TTokKind = enum ## enumeration of all SQL tokens
|
TokKind = enum ## enumeration of all SQL tokens
|
||||||
tkInvalid, ## invalid token
|
tkInvalid, ## invalid token
|
||||||
tkEof, ## end of file reached
|
tkEof, ## end of file reached
|
||||||
tkIdentifier, ## abc
|
tkIdentifier, ## abc
|
||||||
|
|
@ -38,33 +38,35 @@ type
|
||||||
tkBracketRi, ## ']'
|
tkBracketRi, ## ']'
|
||||||
tkDot ## '.'
|
tkDot ## '.'
|
||||||
|
|
||||||
TToken {.final.} = object # a token
|
Token = object # a token
|
||||||
kind: TTokKind # the type of the token
|
kind: TokKind # the type of the token
|
||||||
literal: string # the parsed (string) literal
|
literal: string # the parsed (string) literal
|
||||||
|
|
||||||
TSqlLexer* = object of TBaseLexer ## the parser object.
|
SqlLexer* = object of BaseLexer ## the parser object.
|
||||||
filename: string
|
filename: string
|
||||||
|
|
||||||
|
{.deprecated: [TToken: Token, TSqlLexer: SqlLexer].}
|
||||||
|
|
||||||
const
|
const
|
||||||
tokKindToStr: array[TTokKind, string] = [
|
tokKindToStr: array[TokKind, string] = [
|
||||||
"invalid", "[EOF]", "identifier", "quoted identifier", "string constant",
|
"invalid", "[EOF]", "identifier", "quoted identifier", "string constant",
|
||||||
"escape string constant", "dollar quoted constant", "bit string constant",
|
"escape string constant", "dollar quoted constant", "bit string constant",
|
||||||
"hex string constant", "integer constant", "numeric constant", "operator",
|
"hex string constant", "integer constant", "numeric constant", "operator",
|
||||||
";", ":", ",", "(", ")", "[", "]", "."
|
";", ":", ",", "(", ")", "[", "]", "."
|
||||||
]
|
]
|
||||||
|
|
||||||
proc open(L: var TSqlLexer, input: PStream, filename: string) =
|
proc open(L: var SqlLexer, input: Stream, filename: string) =
|
||||||
lexbase.open(L, input)
|
lexbase.open(L, input)
|
||||||
L.filename = filename
|
L.filename = filename
|
||||||
|
|
||||||
proc close(L: var TSqlLexer) =
|
proc close(L: var SqlLexer) =
|
||||||
lexbase.close(L)
|
lexbase.close(L)
|
||||||
|
|
||||||
proc getColumn(L: TSqlLexer): int =
|
proc getColumn(L: SqlLexer): int =
|
||||||
## get the current column the parser has arrived at.
|
## get the current column the parser has arrived at.
|
||||||
result = getColNumber(L, L.bufPos)
|
result = getColNumber(L, L.bufPos)
|
||||||
|
|
||||||
proc getLine(L: TSqlLexer): int =
|
proc getLine(L: SqlLexer): int =
|
||||||
result = L.linenumber
|
result = L.linenumber
|
||||||
|
|
||||||
proc handleHexChar(c: var TSqlLexer, xi: var int) =
|
proc handleHexChar(c: var TSqlLexer, xi: var int) =
|
||||||
|
|
@ -137,10 +139,10 @@ proc getEscapedChar(c: var TSqlLexer, tok: var TToken) =
|
||||||
else: tok.kind = tkInvalid
|
else: tok.kind = tkInvalid
|
||||||
else: tok.kind = tkInvalid
|
else: tok.kind = tkInvalid
|
||||||
|
|
||||||
proc HandleCRLF(c: var TSqlLexer, pos: int): int =
|
proc handleCRLF(c: var TSqlLexer, pos: int): int =
|
||||||
case c.buf[pos]
|
case c.buf[pos]
|
||||||
of '\c': result = lexbase.HandleCR(c, pos)
|
of '\c': result = lexbase.handleCR(c, pos)
|
||||||
of '\L': result = lexbase.HandleLF(c, pos)
|
of '\L': result = lexbase.handleLF(c, pos)
|
||||||
else: result = pos
|
else: result = pos
|
||||||
|
|
||||||
proc skip(c: var TSqlLexer) =
|
proc skip(c: var TSqlLexer) =
|
||||||
|
|
@ -163,7 +165,7 @@ proc skip(c: var TSqlLexer) =
|
||||||
case buf[pos]
|
case buf[pos]
|
||||||
of '\0': break
|
of '\0': break
|
||||||
of '\c', '\L':
|
of '\c', '\L':
|
||||||
pos = HandleCRLF(c, pos)
|
pos = handleCRLF(c, pos)
|
||||||
buf = c.buf
|
buf = c.buf
|
||||||
of '*':
|
of '*':
|
||||||
if buf[pos+1] == '/':
|
if buf[pos+1] == '/':
|
||||||
|
|
@ -181,13 +183,13 @@ proc skip(c: var TSqlLexer) =
|
||||||
else: inc(pos)
|
else: inc(pos)
|
||||||
else: break
|
else: break
|
||||||
of '\c', '\L':
|
of '\c', '\L':
|
||||||
pos = HandleCRLF(c, pos)
|
pos = handleCRLF(c, pos)
|
||||||
buf = c.buf
|
buf = c.buf
|
||||||
else:
|
else:
|
||||||
break # EndOfFile also leaves the loop
|
break # EndOfFile also leaves the loop
|
||||||
c.bufpos = pos
|
c.bufpos = pos
|
||||||
|
|
||||||
proc getString(c: var TSqlLexer, tok: var TToken, kind: TTokKind) =
|
proc getString(c: var TSqlLexer, tok: var TToken, kind: TokKind) =
|
||||||
var pos = c.bufPos + 1
|
var pos = c.bufPos + 1
|
||||||
var buf = c.buf
|
var buf = c.buf
|
||||||
tok.kind = kind
|
tok.kind = kind
|
||||||
|
|
@ -465,7 +467,7 @@ proc errorStr(L: TSqlLexer, msg: string): string =
|
||||||
# :: left PostgreSQL-style typecast
|
# :: left PostgreSQL-style typecast
|
||||||
# [ ] left array element selection
|
# [ ] left array element selection
|
||||||
# - right unary minus
|
# - right unary minus
|
||||||
# ^ left exponentiation
|
# ^ left exponentiation
|
||||||
# * / % left multiplication, division, modulo
|
# * / % left multiplication, division, modulo
|
||||||
# + - left addition, subtraction
|
# + - left addition, subtraction
|
||||||
# IS IS TRUE, IS FALSE, IS UNKNOWN, IS NULL
|
# IS IS TRUE, IS FALSE, IS UNKNOWN, IS NULL
|
||||||
|
|
@ -483,7 +485,7 @@ proc errorStr(L: TSqlLexer, msg: string): string =
|
||||||
# OR left logical disjunction
|
# OR left logical disjunction
|
||||||
|
|
||||||
type
|
type
|
||||||
TSqlNodeKind* = enum ## kind of SQL abstract syntax tree
|
SqlNodeKind* = enum ## kind of SQL abstract syntax tree
|
||||||
nkNone,
|
nkNone,
|
||||||
nkIdent,
|
nkIdent,
|
||||||
nkStringLit,
|
nkStringLit,
|
||||||
|
|
@ -536,79 +538,82 @@ type
|
||||||
nkEnumDef
|
nkEnumDef
|
||||||
|
|
||||||
type
|
type
|
||||||
EInvalidSql* = object of EInvalidValue ## Invalid SQL encountered
|
SqlParseError* = object of ValueError ## Invalid SQL encountered
|
||||||
PSqlNode* = ref TSqlNode ## an SQL abstract syntax tree node
|
SqlNode* = ref SqlNodeObj ## an SQL abstract syntax tree node
|
||||||
TSqlNode* = object ## an SQL abstract syntax tree node
|
SqlNodeObj* = object ## an SQL abstract syntax tree node
|
||||||
case kind*: TSqlNodeKind ## kind of syntax tree
|
case kind*: SqlNodeKind ## kind of syntax tree
|
||||||
of nkIdent, nkStringLit, nkBitStringLit, nkHexStringLit,
|
of nkIdent, nkStringLit, nkBitStringLit, nkHexStringLit,
|
||||||
nkIntegerLit, nkNumericLit:
|
nkIntegerLit, nkNumericLit:
|
||||||
strVal*: string ## AST leaf: the identifier, numeric literal
|
strVal*: string ## AST leaf: the identifier, numeric literal
|
||||||
## string literal, etc.
|
## string literal, etc.
|
||||||
else:
|
else:
|
||||||
sons*: seq[PSqlNode] ## the node's children
|
sons*: seq[SqlNode] ## the node's children
|
||||||
|
|
||||||
TSqlParser* = object of TSqlLexer ## SQL parser object
|
SqlParser* = object of SqlLexer ## SQL parser object
|
||||||
tok: TToken
|
tok: Token
|
||||||
|
|
||||||
proc newNode(k: TSqlNodeKind): PSqlNode =
|
{.deprecated: [EInvalidSql: SqlParseError, PSqlNode: SqlNode,
|
||||||
|
TSqlNode: SqlNodeObj, TSqlParser: SqlParser, TSqlNodeKind: SqlNodeKind].}
|
||||||
|
|
||||||
|
proc newNode(k: SqlNodeKind): SqlNode =
|
||||||
new(result)
|
new(result)
|
||||||
result.kind = k
|
result.kind = k
|
||||||
|
|
||||||
proc newNode(k: TSqlNodeKind, s: string): PSqlNode =
|
proc newNode(k: SqlNodeKind, s: string): SqlNode =
|
||||||
new(result)
|
new(result)
|
||||||
result.kind = k
|
result.kind = k
|
||||||
result.strVal = s
|
result.strVal = s
|
||||||
|
|
||||||
proc len*(n: PSqlNode): int =
|
proc len*(n: SqlNode): int =
|
||||||
if isNil(n.sons): result = 0
|
if isNil(n.sons): result = 0
|
||||||
else: result = n.sons.len
|
else: result = n.sons.len
|
||||||
|
|
||||||
proc add*(father, n: PSqlNode) =
|
proc add*(father, n: SqlNode) =
|
||||||
if isNil(father.sons): father.sons = @[]
|
if isNil(father.sons): father.sons = @[]
|
||||||
add(father.sons, n)
|
add(father.sons, n)
|
||||||
|
|
||||||
proc getTok(p: var TSqlParser) =
|
proc getTok(p: var SqlParser) =
|
||||||
getTok(p, p.tok)
|
getTok(p, p.tok)
|
||||||
|
|
||||||
proc sqlError(p: TSqlParser, msg: string) =
|
proc sqlError(p: SqlParser, msg: string) =
|
||||||
var e: ref EInvalidSql
|
var e: ref SqlParseError
|
||||||
new(e)
|
new(e)
|
||||||
e.msg = errorStr(p, msg)
|
e.msg = errorStr(p, msg)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
proc isKeyw(p: TSqlParser, keyw: string): bool =
|
proc isKeyw(p: SqlParser, keyw: string): bool =
|
||||||
result = p.tok.kind == tkIdentifier and
|
result = p.tok.kind == tkIdentifier and
|
||||||
cmpIgnoreCase(p.tok.literal, keyw) == 0
|
cmpIgnoreCase(p.tok.literal, keyw) == 0
|
||||||
|
|
||||||
proc isOpr(p: TSqlParser, opr: string): bool =
|
proc isOpr(p: SqlParser, opr: string): bool =
|
||||||
result = p.tok.kind == tkOperator and
|
result = p.tok.kind == tkOperator and
|
||||||
cmpIgnoreCase(p.tok.literal, opr) == 0
|
cmpIgnoreCase(p.tok.literal, opr) == 0
|
||||||
|
|
||||||
proc optKeyw(p: var TSqlParser, keyw: string) =
|
proc optKeyw(p: var SqlParser, keyw: string) =
|
||||||
if p.tok.kind == tkIdentifier and cmpIgnoreCase(p.tok.literal, keyw) == 0:
|
if p.tok.kind == tkIdentifier and cmpIgnoreCase(p.tok.literal, keyw) == 0:
|
||||||
getTok(p)
|
getTok(p)
|
||||||
|
|
||||||
proc expectIdent(p: TSqlParser) =
|
proc expectIdent(p: SqlParser) =
|
||||||
if p.tok.kind != tkIdentifier and p.tok.kind != tkQuotedIdentifier:
|
if p.tok.kind != tkIdentifier and p.tok.kind != tkQuotedIdentifier:
|
||||||
sqlError(p, "identifier expected")
|
sqlError(p, "identifier expected")
|
||||||
|
|
||||||
proc expect(p: TSqlParser, kind: TTokKind) =
|
proc expect(p: SqlParser, kind: TokKind) =
|
||||||
if p.tok.kind != kind:
|
if p.tok.kind != kind:
|
||||||
sqlError(p, tokKindToStr[kind] & " expected")
|
sqlError(p, tokKindToStr[kind] & " expected")
|
||||||
|
|
||||||
proc eat(p: var TSqlParser, kind: TTokKind) =
|
proc eat(p: var SqlParser, kind: TokKind) =
|
||||||
if p.tok.kind == kind:
|
if p.tok.kind == kind:
|
||||||
getTok(p)
|
getTok(p)
|
||||||
else:
|
else:
|
||||||
sqlError(p, tokKindToStr[kind] & " expected")
|
sqlError(p, tokKindToStr[kind] & " expected")
|
||||||
|
|
||||||
proc eat(p: var TSqlParser, keyw: string) =
|
proc eat(p: var SqlParser, keyw: string) =
|
||||||
if isKeyw(p, keyw):
|
if isKeyw(p, keyw):
|
||||||
getTok(p)
|
getTok(p)
|
||||||
else:
|
else:
|
||||||
sqlError(p, keyw.toUpper() & " expected")
|
sqlError(p, keyw.toUpper() & " expected")
|
||||||
|
|
||||||
proc parseDataType(p: var TSqlParser): PSqlNode =
|
proc parseDataType(p: var SqlParser): SqlNode =
|
||||||
if isKeyw(p, "enum"):
|
if isKeyw(p, "enum"):
|
||||||
result = newNode(nkEnumDef)
|
result = newNode(nkEnumDef)
|
||||||
getTok(p)
|
getTok(p)
|
||||||
|
|
@ -636,7 +641,7 @@ proc parseDataType(p: var TSqlParser): PSqlNode =
|
||||||
getTok(p)
|
getTok(p)
|
||||||
eat(p, tkParRi)
|
eat(p, tkParRi)
|
||||||
|
|
||||||
proc getPrecedence(p: TSqlParser): int =
|
proc getPrecedence(p: SqlParser): int =
|
||||||
if isOpr(p, "*") or isOpr(p, "/") or isOpr(p, "%"):
|
if isOpr(p, "*") or isOpr(p, "/") or isOpr(p, "%"):
|
||||||
result = 6
|
result = 6
|
||||||
elif isOpr(p, "+") or isOpr(p, "-"):
|
elif isOpr(p, "+") or isOpr(p, "-"):
|
||||||
|
|
@ -655,9 +660,9 @@ proc getPrecedence(p: TSqlParser): int =
|
||||||
else:
|
else:
|
||||||
result = - 1
|
result = - 1
|
||||||
|
|
||||||
proc parseExpr(p: var TSqlParser): PSqlNode
|
proc parseExpr(p: var SqlParser): SqlNode
|
||||||
|
|
||||||
proc identOrLiteral(p: var TSqlParser): PSqlNode =
|
proc identOrLiteral(p: var SqlParser): SqlNode =
|
||||||
case p.tok.kind
|
case p.tok.kind
|
||||||
of tkIdentifier, tkQuotedIdentifier:
|
of tkIdentifier, tkQuotedIdentifier:
|
||||||
result = newNode(nkIdent, p.tok.literal)
|
result = newNode(nkIdent, p.tok.literal)
|
||||||
|
|
@ -685,7 +690,7 @@ proc identOrLiteral(p: var TSqlParser): PSqlNode =
|
||||||
sqlError(p, "expression expected")
|
sqlError(p, "expression expected")
|
||||||
getTok(p) # we must consume a token here to prevend endless loops!
|
getTok(p) # we must consume a token here to prevend endless loops!
|
||||||
|
|
||||||
proc primary(p: var TSqlParser): PSqlNode =
|
proc primary(p: var SqlParser): SqlNode =
|
||||||
if p.tok.kind == tkOperator or isKeyw(p, "not"):
|
if p.tok.kind == tkOperator or isKeyw(p, "not"):
|
||||||
result = newNode(nkPrefix)
|
result = newNode(nkPrefix)
|
||||||
result.add(newNode(nkIdent, p.tok.literal))
|
result.add(newNode(nkIdent, p.tok.literal))
|
||||||
|
|
@ -723,9 +728,9 @@ proc primary(p: var TSqlParser): PSqlNode =
|
||||||
getTok(p)
|
getTok(p)
|
||||||
else: break
|
else: break
|
||||||
|
|
||||||
proc lowestExprAux(p: var TSqlParser, v: var PSqlNode, limit: int): int =
|
proc lowestExprAux(p: var SqlParser, v: var SqlNode, limit: int): int =
|
||||||
var
|
var
|
||||||
v2, node, opNode: PSqlNode
|
v2, node, opNode: SqlNode
|
||||||
v = primary(p) # expand while operators have priorities higher than 'limit'
|
v = primary(p) # expand while operators have priorities higher than 'limit'
|
||||||
var opPred = getPrecedence(p)
|
var opPred = getPrecedence(p)
|
||||||
result = opPred
|
result = opPred
|
||||||
|
|
@ -740,14 +745,14 @@ proc lowestExprAux(p: var TSqlParser, v: var PSqlNode, limit: int): int =
|
||||||
v = node
|
v = node
|
||||||
opPred = getPrecedence(p)
|
opPred = getPrecedence(p)
|
||||||
|
|
||||||
proc parseExpr(p: var TSqlParser): PSqlNode =
|
proc parseExpr(p: var SqlParser): SqlNode =
|
||||||
discard lowestExprAux(p, result, - 1)
|
discard lowestExprAux(p, result, - 1)
|
||||||
|
|
||||||
proc parseTableName(p: var TSqlParser): PSqlNode =
|
proc parseTableName(p: var SqlParser): SqlNode =
|
||||||
expectIdent(p)
|
expectIdent(p)
|
||||||
result = primary(p)
|
result = primary(p)
|
||||||
|
|
||||||
proc parseColumnReference(p: var TSqlParser): PSqlNode =
|
proc parseColumnReference(p: var SqlParser): SqlNode =
|
||||||
result = parseTableName(p)
|
result = parseTableName(p)
|
||||||
if p.tok.kind == tkParLe:
|
if p.tok.kind == tkParLe:
|
||||||
getTok(p)
|
getTok(p)
|
||||||
|
|
@ -760,12 +765,12 @@ proc parseColumnReference(p: var TSqlParser): PSqlNode =
|
||||||
result.add(parseTableName(p))
|
result.add(parseTableName(p))
|
||||||
eat(p, tkParRi)
|
eat(p, tkParRi)
|
||||||
|
|
||||||
proc parseCheck(p: var TSqlParser): PSqlNode =
|
proc parseCheck(p: var SqlParser): SqlNode =
|
||||||
getTok(p)
|
getTok(p)
|
||||||
result = newNode(nkCheck)
|
result = newNode(nkCheck)
|
||||||
result.add(parseExpr(p))
|
result.add(parseExpr(p))
|
||||||
|
|
||||||
proc parseConstraint(p: var TSqlParser): PSqlNode =
|
proc parseConstraint(p: var SqlParser): SqlNode =
|
||||||
getTok(p)
|
getTok(p)
|
||||||
result = newNode(nkConstraint)
|
result = newNode(nkConstraint)
|
||||||
expectIdent(p)
|
expectIdent(p)
|
||||||
|
|
@ -774,7 +779,7 @@ proc parseConstraint(p: var TSqlParser): PSqlNode =
|
||||||
eat(p, "check")
|
eat(p, "check")
|
||||||
result.add(parseExpr(p))
|
result.add(parseExpr(p))
|
||||||
|
|
||||||
proc parseColumnConstraints(p: var TSqlParser, result: PSqlNode) =
|
proc parseColumnConstraints(p: var SqlParser, result: SqlNode) =
|
||||||
while true:
|
while true:
|
||||||
if isKeyw(p, "default"):
|
if isKeyw(p, "default"):
|
||||||
getTok(p)
|
getTok(p)
|
||||||
|
|
@ -806,7 +811,7 @@ proc parseColumnConstraints(p: var TSqlParser, result: PSqlNode) =
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
proc parseColumnDef(p: var TSqlParser): PSqlNode =
|
proc parseColumnDef(p: var SqlParser): SqlNode =
|
||||||
expectIdent(p)
|
expectIdent(p)
|
||||||
result = newNode(nkColumnDef)
|
result = newNode(nkColumnDef)
|
||||||
result.add(newNode(nkIdent, p.tok.literal))
|
result.add(newNode(nkIdent, p.tok.literal))
|
||||||
|
|
@ -814,7 +819,7 @@ proc parseColumnDef(p: var TSqlParser): PSqlNode =
|
||||||
result.add(parseDataType(p))
|
result.add(parseDataType(p))
|
||||||
parseColumnConstraints(p, result)
|
parseColumnConstraints(p, result)
|
||||||
|
|
||||||
proc parseIfNotExists(p: var TSqlParser, k: TSqlNodeKind): PSqlNode =
|
proc parseIfNotExists(p: var SqlParser, k: TSqlNodeKind): SqlNode =
|
||||||
getTok(p)
|
getTok(p)
|
||||||
if isKeyw(p, "if"):
|
if isKeyw(p, "if"):
|
||||||
getTok(p)
|
getTok(p)
|
||||||
|
|
@ -824,7 +829,7 @@ proc parseIfNotExists(p: var TSqlParser, k: TSqlNodeKind): PSqlNode =
|
||||||
else:
|
else:
|
||||||
result = newNode(k)
|
result = newNode(k)
|
||||||
|
|
||||||
proc parseParIdentList(p: var TSqlParser, father: PSqlNode) =
|
proc parseParIdentList(p: var SqlParser, father: SqlNode) =
|
||||||
eat(p, tkParLe)
|
eat(p, tkParLe)
|
||||||
while true:
|
while true:
|
||||||
expectIdent(p)
|
expectIdent(p)
|
||||||
|
|
@ -834,7 +839,7 @@ proc parseParIdentList(p: var TSqlParser, father: PSqlNode) =
|
||||||
getTok(p)
|
getTok(p)
|
||||||
eat(p, tkParRi)
|
eat(p, tkParRi)
|
||||||
|
|
||||||
proc parseTableConstraint(p: var TSqlParser): PSqlNode =
|
proc parseTableConstraint(p: var SqlParser): SqlNode =
|
||||||
if isKeyw(p, "primary"):
|
if isKeyw(p, "primary"):
|
||||||
getTok(p)
|
getTok(p)
|
||||||
eat(p, "key")
|
eat(p, "key")
|
||||||
|
|
@ -861,7 +866,7 @@ proc parseTableConstraint(p: var TSqlParser): PSqlNode =
|
||||||
else:
|
else:
|
||||||
sqlError(p, "column definition expected")
|
sqlError(p, "column definition expected")
|
||||||
|
|
||||||
proc parseTableDef(p: var TSqlParser): PSqlNode =
|
proc parseTableDef(p: var SqlParser): SqlNode =
|
||||||
result = parseIfNotExists(p, nkCreateTable)
|
result = parseIfNotExists(p, nkCreateTable)
|
||||||
expectIdent(p)
|
expectIdent(p)
|
||||||
result.add(newNode(nkIdent, p.tok.literal))
|
result.add(newNode(nkIdent, p.tok.literal))
|
||||||
|
|
@ -876,7 +881,7 @@ proc parseTableDef(p: var TSqlParser): PSqlNode =
|
||||||
if p.tok.kind != tkComma: break
|
if p.tok.kind != tkComma: break
|
||||||
eat(p, tkParRi)
|
eat(p, tkParRi)
|
||||||
|
|
||||||
proc parseTypeDef(p: var TSqlParser): PSqlNode =
|
proc parseTypeDef(p: var SqlParser): SqlNode =
|
||||||
result = parseIfNotExists(p, nkCreateType)
|
result = parseIfNotExists(p, nkCreateType)
|
||||||
expectIdent(p)
|
expectIdent(p)
|
||||||
result.add(newNode(nkIdent, p.tok.literal))
|
result.add(newNode(nkIdent, p.tok.literal))
|
||||||
|
|
@ -884,12 +889,12 @@ proc parseTypeDef(p: var TSqlParser): PSqlNode =
|
||||||
eat(p, "as")
|
eat(p, "as")
|
||||||
result.add(parseDataType(p))
|
result.add(parseDataType(p))
|
||||||
|
|
||||||
proc parseWhere(p: var TSqlParser): PSqlNode =
|
proc parseWhere(p: var SqlParser): SqlNode =
|
||||||
getTok(p)
|
getTok(p)
|
||||||
result = newNode(nkWhere)
|
result = newNode(nkWhere)
|
||||||
result.add(parseExpr(p))
|
result.add(parseExpr(p))
|
||||||
|
|
||||||
proc parseIndexDef(p: var TSqlParser): PSqlNode =
|
proc parseIndexDef(p: var SqlParser): SqlNode =
|
||||||
result = parseIfNotExists(p, nkCreateIndex)
|
result = parseIfNotExists(p, nkCreateIndex)
|
||||||
if isKeyw(p, "primary"):
|
if isKeyw(p, "primary"):
|
||||||
getTok(p)
|
getTok(p)
|
||||||
|
|
@ -914,7 +919,7 @@ proc parseIndexDef(p: var TSqlParser): PSqlNode =
|
||||||
getTok(p)
|
getTok(p)
|
||||||
eat(p, tkParRi)
|
eat(p, tkParRi)
|
||||||
|
|
||||||
proc parseInsert(p: var TSqlParser): PSqlNode =
|
proc parseInsert(p: var SqlParser): SqlNode =
|
||||||
getTok(p)
|
getTok(p)
|
||||||
eat(p, "into")
|
eat(p, "into")
|
||||||
expectIdent(p)
|
expectIdent(p)
|
||||||
|
|
@ -941,7 +946,7 @@ proc parseInsert(p: var TSqlParser): PSqlNode =
|
||||||
result.add(n)
|
result.add(n)
|
||||||
eat(p, tkParRi)
|
eat(p, tkParRi)
|
||||||
|
|
||||||
proc parseUpdate(p: var TSqlParser): PSqlNode =
|
proc parseUpdate(p: var SqlParser): SqlNode =
|
||||||
getTok(p)
|
getTok(p)
|
||||||
result = newNode(nkUpdate)
|
result = newNode(nkUpdate)
|
||||||
result.add(primary(p))
|
result.add(primary(p))
|
||||||
|
|
@ -962,7 +967,7 @@ proc parseUpdate(p: var TSqlParser): PSqlNode =
|
||||||
else:
|
else:
|
||||||
result.add(nil)
|
result.add(nil)
|
||||||
|
|
||||||
proc parseDelete(p: var TSqlParser): PSqlNode =
|
proc parseDelete(p: var SqlParser): SqlNode =
|
||||||
getTok(p)
|
getTok(p)
|
||||||
result = newNode(nkDelete)
|
result = newNode(nkDelete)
|
||||||
eat(p, "from")
|
eat(p, "from")
|
||||||
|
|
@ -972,7 +977,7 @@ proc parseDelete(p: var TSqlParser): PSqlNode =
|
||||||
else:
|
else:
|
||||||
result.add(nil)
|
result.add(nil)
|
||||||
|
|
||||||
proc parseSelect(p: var TSqlParser): PSqlNode =
|
proc parseSelect(p: var SqlParser): SqlNode =
|
||||||
getTok(p)
|
getTok(p)
|
||||||
if isKeyw(p, "distinct"):
|
if isKeyw(p, "distinct"):
|
||||||
getTok(p)
|
getTok(p)
|
||||||
|
|
@ -1041,7 +1046,7 @@ proc parseSelect(p: var TSqlParser): PSqlNode =
|
||||||
getTok(p)
|
getTok(p)
|
||||||
result.add(n)
|
result.add(n)
|
||||||
|
|
||||||
proc parseStmt(p: var TSqlParser): PSqlNode =
|
proc parseStmt(p: var SqlParser): SqlNode =
|
||||||
if isKeyw(p, "create"):
|
if isKeyw(p, "create"):
|
||||||
getTok(p)
|
getTok(p)
|
||||||
optKeyw(p, "cached")
|
optKeyw(p, "cached")
|
||||||
|
|
@ -1071,7 +1076,7 @@ proc parseStmt(p: var TSqlParser): PSqlNode =
|
||||||
else:
|
else:
|
||||||
sqlError(p, "CREATE expected")
|
sqlError(p, "CREATE expected")
|
||||||
|
|
||||||
proc open(p: var TSqlParser, input: PStream, filename: string) =
|
proc open(p: var SqlParser, input: PStream, filename: string) =
|
||||||
## opens the parser `p` and assigns the input stream `input` to it.
|
## opens the parser `p` and assigns the input stream `input` to it.
|
||||||
## `filename` is only used for error messages.
|
## `filename` is only used for error messages.
|
||||||
open(TSqlLexer(p), input, filename)
|
open(TSqlLexer(p), input, filename)
|
||||||
|
|
@ -1079,7 +1084,7 @@ proc open(p: var TSqlParser, input: PStream, filename: string) =
|
||||||
p.tok.literal = ""
|
p.tok.literal = ""
|
||||||
getTok(p)
|
getTok(p)
|
||||||
|
|
||||||
proc parse(p: var TSqlParser): PSqlNode =
|
proc parse(p: var SqlParser): SqlNode =
|
||||||
## parses the content of `p`'s input stream and returns the SQL AST.
|
## parses the content of `p`'s input stream and returns the SQL AST.
|
||||||
## Syntax errors raise an `EInvalidSql` exception.
|
## Syntax errors raise an `EInvalidSql` exception.
|
||||||
result = newNode(nkStmtList)
|
result = newNode(nkStmtList)
|
||||||
|
|
@ -1090,24 +1095,24 @@ proc parse(p: var TSqlParser): PSqlNode =
|
||||||
if result.len == 1:
|
if result.len == 1:
|
||||||
result = result.sons[0]
|
result = result.sons[0]
|
||||||
|
|
||||||
proc close(p: var TSqlParser) =
|
proc close(p: var SqlParser) =
|
||||||
## closes the parser `p`. The associated input stream is closed too.
|
## closes the parser `p`. The associated input stream is closed too.
|
||||||
close(TSqlLexer(p))
|
close(TSqlLexer(p))
|
||||||
|
|
||||||
proc parseSQL*(input: PStream, filename: string): PSqlNode =
|
proc parseSQL*(input: PStream, filename: string): SqlNode =
|
||||||
## parses the SQL from `input` into an AST and returns the AST.
|
## parses the SQL from `input` into an AST and returns the AST.
|
||||||
## `filename` is only used for error messages.
|
## `filename` is only used for error messages.
|
||||||
## Syntax errors raise an `EInvalidSql` exception.
|
## Syntax errors raise an `EInvalidSql` exception.
|
||||||
var p: TSqlParser
|
var p: SqlParser
|
||||||
open(p, input, filename)
|
open(p, input, filename)
|
||||||
try:
|
try:
|
||||||
result = parse(p)
|
result = parse(p)
|
||||||
finally:
|
finally:
|
||||||
close(p)
|
close(p)
|
||||||
|
|
||||||
proc ra(n: PSqlNode, s: var string, indent: int)
|
proc ra(n: SqlNode, s: var string, indent: int)
|
||||||
|
|
||||||
proc rs(n: PSqlNode, s: var string, indent: int,
|
proc rs(n: SqlNode, s: var string, indent: int,
|
||||||
prefix = "(", suffix = ")",
|
prefix = "(", suffix = ")",
|
||||||
sep = ", ") =
|
sep = ", ") =
|
||||||
if n.len > 0:
|
if n.len > 0:
|
||||||
|
|
@ -1117,7 +1122,7 @@ proc rs(n: PSqlNode, s: var string, indent: int,
|
||||||
ra(n.sons[i], s, indent)
|
ra(n.sons[i], s, indent)
|
||||||
s.add(suffix)
|
s.add(suffix)
|
||||||
|
|
||||||
proc ra(n: PSqlNode, s: var string, indent: int) =
|
proc ra(n: SqlNode, s: var string, indent: int) =
|
||||||
if n == nil: return
|
if n == nil: return
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkNone: discard
|
of nkNone: discard
|
||||||
|
|
@ -1320,7 +1325,7 @@ proc ra(n: PSqlNode, s: var string, indent: int) =
|
||||||
#for x, y, z in db.select(fromm = a, b where = a.name == b.name):
|
#for x, y, z in db.select(fromm = a, b where = a.name == b.name):
|
||||||
# writeln x, y, z
|
# writeln x, y, z
|
||||||
|
|
||||||
proc renderSQL*(n: PSqlNode): string =
|
proc renderSQL*(n: SqlNode): string =
|
||||||
## Converts an SQL abstract syntax tree to its string representation.
|
## Converts an SQL abstract syntax tree to its string representation.
|
||||||
result = ""
|
result = ""
|
||||||
ra(n, result, 0)
|
ra(n, result, 0)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2014 Dominik Picheta
|
# (c) Copyright 2014 Dominik Picheta
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -15,12 +15,14 @@
|
||||||
import strutils
|
import strutils
|
||||||
|
|
||||||
type
|
type
|
||||||
TUrl* = tuple[ ## represents a *Uniform Resource Locator* (URL)
|
Url* = tuple[ ## represents a *Uniform Resource Locator* (URL)
|
||||||
## any optional component is "" if it does not exist
|
## any optional component is "" if it does not exist
|
||||||
scheme, username, password,
|
scheme, username, password,
|
||||||
hostname, port, path, query, anchor: string]
|
hostname, port, path, query, anchor: string]
|
||||||
|
|
||||||
proc parseUrl*(url: string): TUrl {.deprecated.} =
|
{.deprecated: [TUrl: Url].}
|
||||||
|
|
||||||
|
proc parseUrl*(url: string): Url {.deprecated.} =
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
||||||
var scheme, username, password: string = ""
|
var scheme, username, password: string = ""
|
||||||
|
|
@ -86,7 +88,7 @@ proc parseUrl*(url: string): TUrl {.deprecated.} =
|
||||||
|
|
||||||
return (scheme, username, password, hostname, port, path, query, anchor)
|
return (scheme, username, password, hostname, port, path, query, anchor)
|
||||||
|
|
||||||
proc `$`*(u: TUrl): string {.deprecated.} =
|
proc `$`*(u: Url): string {.deprecated.} =
|
||||||
## turns the URL `u` into its string representation.
|
## turns the URL `u` into its string representation.
|
||||||
result = ""
|
result = ""
|
||||||
if u.scheme.len > 0:
|
if u.scheme.len > 0:
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -35,7 +35,7 @@ proc parseHex*(s: string, number: var int, start = 0): int {.
|
||||||
## can use this feature to *chain* calls, though the result int will quickly
|
## can use this feature to *chain* calls, though the result int will quickly
|
||||||
## overflow. Example:
|
## overflow. Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## var value = 0
|
## var value = 0
|
||||||
## discard parseHex("0x38", value)
|
## discard parseHex("0x38", value)
|
||||||
## assert value == 56
|
## assert value == 56
|
||||||
|
|
@ -332,7 +332,7 @@ proc parseFloat*(s: string, number: var float, start = 0): int {.
|
||||||
number = bf
|
number = bf
|
||||||
|
|
||||||
type
|
type
|
||||||
TInterpolatedKind* = enum ## describes for `interpolatedFragments`
|
InterpolatedKind* = enum ## describes for `interpolatedFragments`
|
||||||
## which part of the interpolated string is
|
## which part of the interpolated string is
|
||||||
## yielded; for example in "str$$$var${expr}"
|
## yielded; for example in "str$$$var${expr}"
|
||||||
ikStr, ## ``str`` part of the interpolated string
|
ikStr, ## ``str`` part of the interpolated string
|
||||||
|
|
@ -340,7 +340,9 @@ type
|
||||||
ikVar, ## ``var`` part of the interpolated string
|
ikVar, ## ``var`` part of the interpolated string
|
||||||
ikExpr ## ``expr`` part of the interpolated string
|
ikExpr ## ``expr`` part of the interpolated string
|
||||||
|
|
||||||
iterator interpolatedFragments*(s: string): tuple[kind: TInterpolatedKind,
|
{.deprecated: [TInterpolatedKind: InterpolatedKind].}
|
||||||
|
|
||||||
|
iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
|
||||||
value: string] =
|
value: string] =
|
||||||
## Tokenizes the string `s` into substrings for interpolation purposes.
|
## Tokenizes the string `s` into substrings for interpolation purposes.
|
||||||
##
|
##
|
||||||
|
|
@ -360,7 +362,7 @@ iterator interpolatedFragments*(s: string): tuple[kind: TInterpolatedKind,
|
||||||
## (ikString, " ")
|
## (ikString, " ")
|
||||||
## (ikDollar, "$")
|
## (ikDollar, "$")
|
||||||
var i = 0
|
var i = 0
|
||||||
var kind: TInterpolatedKind
|
var kind: InterpolatedKind
|
||||||
while true:
|
while true:
|
||||||
var j = i
|
var j = i
|
||||||
if s[j] == '$':
|
if s[j] == '$':
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2010 Andreas Rumpf
|
# (c) Copyright 2010 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -56,7 +56,7 @@ import
|
||||||
# xmlElementCloseEnd, ## ``/>``
|
# xmlElementCloseEnd, ## ``/>``
|
||||||
|
|
||||||
type
|
type
|
||||||
TXmlEventKind* = enum ## enumation of all events that may occur when parsing
|
XmlEventKind* = enum ## enumation of all events that may occur when parsing
|
||||||
xmlError, ## an error ocurred during parsing
|
xmlError, ## an error ocurred during parsing
|
||||||
xmlEof, ## end of file reached
|
xmlEof, ## end of file reached
|
||||||
xmlCharData, ## character data
|
xmlCharData, ## character data
|
||||||
|
|
@ -72,7 +72,7 @@ type
|
||||||
xmlEntity, ## &entity;
|
xmlEntity, ## &entity;
|
||||||
xmlSpecial ## ``<! ... data ... >``
|
xmlSpecial ## ``<! ... data ... >``
|
||||||
|
|
||||||
TXmlError* = enum ## enumeration that lists all errors that can occur
|
XmlErrorKind* = enum ## enumeration that lists all errors that can occur
|
||||||
errNone, ## no error
|
errNone, ## no error
|
||||||
errEndOfCDataExpected, ## ``]]>`` expected
|
errEndOfCDataExpected, ## ``]]>`` expected
|
||||||
errNameExpected, ## name expected
|
errNameExpected, ## name expected
|
||||||
|
|
@ -83,23 +83,26 @@ type
|
||||||
errQuoteExpected, ## ``"`` or ``'`` expected
|
errQuoteExpected, ## ``"`` or ``'`` expected
|
||||||
errEndOfCommentExpected ## ``-->`` expected
|
errEndOfCommentExpected ## ``-->`` expected
|
||||||
|
|
||||||
TParserState = enum
|
ParserState = enum
|
||||||
stateStart, stateNormal, stateAttr, stateEmptyElementTag, stateError
|
stateStart, stateNormal, stateAttr, stateEmptyElementTag, stateError
|
||||||
|
|
||||||
TXmlParseOption* = enum ## options for the XML parser
|
XmlParseOption* = enum ## options for the XML parser
|
||||||
reportWhitespace, ## report whitespace
|
reportWhitespace, ## report whitespace
|
||||||
reportComments ## report comments
|
reportComments ## report comments
|
||||||
|
|
||||||
TXmlParser* = object of TBaseLexer ## the parser object.
|
XmlParser* = object of TBaseLexer ## the parser object.
|
||||||
a, b, c: string
|
a, b, c: string
|
||||||
kind: TXmlEventKind
|
kind: XmlEventKind
|
||||||
err: TXmlError
|
err: XmlErrorKind
|
||||||
state: TParserState
|
state: ParserState
|
||||||
filename: string
|
filename: string
|
||||||
options: set[TXmlParseOption]
|
options: set[XmlParseOption]
|
||||||
|
|
||||||
|
{.deprecated: [TXmlParser: XmlParser, TXmlParseOptions: XmlParseOptions,
|
||||||
|
TXmlError: XmlErrorKind, TXmlEventKind: XmlEventKind].}
|
||||||
|
|
||||||
const
|
const
|
||||||
errorMessages: array [TXmlError, string] = [
|
errorMessages: array[XmlError, string] = [
|
||||||
"no error",
|
"no error",
|
||||||
"']]>' expected",
|
"']]>' expected",
|
||||||
"name expected",
|
"name expected",
|
||||||
|
|
@ -111,8 +114,8 @@ const
|
||||||
"'-->' expected"
|
"'-->' expected"
|
||||||
]
|
]
|
||||||
|
|
||||||
proc open*(my: var TXmlParser, input: PStream, filename: string,
|
proc open*(my: var XmlParser, input: Stream, filename: string,
|
||||||
options: set[TXmlParseOption] = {}) =
|
options: set[XmlParseOption] = {}) =
|
||||||
## initializes the parser with an input stream. `Filename` is only used
|
## initializes the parser with an input stream. `Filename` is only used
|
||||||
## for nice error messages. The parser's behaviour can be controlled by
|
## for nice error messages. The parser's behaviour can be controlled by
|
||||||
## the `options` parameter: If `options` contains ``reportWhitespace``
|
## the `options` parameter: If `options` contains ``reportWhitespace``
|
||||||
|
|
@ -127,7 +130,7 @@ proc open*(my: var TXmlParser, input: PStream, filename: string,
|
||||||
my.b = ""
|
my.b = ""
|
||||||
my.options = options
|
my.options = options
|
||||||
|
|
||||||
proc close*(my: var TXmlParser) {.inline.} =
|
proc close*(my: var XmlParser) {.inline.} =
|
||||||
## closes the parser `my` and its associated input stream.
|
## closes the parser `my` and its associated input stream.
|
||||||
lexbase.close(my)
|
lexbase.close(my)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -32,7 +32,7 @@ const
|
||||||
## can be captured. More subpatterns cannot be captured!
|
## can be captured. More subpatterns cannot be captured!
|
||||||
|
|
||||||
type
|
type
|
||||||
TPegKind = enum
|
PegKind = enum
|
||||||
pkEmpty,
|
pkEmpty,
|
||||||
pkAny, ## any character (.)
|
pkAny, ## any character (.)
|
||||||
pkAnyRune, ## any Unicode character (_)
|
pkAnyRune, ## any Unicode character (_)
|
||||||
|
|
@ -67,16 +67,16 @@ type
|
||||||
pkRule, ## a <- b
|
pkRule, ## a <- b
|
||||||
pkList, ## a, b
|
pkList, ## a, b
|
||||||
pkStartAnchor ## ^ --> Internal DSL: startAnchor()
|
pkStartAnchor ## ^ --> Internal DSL: startAnchor()
|
||||||
TNonTerminalFlag = enum
|
NonTerminalFlag = enum
|
||||||
ntDeclared, ntUsed
|
ntDeclared, ntUsed
|
||||||
TNonTerminal {.final.} = object ## represents a non terminal symbol
|
NonTerminalObj = object ## represents a non terminal symbol
|
||||||
name: string ## the name of the symbol
|
name: string ## the name of the symbol
|
||||||
line: int ## line the symbol has been declared/used in
|
line: int ## line the symbol has been declared/used in
|
||||||
col: int ## column the symbol has been declared/used in
|
col: int ## column the symbol has been declared/used in
|
||||||
flags: set[TNonTerminalFlag] ## the nonterminal's flags
|
flags: set[NonTerminalFlag] ## the nonterminal's flags
|
||||||
rule: TNode ## the rule that the symbol refers to
|
rule: TNode ## the rule that the symbol refers to
|
||||||
TNode {.final, shallow.} = object
|
TNode {.shallow.} = object
|
||||||
case kind: TPegKind
|
case kind: PegKind
|
||||||
of pkEmpty..pkWhitespace: nil
|
of pkEmpty..pkWhitespace: nil
|
||||||
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string
|
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string
|
||||||
of pkChar, pkGreedyRepChar: ch: char
|
of pkChar, pkGreedyRepChar: ch: char
|
||||||
|
|
@ -84,11 +84,13 @@ type
|
||||||
of pkNonTerminal: nt: PNonTerminal
|
of pkNonTerminal: nt: PNonTerminal
|
||||||
of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns]
|
of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns]
|
||||||
else: sons: seq[TNode]
|
else: sons: seq[TNode]
|
||||||
PNonTerminal* = ref TNonTerminal
|
NonTerminal* = ref NonTerminalObj
|
||||||
|
|
||||||
TPeg* = TNode ## type that represents a PEG
|
Peg* = TNode ## type that represents a PEG
|
||||||
|
|
||||||
proc term*(t: string): TPeg {.nosideEffect, rtl, extern: "npegs$1Str".} =
|
{.deprecated: [TPeg: Peg].}
|
||||||
|
|
||||||
|
proc term*(t: string): Peg {.nosideEffect, rtl, extern: "npegs$1Str".} =
|
||||||
## constructs a PEG from a terminal string
|
## constructs a PEG from a terminal string
|
||||||
if t.len != 1:
|
if t.len != 1:
|
||||||
result.kind = pkTerminal
|
result.kind = pkTerminal
|
||||||
|
|
@ -97,35 +99,35 @@ proc term*(t: string): TPeg {.nosideEffect, rtl, extern: "npegs$1Str".} =
|
||||||
result.kind = pkChar
|
result.kind = pkChar
|
||||||
result.ch = t[0]
|
result.ch = t[0]
|
||||||
|
|
||||||
proc termIgnoreCase*(t: string): TPeg {.
|
proc termIgnoreCase*(t: string): Peg {.
|
||||||
nosideEffect, rtl, extern: "npegs$1".} =
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## constructs a PEG from a terminal string; ignore case for matching
|
## constructs a PEG from a terminal string; ignore case for matching
|
||||||
result.kind = pkTerminalIgnoreCase
|
result.kind = pkTerminalIgnoreCase
|
||||||
result.term = t
|
result.term = t
|
||||||
|
|
||||||
proc termIgnoreStyle*(t: string): TPeg {.
|
proc termIgnoreStyle*(t: string): Peg {.
|
||||||
nosideEffect, rtl, extern: "npegs$1".} =
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## constructs a PEG from a terminal string; ignore style for matching
|
## constructs a PEG from a terminal string; ignore style for matching
|
||||||
result.kind = pkTerminalIgnoreStyle
|
result.kind = pkTerminalIgnoreStyle
|
||||||
result.term = t
|
result.term = t
|
||||||
|
|
||||||
proc term*(t: char): TPeg {.nosideEffect, rtl, extern: "npegs$1Char".} =
|
proc term*(t: char): Peg {.nosideEffect, rtl, extern: "npegs$1Char".} =
|
||||||
## constructs a PEG from a terminal char
|
## constructs a PEG from a terminal char
|
||||||
assert t != '\0'
|
assert t != '\0'
|
||||||
result.kind = pkChar
|
result.kind = pkChar
|
||||||
result.ch = t
|
result.ch = t
|
||||||
|
|
||||||
proc charSet*(s: set[char]): TPeg {.nosideEffect, rtl, extern: "npegs$1".} =
|
proc charSet*(s: set[char]): Peg {.nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## constructs a PEG from a character set `s`
|
## constructs a PEG from a character set `s`
|
||||||
assert '\0' notin s
|
assert '\0' notin s
|
||||||
result.kind = pkCharChoice
|
result.kind = pkCharChoice
|
||||||
new(result.charChoice)
|
new(result.charChoice)
|
||||||
result.charChoice[] = s
|
result.charChoice[] = s
|
||||||
|
|
||||||
proc len(a: TPeg): int {.inline.} = return a.sons.len
|
proc len(a: Peg): int {.inline.} = return a.sons.len
|
||||||
proc add(d: var TPeg, s: TPeg) {.inline.} = add(d.sons, s)
|
proc add(d: var Peg, s: Peg) {.inline.} = add(d.sons, s)
|
||||||
|
|
||||||
proc addChoice(dest: var TPeg, elem: TPeg) =
|
proc addChoice(dest: var Peg, elem: Peg) =
|
||||||
var L = dest.len-1
|
var L = dest.len-1
|
||||||
if L >= 0 and dest.sons[L].kind == pkCharChoice:
|
if L >= 0 and dest.sons[L].kind == pkCharChoice:
|
||||||
# caution! Do not introduce false aliasing here!
|
# caution! Do not introduce false aliasing here!
|
||||||
|
|
@ -137,7 +139,7 @@ proc addChoice(dest: var TPeg, elem: TPeg) =
|
||||||
else: add(dest, elem)
|
else: add(dest, elem)
|
||||||
else: add(dest, elem)
|
else: add(dest, elem)
|
||||||
|
|
||||||
template multipleOp(k: TPegKind, localOpt: expr) =
|
template multipleOp(k: PegKind, localOpt: expr) =
|
||||||
result.kind = k
|
result.kind = k
|
||||||
result.sons = @[]
|
result.sons = @[]
|
||||||
for x in items(a):
|
for x in items(a):
|
||||||
|
|
@ -149,12 +151,12 @@ template multipleOp(k: TPegKind, localOpt: expr) =
|
||||||
if result.len == 1:
|
if result.len == 1:
|
||||||
result = result.sons[0]
|
result = result.sons[0]
|
||||||
|
|
||||||
proc `/`*(a: varargs[TPeg]): TPeg {.
|
proc `/`*(a: varargs[Peg]): Peg {.
|
||||||
nosideEffect, rtl, extern: "npegsOrderedChoice".} =
|
nosideEffect, rtl, extern: "npegsOrderedChoice".} =
|
||||||
## constructs an ordered choice with the PEGs in `a`
|
## constructs an ordered choice with the PEGs in `a`
|
||||||
multipleOp(pkOrderedChoice, addChoice)
|
multipleOp(pkOrderedChoice, addChoice)
|
||||||
|
|
||||||
proc addSequence(dest: var TPeg, elem: TPeg) =
|
proc addSequence(dest: var Peg, elem: Peg) =
|
||||||
var L = dest.len-1
|
var L = dest.len-1
|
||||||
if L >= 0 and dest.sons[L].kind == pkTerminal:
|
if L >= 0 and dest.sons[L].kind == pkTerminal:
|
||||||
# caution! Do not introduce false aliasing here!
|
# caution! Do not introduce false aliasing here!
|
||||||
|
|
@ -166,12 +168,12 @@ proc addSequence(dest: var TPeg, elem: TPeg) =
|
||||||
else: add(dest, elem)
|
else: add(dest, elem)
|
||||||
else: add(dest, elem)
|
else: add(dest, elem)
|
||||||
|
|
||||||
proc sequence*(a: varargs[TPeg]): TPeg {.
|
proc sequence*(a: varargs[Peg]): Peg {.
|
||||||
nosideEffect, rtl, extern: "npegs$1".} =
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## constructs a sequence with all the PEGs from `a`
|
## constructs a sequence with all the PEGs from `a`
|
||||||
multipleOp(pkSequence, addSequence)
|
multipleOp(pkSequence, addSequence)
|
||||||
|
|
||||||
proc `?`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsOptional".} =
|
proc `?`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsOptional".} =
|
||||||
## constructs an optional for the PEG `a`
|
## constructs an optional for the PEG `a`
|
||||||
if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar,
|
if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar,
|
||||||
pkGreedyRepSet}:
|
pkGreedyRepSet}:
|
||||||
|
|
@ -182,7 +184,7 @@ proc `?`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsOptional".} =
|
||||||
result.kind = pkOption
|
result.kind = pkOption
|
||||||
result.sons = @[a]
|
result.sons = @[a]
|
||||||
|
|
||||||
proc `*`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsGreedyRep".} =
|
proc `*`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsGreedyRep".} =
|
||||||
## constructs a "greedy repetition" for the PEG `a`
|
## constructs a "greedy repetition" for the PEG `a`
|
||||||
case a.kind
|
case a.kind
|
||||||
of pkGreedyRep, pkGreedyRepChar, pkGreedyRepSet, pkGreedyAny, pkOption:
|
of pkGreedyRep, pkGreedyRepChar, pkGreedyRepSet, pkGreedyAny, pkOption:
|
||||||
|
|
@ -200,111 +202,99 @@ proc `*`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsGreedyRep".} =
|
||||||
result.kind = pkGreedyRep
|
result.kind = pkGreedyRep
|
||||||
result.sons = @[a]
|
result.sons = @[a]
|
||||||
|
|
||||||
proc `!*`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsSearch".} =
|
proc `!*`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsSearch".} =
|
||||||
## constructs a "search" for the PEG `a`
|
## constructs a "search" for the PEG `a`
|
||||||
result.kind = pkSearch
|
result.kind = pkSearch
|
||||||
result.sons = @[a]
|
result.sons = @[a]
|
||||||
|
|
||||||
proc `!*\`*(a: TPeg): TPeg {.noSideEffect, rtl,
|
proc `!*\`*(a: Peg): Peg {.noSideEffect, rtl,
|
||||||
extern: "npgegsCapturedSearch".} =
|
extern: "npgegsCapturedSearch".} =
|
||||||
## constructs a "captured search" for the PEG `a`
|
## constructs a "captured search" for the PEG `a`
|
||||||
result.kind = pkCapturedSearch
|
result.kind = pkCapturedSearch
|
||||||
result.sons = @[a]
|
result.sons = @[a]
|
||||||
|
|
||||||
when false:
|
proc `+`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsGreedyPosRep".} =
|
||||||
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".} =
|
|
||||||
## constructs a "greedy positive repetition" with the PEG `a`
|
## constructs a "greedy positive repetition" with the PEG `a`
|
||||||
return sequence(a, *a)
|
return sequence(a, *a)
|
||||||
|
|
||||||
proc `&`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsAndPredicate".} =
|
proc `&`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsAndPredicate".} =
|
||||||
## constructs an "and predicate" with the PEG `a`
|
## constructs an "and predicate" with the PEG `a`
|
||||||
result.kind = pkAndPredicate
|
result.kind = pkAndPredicate
|
||||||
result.sons = @[a]
|
result.sons = @[a]
|
||||||
|
|
||||||
proc `!`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsNotPredicate".} =
|
proc `!`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsNotPredicate".} =
|
||||||
## constructs a "not predicate" with the PEG `a`
|
## constructs a "not predicate" with the PEG `a`
|
||||||
result.kind = pkNotPredicate
|
result.kind = pkNotPredicate
|
||||||
result.sons = @[a]
|
result.sons = @[a]
|
||||||
|
|
||||||
proc any*: TPeg {.inline.} =
|
proc any*: Peg {.inline.} =
|
||||||
## constructs the PEG `any character`:idx: (``.``)
|
## constructs the PEG `any character`:idx: (``.``)
|
||||||
result.kind = pkAny
|
result.kind = pkAny
|
||||||
|
|
||||||
proc anyRune*: TPeg {.inline.} =
|
proc anyRune*: Peg {.inline.} =
|
||||||
## constructs the PEG `any rune`:idx: (``_``)
|
## constructs the PEG `any rune`:idx: (``_``)
|
||||||
result.kind = pkAnyRune
|
result.kind = pkAnyRune
|
||||||
|
|
||||||
proc newLine*: TPeg {.inline.} =
|
proc newLine*: Peg {.inline.} =
|
||||||
## constructs the PEG `newline`:idx: (``\n``)
|
## constructs the PEG `newline`:idx: (``\n``)
|
||||||
result.kind = pkNewline
|
result.kind = pkNewline
|
||||||
|
|
||||||
proc unicodeLetter*: TPeg {.inline.} =
|
proc unicodeLetter*: Peg {.inline.} =
|
||||||
## constructs the PEG ``\letter`` which matches any Unicode letter.
|
## constructs the PEG ``\letter`` which matches any Unicode letter.
|
||||||
result.kind = pkLetter
|
result.kind = pkLetter
|
||||||
|
|
||||||
proc unicodeLower*: TPeg {.inline.} =
|
proc unicodeLower*: Peg {.inline.} =
|
||||||
## constructs the PEG ``\lower`` which matches any Unicode lowercase letter.
|
## constructs the PEG ``\lower`` which matches any Unicode lowercase letter.
|
||||||
result.kind = pkLower
|
result.kind = pkLower
|
||||||
|
|
||||||
proc unicodeUpper*: TPeg {.inline.} =
|
proc unicodeUpper*: Peg {.inline.} =
|
||||||
## constructs the PEG ``\upper`` which matches any Unicode uppercase letter.
|
## constructs the PEG ``\upper`` which matches any Unicode uppercase letter.
|
||||||
result.kind = pkUpper
|
result.kind = pkUpper
|
||||||
|
|
||||||
proc unicodeTitle*: TPeg {.inline.} =
|
proc unicodeTitle*: Peg {.inline.} =
|
||||||
## constructs the PEG ``\title`` which matches any Unicode title letter.
|
## constructs the PEG ``\title`` which matches any Unicode title letter.
|
||||||
result.kind = pkTitle
|
result.kind = pkTitle
|
||||||
|
|
||||||
proc unicodeWhitespace*: TPeg {.inline.} =
|
proc unicodeWhitespace*: Peg {.inline.} =
|
||||||
## constructs the PEG ``\white`` which matches any Unicode
|
## constructs the PEG ``\white`` which matches any Unicode
|
||||||
## whitespace character.
|
## whitespace character.
|
||||||
result.kind = pkWhitespace
|
result.kind = pkWhitespace
|
||||||
|
|
||||||
proc startAnchor*: TPeg {.inline.} =
|
proc startAnchor*: Peg {.inline.} =
|
||||||
## constructs the PEG ``^`` which matches the start of the input.
|
## constructs the PEG ``^`` which matches the start of the input.
|
||||||
result.kind = pkStartAnchor
|
result.kind = pkStartAnchor
|
||||||
|
|
||||||
proc endAnchor*: TPeg {.inline.} =
|
proc endAnchor*: Peg {.inline.} =
|
||||||
## constructs the PEG ``$`` which matches the end of the input.
|
## constructs the PEG ``$`` which matches the end of the input.
|
||||||
result = !any()
|
result = !any()
|
||||||
|
|
||||||
proc capture*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsCapture".} =
|
proc capture*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsCapture".} =
|
||||||
## constructs a capture with the PEG `a`
|
## constructs a capture with the PEG `a`
|
||||||
result.kind = pkCapture
|
result.kind = pkCapture
|
||||||
result.sons = @[a]
|
result.sons = @[a]
|
||||||
|
|
||||||
proc backref*(index: range[1..MaxSubPatterns]): TPeg {.
|
proc backref*(index: range[1..MaxSubPatterns]): Peg {.
|
||||||
nosideEffect, rtl, extern: "npegs$1".} =
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## constructs a back reference of the given `index`. `index` starts counting
|
## constructs a back reference of the given `index`. `index` starts counting
|
||||||
## from 1.
|
## from 1.
|
||||||
result.kind = pkBackRef
|
result.kind = pkBackRef
|
||||||
result.index = index-1
|
result.index = index-1
|
||||||
|
|
||||||
proc backrefIgnoreCase*(index: range[1..MaxSubPatterns]): TPeg {.
|
proc backrefIgnoreCase*(index: range[1..MaxSubPatterns]): Peg {.
|
||||||
nosideEffect, rtl, extern: "npegs$1".} =
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## constructs a back reference of the given `index`. `index` starts counting
|
## constructs a back reference of the given `index`. `index` starts counting
|
||||||
## from 1. Ignores case for matching.
|
## from 1. Ignores case for matching.
|
||||||
result.kind = pkBackRefIgnoreCase
|
result.kind = pkBackRefIgnoreCase
|
||||||
result.index = index-1
|
result.index = index-1
|
||||||
|
|
||||||
proc backrefIgnoreStyle*(index: range[1..MaxSubPatterns]): TPeg {.
|
proc backrefIgnoreStyle*(index: range[1..MaxSubPatterns]): Peg {.
|
||||||
nosideEffect, rtl, extern: "npegs$1".}=
|
nosideEffect, rtl, extern: "npegs$1".}=
|
||||||
## constructs a back reference of the given `index`. `index` starts counting
|
## constructs a back reference of the given `index`. `index` starts counting
|
||||||
## from 1. Ignores style for matching.
|
## from 1. Ignores style for matching.
|
||||||
result.kind = pkBackRefIgnoreStyle
|
result.kind = pkBackRefIgnoreStyle
|
||||||
result.index = index-1
|
result.index = index-1
|
||||||
|
|
||||||
proc spaceCost(n: TPeg): int =
|
proc spaceCost(n: Peg): int =
|
||||||
case n.kind
|
case n.kind
|
||||||
of pkEmpty: discard
|
of pkEmpty: discard
|
||||||
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar,
|
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar,
|
||||||
|
|
@ -319,7 +309,7 @@ proc spaceCost(n: TPeg): int =
|
||||||
inc(result, spaceCost(n.sons[i]))
|
inc(result, spaceCost(n.sons[i]))
|
||||||
if result >= InlineThreshold: break
|
if result >= InlineThreshold: break
|
||||||
|
|
||||||
proc nonterminal*(n: PNonTerminal): TPeg {.
|
proc nonterminal*(n: PNonTerminal): Peg {.
|
||||||
nosideEffect, rtl, extern: "npegs$1".} =
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## constructs a PEG that consists of the nonterminal symbol
|
## constructs a PEG that consists of the nonterminal symbol
|
||||||
assert n != nil
|
assert n != nil
|
||||||
|
|
@ -415,7 +405,7 @@ proc charSetEsc(cc: set[char]): string =
|
||||||
else:
|
else:
|
||||||
result = '[' & charSetEscAux(cc) & ']'
|
result = '[' & charSetEscAux(cc) & ']'
|
||||||
|
|
||||||
proc toStrAux(r: TPeg, res: var string) =
|
proc toStrAux(r: Peg, res: var string) =
|
||||||
case r.kind
|
case r.kind
|
||||||
of pkEmpty: add(res, "()")
|
of pkEmpty: add(res, "()")
|
||||||
of pkAny: add(res, '.')
|
of pkAny: add(res, '.')
|
||||||
|
|
@ -501,7 +491,7 @@ proc toStrAux(r: TPeg, res: var string) =
|
||||||
of pkStartAnchor:
|
of pkStartAnchor:
|
||||||
add(res, '^')
|
add(res, '^')
|
||||||
|
|
||||||
proc `$` *(r: TPeg): string {.nosideEffect, rtl, extern: "npegsToString".} =
|
proc `$` *(r: Peg): string {.nosideEffect, rtl, extern: "npegsToString".} =
|
||||||
## converts a PEG to its string representation
|
## converts a PEG to its string representation
|
||||||
result = ""
|
result = ""
|
||||||
toStrAux(r, result)
|
toStrAux(r, result)
|
||||||
|
|
@ -509,12 +499,14 @@ proc `$` *(r: TPeg): string {.nosideEffect, rtl, extern: "npegsToString".} =
|
||||||
# --------------------- core engine -------------------------------------------
|
# --------------------- core engine -------------------------------------------
|
||||||
|
|
||||||
type
|
type
|
||||||
TCaptures* {.final.} = object ## contains the captured substrings.
|
Captures* = object ## contains the captured substrings.
|
||||||
matches: array[0..maxSubpatterns-1, tuple[first, last: int]]
|
matches: array[0..maxSubpatterns-1, tuple[first, last: int]]
|
||||||
ml: int
|
ml: int
|
||||||
origStart: int
|
origStart: int
|
||||||
|
|
||||||
proc bounds*(c: TCaptures,
|
{.deprecated: [TCaptures: Captures].}
|
||||||
|
|
||||||
|
proc bounds*(c: Captures,
|
||||||
i: range[0..maxSubpatterns-1]): tuple[first, last: int] =
|
i: range[0..maxSubpatterns-1]): tuple[first, last: int] =
|
||||||
## returns the bounds ``[first..last]`` of the `i`'th capture.
|
## returns the bounds ``[first..last]`` of the `i`'th capture.
|
||||||
result = c.matches[i]
|
result = c.matches[i]
|
||||||
|
|
@ -533,7 +525,7 @@ when not useUnicode:
|
||||||
proc isTitle(a: char): bool {.inline.} = return false
|
proc isTitle(a: char): bool {.inline.} = return false
|
||||||
proc isWhiteSpace(a: char): bool {.inline.} = return a in {' ', '\9'..'\13'}
|
proc isWhiteSpace(a: char): bool {.inline.} = return a in {' ', '\9'..'\13'}
|
||||||
|
|
||||||
proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {.
|
proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
|
||||||
nosideEffect, rtl, extern: "npegs$1".} =
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## low-level matching proc that implements the PEG interpreter. Use this
|
## low-level matching proc that implements the PEG interpreter. Use this
|
||||||
## for maximum efficiency (every other PEG operation ends up calling this
|
## for maximum efficiency (every other PEG operation ends up calling this
|
||||||
|
|
@ -734,7 +726,7 @@ proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {.
|
||||||
of pkBackRef..pkBackRefIgnoreStyle:
|
of pkBackRef..pkBackRefIgnoreStyle:
|
||||||
if p.index >= c.ml: return -1
|
if p.index >= c.ml: return -1
|
||||||
var (a, b) = c.matches[p.index]
|
var (a, b) = c.matches[p.index]
|
||||||
var n: TPeg
|
var n: Peg
|
||||||
n.kind = succ(pkTerminal, ord(p.kind)-ord(pkBackRef))
|
n.kind = succ(pkTerminal, ord(p.kind)-ord(pkBackRef))
|
||||||
n.term = s.substr(a, b)
|
n.term = s.substr(a, b)
|
||||||
result = rawMatch(s, n, start, c)
|
result = rawMatch(s, n, start, c)
|
||||||
|
|
@ -747,51 +739,51 @@ template fillMatches(s, caps, c: expr) =
|
||||||
for k in 0..c.ml-1:
|
for k in 0..c.ml-1:
|
||||||
caps[k] = substr(s, c.matches[k][0], c.matches[k][1])
|
caps[k] = substr(s, c.matches[k][0], c.matches[k][1])
|
||||||
|
|
||||||
proc match*(s: string, pattern: TPeg, matches: var openArray[string],
|
proc match*(s: string, pattern: Peg, matches: var openArray[string],
|
||||||
start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} =
|
start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} =
|
||||||
## returns ``true`` if ``s[start..]`` matches the ``pattern`` and
|
## returns ``true`` if ``s[start..]`` matches the ``pattern`` and
|
||||||
## the captured substrings in the array ``matches``. If it does not
|
## the captured substrings in the array ``matches``. If it does not
|
||||||
## match, nothing is written into ``matches`` and ``false`` is
|
## match, nothing is written into ``matches`` and ``false`` is
|
||||||
## returned.
|
## returned.
|
||||||
var c: TCaptures
|
var c: Captures
|
||||||
c.origStart = start
|
c.origStart = start
|
||||||
result = rawMatch(s, pattern, start, c) == len(s) - start
|
result = rawMatch(s, pattern, start, c) == len(s) - start
|
||||||
if result: fillMatches(s, matches, c)
|
if result: fillMatches(s, matches, c)
|
||||||
|
|
||||||
proc match*(s: string, pattern: TPeg,
|
proc match*(s: string, pattern: Peg,
|
||||||
start = 0): bool {.nosideEffect, rtl, extern: "npegs$1".} =
|
start = 0): bool {.nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``.
|
## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``.
|
||||||
var c: TCaptures
|
var c: Captures
|
||||||
c.origStart = start
|
c.origStart = start
|
||||||
result = rawMatch(s, pattern, start, c) == len(s)-start
|
result = rawMatch(s, pattern, start, c) == len(s)-start
|
||||||
|
|
||||||
proc matchLen*(s: string, pattern: TPeg, matches: var openArray[string],
|
proc matchLen*(s: string, pattern: Peg, matches: var openArray[string],
|
||||||
start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
|
start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
|
||||||
## the same as ``match``, but it returns the length of the match,
|
## the same as ``match``, but it returns the length of the match,
|
||||||
## if there is no match, -1 is returned. Note that a match length
|
## if there is no match, -1 is returned. Note that a match length
|
||||||
## of zero can happen. It's possible that a suffix of `s` remains
|
## of zero can happen. It's possible that a suffix of `s` remains
|
||||||
## that does not belong to the match.
|
## that does not belong to the match.
|
||||||
var c: TCaptures
|
var c: Captures
|
||||||
c.origStart = start
|
c.origStart = start
|
||||||
result = rawMatch(s, pattern, start, c)
|
result = rawMatch(s, pattern, start, c)
|
||||||
if result >= 0: fillMatches(s, matches, c)
|
if result >= 0: fillMatches(s, matches, c)
|
||||||
|
|
||||||
proc matchLen*(s: string, pattern: TPeg,
|
proc matchLen*(s: string, pattern: Peg,
|
||||||
start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} =
|
start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## the same as ``match``, but it returns the length of the match,
|
## the same as ``match``, but it returns the length of the match,
|
||||||
## if there is no match, -1 is returned. Note that a match length
|
## if there is no match, -1 is returned. Note that a match length
|
||||||
## of zero can happen. It's possible that a suffix of `s` remains
|
## of zero can happen. It's possible that a suffix of `s` remains
|
||||||
## that does not belong to the match.
|
## that does not belong to the match.
|
||||||
var c: TCaptures
|
var c: Captures
|
||||||
c.origStart = start
|
c.origStart = start
|
||||||
result = rawMatch(s, pattern, start, c)
|
result = rawMatch(s, pattern, start, c)
|
||||||
|
|
||||||
proc find*(s: string, pattern: TPeg, matches: var openArray[string],
|
proc find*(s: string, pattern: Peg, matches: var openArray[string],
|
||||||
start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
|
start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
|
||||||
## returns the starting position of ``pattern`` in ``s`` and the captured
|
## returns the starting position of ``pattern`` in ``s`` and the captured
|
||||||
## substrings in the array ``matches``. If it does not match, nothing
|
## substrings in the array ``matches``. If it does not match, nothing
|
||||||
## is written into ``matches`` and -1 is returned.
|
## is written into ``matches`` and -1 is returned.
|
||||||
var c: TCaptures
|
var c: Captures
|
||||||
c.origStart = start
|
c.origStart = start
|
||||||
for i in start .. s.len-1:
|
for i in start .. s.len-1:
|
||||||
c.ml = 0
|
c.ml = 0
|
||||||
|
|
@ -801,14 +793,14 @@ proc find*(s: string, pattern: TPeg, matches: var openArray[string],
|
||||||
return -1
|
return -1
|
||||||
# could also use the pattern here: (!P .)* P
|
# could also use the pattern here: (!P .)* P
|
||||||
|
|
||||||
proc findBounds*(s: string, pattern: TPeg, matches: var openArray[string],
|
proc findBounds*(s: string, pattern: Peg, matches: var openArray[string],
|
||||||
start = 0): tuple[first, last: int] {.
|
start = 0): tuple[first, last: int] {.
|
||||||
nosideEffect, rtl, extern: "npegs$1Capture".} =
|
nosideEffect, rtl, extern: "npegs$1Capture".} =
|
||||||
## returns the starting position and end position of ``pattern`` in ``s``
|
## returns the starting position and end position of ``pattern`` in ``s``
|
||||||
## and the captured
|
## and the captured
|
||||||
## substrings in the array ``matches``. If it does not match, nothing
|
## substrings in the array ``matches``. If it does not match, nothing
|
||||||
## is written into ``matches`` and (-1,0) is returned.
|
## is written into ``matches`` and (-1,0) is returned.
|
||||||
var c: TCaptures
|
var c: Captures
|
||||||
c.origStart = start
|
c.origStart = start
|
||||||
for i in start .. s.len-1:
|
for i in start .. s.len-1:
|
||||||
c.ml = 0
|
c.ml = 0
|
||||||
|
|
@ -818,19 +810,19 @@ proc findBounds*(s: string, pattern: TPeg, matches: var openArray[string],
|
||||||
return (i, i+L-1)
|
return (i, i+L-1)
|
||||||
return (-1, 0)
|
return (-1, 0)
|
||||||
|
|
||||||
proc find*(s: string, pattern: TPeg,
|
proc find*(s: string, pattern: Peg,
|
||||||
start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} =
|
start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## returns the starting position of ``pattern`` in ``s``. If it does not
|
## returns the starting position of ``pattern`` in ``s``. If it does not
|
||||||
## match, -1 is returned.
|
## match, -1 is returned.
|
||||||
var c: TCaptures
|
var c: Captures
|
||||||
c.origStart = start
|
c.origStart = start
|
||||||
for i in start .. s.len-1:
|
for i in start .. s.len-1:
|
||||||
if rawMatch(s, pattern, i, c) >= 0: return i
|
if rawMatch(s, pattern, i, c) >= 0: return i
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
iterator findAll*(s: string, pattern: TPeg, start = 0): string =
|
iterator findAll*(s: string, pattern: Peg, start = 0): string =
|
||||||
## yields all matching *substrings* of `s` that match `pattern`.
|
## yields all matching *substrings* of `s` that match `pattern`.
|
||||||
var c: TCaptures
|
var c: Captures
|
||||||
c.origStart = start
|
c.origStart = start
|
||||||
var i = start
|
var i = start
|
||||||
while i < s.len:
|
while i < s.len:
|
||||||
|
|
@ -842,7 +834,7 @@ iterator findAll*(s: string, pattern: TPeg, start = 0): string =
|
||||||
yield substr(s, i, i+L-1)
|
yield substr(s, i, i+L-1)
|
||||||
inc(i, L)
|
inc(i, L)
|
||||||
|
|
||||||
proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {.
|
proc findAll*(s: string, pattern: Peg, start = 0): seq[string] {.
|
||||||
nosideEffect, rtl, extern: "npegs$1".} =
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## returns all matching *substrings* of `s` that match `pattern`.
|
## returns all matching *substrings* of `s` that match `pattern`.
|
||||||
## If it does not match, @[] is returned.
|
## If it does not match, @[] is returned.
|
||||||
|
|
@ -851,11 +843,11 @@ proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {.
|
||||||
when not defined(nimhygiene):
|
when not defined(nimhygiene):
|
||||||
{.pragma: inject.}
|
{.pragma: inject.}
|
||||||
|
|
||||||
template `=~`*(s: string, pattern: TPeg): bool =
|
template `=~`*(s: string, pattern: Peg): bool =
|
||||||
## This calls ``match`` with an implicit declared ``matches`` array that
|
## This calls ``match`` with an implicit declared ``matches`` array that
|
||||||
## can be used in the scope of the ``=~`` call:
|
## can be used in the scope of the ``=~`` call:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
##
|
##
|
||||||
## if line =~ peg"\s* {\w+} \s* '=' \s* {\w+}":
|
## if line =~ peg"\s* {\w+} \s* '=' \s* {\w+}":
|
||||||
## # matches a key=value pair:
|
## # matches a key=value pair:
|
||||||
|
|
@ -876,46 +868,46 @@ template `=~`*(s: string, pattern: TPeg): bool =
|
||||||
|
|
||||||
# ------------------------- more string handling ------------------------------
|
# ------------------------- more string handling ------------------------------
|
||||||
|
|
||||||
proc contains*(s: string, pattern: TPeg, start = 0): bool {.
|
proc contains*(s: string, pattern: Peg, start = 0): bool {.
|
||||||
nosideEffect, rtl, extern: "npegs$1".} =
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## same as ``find(s, pattern, start) >= 0``
|
## same as ``find(s, pattern, start) >= 0``
|
||||||
return find(s, pattern, start) >= 0
|
return find(s, pattern, start) >= 0
|
||||||
|
|
||||||
proc contains*(s: string, pattern: TPeg, matches: var openArray[string],
|
proc contains*(s: string, pattern: Peg, matches: var openArray[string],
|
||||||
start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} =
|
start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} =
|
||||||
## same as ``find(s, pattern, matches, start) >= 0``
|
## same as ``find(s, pattern, matches, start) >= 0``
|
||||||
return find(s, pattern, matches, start) >= 0
|
return find(s, pattern, matches, start) >= 0
|
||||||
|
|
||||||
proc startsWith*(s: string, prefix: TPeg, start = 0): bool {.
|
proc startsWith*(s: string, prefix: Peg, start = 0): bool {.
|
||||||
nosideEffect, rtl, extern: "npegs$1".} =
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## returns true if `s` starts with the pattern `prefix`
|
## returns true if `s` starts with the pattern `prefix`
|
||||||
result = matchLen(s, prefix, start) >= 0
|
result = matchLen(s, prefix, start) >= 0
|
||||||
|
|
||||||
proc endsWith*(s: string, suffix: TPeg, start = 0): bool {.
|
proc endsWith*(s: string, suffix: Peg, start = 0): bool {.
|
||||||
nosideEffect, rtl, extern: "npegs$1".} =
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## returns true if `s` ends with the pattern `prefix`
|
## returns true if `s` ends with the pattern `prefix`
|
||||||
var c: TCaptures
|
var c: Captures
|
||||||
c.origStart = start
|
c.origStart = start
|
||||||
for i in start .. s.len-1:
|
for i in start .. s.len-1:
|
||||||
if rawMatch(s, suffix, i, c) == s.len - i: return true
|
if rawMatch(s, suffix, i, c) == s.len - i: return true
|
||||||
|
|
||||||
proc replacef*(s: string, sub: TPeg, by: string): string {.
|
proc replacef*(s: string, sub: Peg, by: string): string {.
|
||||||
nosideEffect, rtl, extern: "npegs$1".} =
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by`
|
## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by`
|
||||||
## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
|
## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## "var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2")
|
## "var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2")
|
||||||
##
|
##
|
||||||
## Results in:
|
## Results in:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
##
|
##
|
||||||
## "var1<-keykey; val2<-key2key2"
|
## "var1<-keykey; val2<-key2key2"
|
||||||
result = ""
|
result = ""
|
||||||
var i = 0
|
var i = 0
|
||||||
var caps: array[0..maxSubpatterns-1, string]
|
var caps: array[0..maxSubpatterns-1, string]
|
||||||
var c: TCaptures
|
var c: Captures
|
||||||
while i < s.len:
|
while i < s.len:
|
||||||
c.ml = 0
|
c.ml = 0
|
||||||
var x = rawMatch(s, sub, i, c)
|
var x = rawMatch(s, sub, i, c)
|
||||||
|
|
@ -928,13 +920,13 @@ proc replacef*(s: string, sub: TPeg, by: string): string {.
|
||||||
inc(i, x)
|
inc(i, x)
|
||||||
add(result, substr(s, i))
|
add(result, substr(s, i))
|
||||||
|
|
||||||
proc replace*(s: string, sub: TPeg, by = ""): string {.
|
proc replace*(s: string, sub: Peg, by = ""): string {.
|
||||||
nosideEffect, rtl, extern: "npegs$1".} =
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## Replaces `sub` in `s` by the string `by`. Captures cannot be accessed
|
## Replaces `sub` in `s` by the string `by`. Captures cannot be accessed
|
||||||
## in `by`.
|
## in `by`.
|
||||||
result = ""
|
result = ""
|
||||||
var i = 0
|
var i = 0
|
||||||
var c: TCaptures
|
var c: Captures
|
||||||
while i < s.len:
|
while i < s.len:
|
||||||
var x = rawMatch(s, sub, i, c)
|
var x = rawMatch(s, sub, i, c)
|
||||||
if x <= 0:
|
if x <= 0:
|
||||||
|
|
@ -946,13 +938,13 @@ proc replace*(s: string, sub: TPeg, by = ""): string {.
|
||||||
add(result, substr(s, i))
|
add(result, substr(s, i))
|
||||||
|
|
||||||
proc parallelReplace*(s: string, subs: varargs[
|
proc parallelReplace*(s: string, subs: varargs[
|
||||||
tuple[pattern: TPeg, repl: string]]): string {.
|
tuple[pattern: Peg, repl: string]]): string {.
|
||||||
nosideEffect, rtl, extern: "npegs$1".} =
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## Returns a modified copy of `s` with the substitutions in `subs`
|
## Returns a modified copy of `s` with the substitutions in `subs`
|
||||||
## applied in parallel.
|
## applied in parallel.
|
||||||
result = ""
|
result = ""
|
||||||
var i = 0
|
var i = 0
|
||||||
var c: TCaptures
|
var c: Captures
|
||||||
var caps: array[0..maxSubpatterns-1, string]
|
var caps: array[0..maxSubpatterns-1, string]
|
||||||
while i < s.len:
|
while i < s.len:
|
||||||
block searchSubs:
|
block searchSubs:
|
||||||
|
|
@ -970,7 +962,7 @@ proc parallelReplace*(s: string, subs: varargs[
|
||||||
add(result, substr(s, i))
|
add(result, substr(s, i))
|
||||||
|
|
||||||
proc transformFile*(infile, outfile: string,
|
proc transformFile*(infile, outfile: string,
|
||||||
subs: varargs[tuple[pattern: TPeg, repl: string]]) {.
|
subs: varargs[tuple[pattern: Peg, repl: string]]) {.
|
||||||
rtl, extern: "npegs$1".} =
|
rtl, extern: "npegs$1".} =
|
||||||
## reads in the file `infile`, performs a parallel replacement (calls
|
## reads in the file `infile`, performs a parallel replacement (calls
|
||||||
## `parallelReplace`) and writes back to `outfile`. Raises ``EIO`` if an
|
## `parallelReplace`) and writes back to `outfile`. Raises ``EIO`` if an
|
||||||
|
|
@ -978,25 +970,25 @@ proc transformFile*(infile, outfile: string,
|
||||||
var x = readFile(infile).string
|
var x = readFile(infile).string
|
||||||
writeFile(outfile, x.parallelReplace(subs))
|
writeFile(outfile, x.parallelReplace(subs))
|
||||||
|
|
||||||
iterator split*(s: string, sep: TPeg): string =
|
iterator split*(s: string, sep: Peg): string =
|
||||||
## Splits the string `s` into substrings.
|
## Splits the string `s` into substrings.
|
||||||
##
|
##
|
||||||
## Substrings are separated by the PEG `sep`.
|
## Substrings are separated by the PEG `sep`.
|
||||||
## Examples:
|
## Examples:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## for word in split("00232this02939is39an22example111", peg"\d+"):
|
## for word in split("00232this02939is39an22example111", peg"\d+"):
|
||||||
## writeln(stdout, word)
|
## writeln(stdout, word)
|
||||||
##
|
##
|
||||||
## Results in:
|
## Results in:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## "this"
|
## "this"
|
||||||
## "is"
|
## "is"
|
||||||
## "an"
|
## "an"
|
||||||
## "example"
|
## "example"
|
||||||
##
|
##
|
||||||
var c: TCaptures
|
var c: Captures
|
||||||
var
|
var
|
||||||
first = 0
|
first = 0
|
||||||
last = 0
|
last = 0
|
||||||
|
|
@ -1013,7 +1005,7 @@ iterator split*(s: string, sep: TPeg): string =
|
||||||
if first < last:
|
if first < last:
|
||||||
yield substr(s, first, last-1)
|
yield substr(s, first, last-1)
|
||||||
|
|
||||||
proc split*(s: string, sep: TPeg): seq[string] {.
|
proc split*(s: string, sep: Peg): seq[string] {.
|
||||||
nosideEffect, rtl, extern: "npegs$1".} =
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## Splits the string `s` into substrings.
|
## Splits the string `s` into substrings.
|
||||||
accumulateResult(split(s, sep))
|
accumulateResult(split(s, sep))
|
||||||
|
|
@ -1060,7 +1052,7 @@ type
|
||||||
charset: set[char] ## if kind == tkCharSet
|
charset: set[char] ## if kind == tkCharSet
|
||||||
index: int ## if kind == tkBackref
|
index: int ## if kind == tkBackref
|
||||||
|
|
||||||
TPegLexer {.inheritable.} = object ## the lexer object.
|
PegLexer {.inheritable.} = object ## the lexer object.
|
||||||
bufpos: int ## the current position within the buffer
|
bufpos: int ## the current position within the buffer
|
||||||
buf: cstring ## the buffer itself
|
buf: cstring ## the buffer itself
|
||||||
LineNumber: int ## the current line number
|
LineNumber: int ## the current line number
|
||||||
|
|
@ -1076,20 +1068,20 @@ const
|
||||||
"@", "built-in", "escaped", "$", "$", "^"
|
"@", "built-in", "escaped", "$", "$", "^"
|
||||||
]
|
]
|
||||||
|
|
||||||
proc handleCR(L: var TPegLexer, pos: int): int =
|
proc handleCR(L: var PegLexer, pos: int): int =
|
||||||
assert(L.buf[pos] == '\c')
|
assert(L.buf[pos] == '\c')
|
||||||
inc(L.linenumber)
|
inc(L.linenumber)
|
||||||
result = pos+1
|
result = pos+1
|
||||||
if L.buf[result] == '\L': inc(result)
|
if L.buf[result] == '\L': inc(result)
|
||||||
L.lineStart = result
|
L.lineStart = result
|
||||||
|
|
||||||
proc handleLF(L: var TPegLexer, pos: int): int =
|
proc handleLF(L: var PegLexer, pos: int): int =
|
||||||
assert(L.buf[pos] == '\L')
|
assert(L.buf[pos] == '\L')
|
||||||
inc(L.linenumber)
|
inc(L.linenumber)
|
||||||
result = pos+1
|
result = pos+1
|
||||||
L.lineStart = result
|
L.lineStart = result
|
||||||
|
|
||||||
proc init(L: var TPegLexer, input, filename: string, line = 1, col = 0) =
|
proc init(L: var PegLexer, input, filename: string, line = 1, col = 0) =
|
||||||
L.buf = input
|
L.buf = input
|
||||||
L.bufpos = 0
|
L.bufpos = 0
|
||||||
L.lineNumber = line
|
L.lineNumber = line
|
||||||
|
|
@ -1097,18 +1089,18 @@ proc init(L: var TPegLexer, input, filename: string, line = 1, col = 0) =
|
||||||
L.lineStart = 0
|
L.lineStart = 0
|
||||||
L.filename = filename
|
L.filename = filename
|
||||||
|
|
||||||
proc getColumn(L: TPegLexer): int {.inline.} =
|
proc getColumn(L: PegLexer): int {.inline.} =
|
||||||
result = abs(L.bufpos - L.lineStart) + L.colOffset
|
result = abs(L.bufpos - L.lineStart) + L.colOffset
|
||||||
|
|
||||||
proc getLine(L: TPegLexer): int {.inline.} =
|
proc getLine(L: PegLexer): int {.inline.} =
|
||||||
result = L.linenumber
|
result = L.linenumber
|
||||||
|
|
||||||
proc errorStr(L: TPegLexer, msg: string, line = -1, col = -1): string =
|
proc errorStr(L: PegLexer, msg: string, line = -1, col = -1): string =
|
||||||
var line = if line < 0: getLine(L) else: line
|
var line = if line < 0: getLine(L) else: line
|
||||||
var col = if col < 0: getColumn(L) else: col
|
var col = if col < 0: getColumn(L) else: col
|
||||||
result = "$1($2, $3) Error: $4" % [L.filename, $line, $col, msg]
|
result = "$1($2, $3) Error: $4" % [L.filename, $line, $col, msg]
|
||||||
|
|
||||||
proc handleHexChar(c: var TPegLexer, xi: var int) =
|
proc handleHexChar(c: var PegLexer, xi: var int) =
|
||||||
case c.buf[c.bufpos]
|
case c.buf[c.bufpos]
|
||||||
of '0'..'9':
|
of '0'..'9':
|
||||||
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0'))
|
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0'))
|
||||||
|
|
@ -1121,7 +1113,7 @@ proc handleHexChar(c: var TPegLexer, xi: var int) =
|
||||||
inc(c.bufpos)
|
inc(c.bufpos)
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc getEscapedChar(c: var TPegLexer, tok: var TToken) =
|
proc getEscapedChar(c: var PegLexer, tok: var TToken) =
|
||||||
inc(c.bufpos)
|
inc(c.bufpos)
|
||||||
case c.buf[c.bufpos]
|
case c.buf[c.bufpos]
|
||||||
of 'r', 'R', 'c', 'C':
|
of 'r', 'R', 'c', 'C':
|
||||||
|
|
@ -1173,7 +1165,7 @@ proc getEscapedChar(c: var TPegLexer, tok: var TToken) =
|
||||||
add(tok.literal, c.buf[c.bufpos])
|
add(tok.literal, c.buf[c.bufpos])
|
||||||
inc(c.bufpos)
|
inc(c.bufpos)
|
||||||
|
|
||||||
proc skip(c: var TPegLexer) =
|
proc skip(c: var PegLexer) =
|
||||||
var pos = c.bufpos
|
var pos = c.bufpos
|
||||||
var buf = c.buf
|
var buf = c.buf
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -1192,7 +1184,7 @@ proc skip(c: var TPegLexer) =
|
||||||
break # EndOfFile also leaves the loop
|
break # EndOfFile also leaves the loop
|
||||||
c.bufpos = pos
|
c.bufpos = pos
|
||||||
|
|
||||||
proc getString(c: var TPegLexer, tok: var TToken) =
|
proc getString(c: var PegLexer, tok: var TToken) =
|
||||||
tok.kind = tkStringLit
|
tok.kind = tkStringLit
|
||||||
var pos = c.bufPos + 1
|
var pos = c.bufPos + 1
|
||||||
var buf = c.buf
|
var buf = c.buf
|
||||||
|
|
@ -1214,7 +1206,7 @@ proc getString(c: var TPegLexer, tok: var TToken) =
|
||||||
inc(pos)
|
inc(pos)
|
||||||
c.bufpos = pos
|
c.bufpos = pos
|
||||||
|
|
||||||
proc getDollar(c: var TPegLexer, tok: var TToken) =
|
proc getDollar(c: var PegLexer, tok: var TToken) =
|
||||||
var pos = c.bufPos + 1
|
var pos = c.bufPos + 1
|
||||||
var buf = c.buf
|
var buf = c.buf
|
||||||
if buf[pos] in {'0'..'9'}:
|
if buf[pos] in {'0'..'9'}:
|
||||||
|
|
@ -1227,7 +1219,7 @@ proc getDollar(c: var TPegLexer, tok: var TToken) =
|
||||||
tok.kind = tkDollar
|
tok.kind = tkDollar
|
||||||
c.bufpos = pos
|
c.bufpos = pos
|
||||||
|
|
||||||
proc getCharSet(c: var TPegLexer, tok: var TToken) =
|
proc getCharSet(c: var PegLexer, tok: var TToken) =
|
||||||
tok.kind = tkCharSet
|
tok.kind = tkCharSet
|
||||||
tok.charset = {}
|
tok.charset = {}
|
||||||
var pos = c.bufPos + 1
|
var pos = c.bufPos + 1
|
||||||
|
|
@ -1278,7 +1270,7 @@ proc getCharSet(c: var TPegLexer, tok: var TToken) =
|
||||||
c.bufpos = pos
|
c.bufpos = pos
|
||||||
if caret: tok.charset = {'\1'..'\xFF'} - tok.charset
|
if caret: tok.charset = {'\1'..'\xFF'} - tok.charset
|
||||||
|
|
||||||
proc getSymbol(c: var TPegLexer, tok: var TToken) =
|
proc getSymbol(c: var PegLexer, tok: var TToken) =
|
||||||
var pos = c.bufpos
|
var pos = c.bufpos
|
||||||
var buf = c.buf
|
var buf = c.buf
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -1288,7 +1280,7 @@ proc getSymbol(c: var TPegLexer, tok: var TToken) =
|
||||||
c.bufpos = pos
|
c.bufpos = pos
|
||||||
tok.kind = tkIdentifier
|
tok.kind = tkIdentifier
|
||||||
|
|
||||||
proc getBuiltin(c: var TPegLexer, tok: var TToken) =
|
proc getBuiltin(c: var PegLexer, tok: var TToken) =
|
||||||
if c.buf[c.bufpos+1] in strutils.Letters:
|
if c.buf[c.bufpos+1] in strutils.Letters:
|
||||||
inc(c.bufpos)
|
inc(c.bufpos)
|
||||||
getSymbol(c, tok)
|
getSymbol(c, tok)
|
||||||
|
|
@ -1297,7 +1289,7 @@ proc getBuiltin(c: var TPegLexer, tok: var TToken) =
|
||||||
tok.kind = tkEscaped
|
tok.kind = tkEscaped
|
||||||
getEscapedChar(c, tok) # may set tok.kind to tkInvalid
|
getEscapedChar(c, tok) # may set tok.kind to tkInvalid
|
||||||
|
|
||||||
proc getTok(c: var TPegLexer, tok: var TToken) =
|
proc getTok(c: var PegLexer, tok: var TToken) =
|
||||||
tok.kind = tkInvalid
|
tok.kind = tkInvalid
|
||||||
tok.modifier = modNone
|
tok.modifier = modNone
|
||||||
setLen(tok.literal, 0)
|
setLen(tok.literal, 0)
|
||||||
|
|
@ -1403,7 +1395,7 @@ proc getTok(c: var TPegLexer, tok: var TToken) =
|
||||||
add(tok.literal, c.buf[c.bufpos])
|
add(tok.literal, c.buf[c.bufpos])
|
||||||
inc(c.bufpos)
|
inc(c.bufpos)
|
||||||
|
|
||||||
proc arrowIsNextTok(c: TPegLexer): bool =
|
proc arrowIsNextTok(c: PegLexer): bool =
|
||||||
# the only look ahead we need
|
# the only look ahead we need
|
||||||
var pos = c.bufpos
|
var pos = c.bufpos
|
||||||
while c.buf[pos] in {'\t', ' '}: inc(pos)
|
while c.buf[pos] in {'\t', ' '}: inc(pos)
|
||||||
|
|
@ -1414,31 +1406,31 @@ proc arrowIsNextTok(c: TPegLexer): bool =
|
||||||
type
|
type
|
||||||
EInvalidPeg* = object of EInvalidValue ## raised if an invalid
|
EInvalidPeg* = object of EInvalidValue ## raised if an invalid
|
||||||
## PEG has been detected
|
## PEG has been detected
|
||||||
TPegParser = object of TPegLexer ## the PEG parser object
|
PegParser = object of PegLexer ## the PEG parser object
|
||||||
tok: TToken
|
tok: TToken
|
||||||
nonterms: seq[PNonTerminal]
|
nonterms: seq[PNonTerminal]
|
||||||
modifier: TModifier
|
modifier: TModifier
|
||||||
captures: int
|
captures: int
|
||||||
identIsVerbatim: bool
|
identIsVerbatim: bool
|
||||||
skip: TPeg
|
skip: Peg
|
||||||
|
|
||||||
proc pegError(p: TPegParser, msg: string, line = -1, col = -1) =
|
proc pegError(p: PegParser, msg: string, line = -1, col = -1) =
|
||||||
var e: ref EInvalidPeg
|
var e: ref EInvalidPeg
|
||||||
new(e)
|
new(e)
|
||||||
e.msg = errorStr(p, msg, line, col)
|
e.msg = errorStr(p, msg, line, col)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
proc getTok(p: var TPegParser) =
|
proc getTok(p: var PegParser) =
|
||||||
getTok(p, p.tok)
|
getTok(p, p.tok)
|
||||||
if p.tok.kind == tkInvalid: pegError(p, "invalid token")
|
if p.tok.kind == tkInvalid: pegError(p, "invalid token")
|
||||||
|
|
||||||
proc eat(p: var TPegParser, kind: TTokKind) =
|
proc eat(p: var PegParser, kind: TTokKind) =
|
||||||
if p.tok.kind == kind: getTok(p)
|
if p.tok.kind == kind: getTok(p)
|
||||||
else: pegError(p, tokKindToStr[kind] & " expected")
|
else: pegError(p, tokKindToStr[kind] & " expected")
|
||||||
|
|
||||||
proc parseExpr(p: var TPegParser): TPeg
|
proc parseExpr(p: var PegParser): Peg
|
||||||
|
|
||||||
proc getNonTerminal(p: var TPegParser, name: string): PNonTerminal =
|
proc getNonTerminal(p: var PegParser, name: string): PNonTerminal =
|
||||||
for i in 0..high(p.nonterms):
|
for i in 0..high(p.nonterms):
|
||||||
result = p.nonterms[i]
|
result = p.nonterms[i]
|
||||||
if cmpIgnoreStyle(result.name, name) == 0: return
|
if cmpIgnoreStyle(result.name, name) == 0: return
|
||||||
|
|
@ -1446,19 +1438,19 @@ proc getNonTerminal(p: var TPegParser, name: string): PNonTerminal =
|
||||||
result = newNonTerminal(name, getLine(p), getColumn(p))
|
result = newNonTerminal(name, getLine(p), getColumn(p))
|
||||||
add(p.nonterms, result)
|
add(p.nonterms, result)
|
||||||
|
|
||||||
proc modifiedTerm(s: string, m: TModifier): TPeg =
|
proc modifiedTerm(s: string, m: TModifier): Peg =
|
||||||
case m
|
case m
|
||||||
of modNone, modVerbatim: result = term(s)
|
of modNone, modVerbatim: result = term(s)
|
||||||
of modIgnoreCase: result = termIgnoreCase(s)
|
of modIgnoreCase: result = termIgnoreCase(s)
|
||||||
of modIgnoreStyle: result = termIgnoreStyle(s)
|
of modIgnoreStyle: result = termIgnoreStyle(s)
|
||||||
|
|
||||||
proc modifiedBackref(s: int, m: TModifier): TPeg =
|
proc modifiedBackref(s: int, m: TModifier): Peg =
|
||||||
case m
|
case m
|
||||||
of modNone, modVerbatim: result = backref(s)
|
of modNone, modVerbatim: result = backref(s)
|
||||||
of modIgnoreCase: result = backrefIgnoreCase(s)
|
of modIgnoreCase: result = backrefIgnoreCase(s)
|
||||||
of modIgnoreStyle: result = backrefIgnoreStyle(s)
|
of modIgnoreStyle: result = backrefIgnoreStyle(s)
|
||||||
|
|
||||||
proc builtin(p: var TPegParser): TPeg =
|
proc builtin(p: var PegParser): Peg =
|
||||||
# do not use "y", "skip" or "i" as these would be ambiguous
|
# do not use "y", "skip" or "i" as these would be ambiguous
|
||||||
case p.tok.literal
|
case p.tok.literal
|
||||||
of "n": result = newLine()
|
of "n": result = newLine()
|
||||||
|
|
@ -1478,11 +1470,11 @@ proc builtin(p: var TPegParser): TPeg =
|
||||||
of "white": result = unicodeWhitespace()
|
of "white": result = unicodeWhitespace()
|
||||||
else: pegError(p, "unknown built-in: " & p.tok.literal)
|
else: pegError(p, "unknown built-in: " & p.tok.literal)
|
||||||
|
|
||||||
proc token(terminal: TPeg, p: TPegParser): TPeg =
|
proc token(terminal: Peg, p: PegParser): Peg =
|
||||||
if p.skip.kind == pkEmpty: result = terminal
|
if p.skip.kind == pkEmpty: result = terminal
|
||||||
else: result = sequence(p.skip, terminal)
|
else: result = sequence(p.skip, terminal)
|
||||||
|
|
||||||
proc primary(p: var TPegParser): TPeg =
|
proc primary(p: var PegParser): Peg =
|
||||||
case p.tok.kind
|
case p.tok.kind
|
||||||
of tkAmp:
|
of tkAmp:
|
||||||
getTok(p)
|
getTok(p)
|
||||||
|
|
@ -1571,7 +1563,7 @@ proc primary(p: var TPegParser): TPeg =
|
||||||
getTok(p)
|
getTok(p)
|
||||||
else: break
|
else: break
|
||||||
|
|
||||||
proc seqExpr(p: var TPegParser): TPeg =
|
proc seqExpr(p: var PegParser): Peg =
|
||||||
result = primary(p)
|
result = primary(p)
|
||||||
while true:
|
while true:
|
||||||
case p.tok.kind
|
case p.tok.kind
|
||||||
|
|
@ -1585,13 +1577,13 @@ proc seqExpr(p: var TPegParser): TPeg =
|
||||||
else: break
|
else: break
|
||||||
else: break
|
else: break
|
||||||
|
|
||||||
proc parseExpr(p: var TPegParser): TPeg =
|
proc parseExpr(p: var PegParser): Peg =
|
||||||
result = seqExpr(p)
|
result = seqExpr(p)
|
||||||
while p.tok.kind == tkBar:
|
while p.tok.kind == tkBar:
|
||||||
getTok(p)
|
getTok(p)
|
||||||
result = result / seqExpr(p)
|
result = result / seqExpr(p)
|
||||||
|
|
||||||
proc parseRule(p: var TPegParser): PNonTerminal =
|
proc parseRule(p: var PegParser): PNonTerminal =
|
||||||
if p.tok.kind == tkIdentifier and arrowIsNextTok(p):
|
if p.tok.kind == tkIdentifier and arrowIsNextTok(p):
|
||||||
result = getNonTerminal(p, p.tok.literal)
|
result = getNonTerminal(p, p.tok.literal)
|
||||||
if ntDeclared in result.flags:
|
if ntDeclared in result.flags:
|
||||||
|
|
@ -1605,7 +1597,7 @@ proc parseRule(p: var TPegParser): PNonTerminal =
|
||||||
else:
|
else:
|
||||||
pegError(p, "rule expected, but found: " & p.tok.literal)
|
pegError(p, "rule expected, but found: " & p.tok.literal)
|
||||||
|
|
||||||
proc rawParse(p: var TPegParser): TPeg =
|
proc rawParse(p: var PegParser): Peg =
|
||||||
## parses a rule or a PEG expression
|
## parses a rule or a PEG expression
|
||||||
while p.tok.kind == tkBuiltin:
|
while p.tok.kind == tkBuiltin:
|
||||||
case p.tok.literal
|
case p.tok.literal
|
||||||
|
|
@ -1635,12 +1627,12 @@ proc rawParse(p: var TPegParser): TPeg =
|
||||||
elif ntUsed notin nt.flags and i > 0:
|
elif ntUsed notin nt.flags and i > 0:
|
||||||
pegError(p, "unused rule: " & nt.name, nt.line, nt.col)
|
pegError(p, "unused rule: " & nt.name, nt.line, nt.col)
|
||||||
|
|
||||||
proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): TPeg =
|
proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): Peg =
|
||||||
## constructs a TPeg object from `pattern`. `filename`, `line`, `col` are
|
## constructs a Peg object from `pattern`. `filename`, `line`, `col` are
|
||||||
## used for error messages, but they only provide start offsets. `parsePeg`
|
## used for error messages, but they only provide start offsets. `parsePeg`
|
||||||
## keeps track of line and column numbers within `pattern`.
|
## keeps track of line and column numbers within `pattern`.
|
||||||
var p: TPegParser
|
var p: PegParser
|
||||||
init(TPegLexer(p), pattern, filename, line, col)
|
init(PegLexer(p), pattern, filename, line, col)
|
||||||
p.tok.kind = tkInvalid
|
p.tok.kind = tkInvalid
|
||||||
p.tok.modifier = modNone
|
p.tok.modifier = modNone
|
||||||
p.tok.literal = ""
|
p.tok.literal = ""
|
||||||
|
|
@ -1650,8 +1642,8 @@ proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): TPeg =
|
||||||
getTok(p)
|
getTok(p)
|
||||||
result = rawParse(p)
|
result = rawParse(p)
|
||||||
|
|
||||||
proc peg*(pattern: string): TPeg =
|
proc peg*(pattern: string): Peg =
|
||||||
## constructs a TPeg object from the `pattern`. The short name has been
|
## constructs a Peg object from the `pattern`. The short name has been
|
||||||
## chosen to encourage its use as a raw string modifier::
|
## chosen to encourage its use as a raw string modifier::
|
||||||
##
|
##
|
||||||
## peg"{\ident} \s* '=' \s* {.*}"
|
## peg"{\ident} \s* '=' \s* {.*}"
|
||||||
|
|
@ -1704,7 +1696,7 @@ when isMainModule:
|
||||||
expr.rule = sequence(capture(ident), *sequence(
|
expr.rule = sequence(capture(ident), *sequence(
|
||||||
nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr)))
|
nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr)))
|
||||||
|
|
||||||
var c: TCaptures
|
var c: Captures
|
||||||
var s = "a+b + c +d+e+f"
|
var s = "a+b + c +d+e+f"
|
||||||
assert rawMatch(s, expr.rule, 0, c) == len(s)
|
assert rawMatch(s, expr.rule, 0, c) == len(s)
|
||||||
var a = ""
|
var a = ""
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2013 Robert Persson
|
# (c) Copyright 2013 Robert Persson
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -12,9 +12,10 @@ import strutils
|
||||||
import numeric
|
import numeric
|
||||||
|
|
||||||
type
|
type
|
||||||
TPoly* = object
|
Poly* = object
|
||||||
cofs:seq[float]
|
cofs:seq[float]
|
||||||
|
|
||||||
|
{.deprecated: [TPoly: Poly].}
|
||||||
|
|
||||||
proc degree*(p:TPoly):int=
|
proc degree*(p:TPoly):int=
|
||||||
## Returns the degree of the polynomial,
|
## Returns the degree of the polynomial,
|
||||||
|
|
@ -104,7 +105,7 @@ proc `$` *(p:TPoly):string =
|
||||||
result="0"
|
result="0"
|
||||||
|
|
||||||
|
|
||||||
proc derivative*(p:TPoly):TPoly=
|
proc derivative*(p: TPoly): TPoly=
|
||||||
## Returns a new polynomial, which is the derivative of `p`
|
## Returns a new polynomial, which is the derivative of `p`
|
||||||
newSeq[float](result.cofs,p.degree)
|
newSeq[float](result.cofs,p.degree)
|
||||||
for idx in 0..high(result.cofs):
|
for idx in 0..high(result.cofs):
|
||||||
|
|
@ -310,9 +311,9 @@ proc getRangeForRoots(p:TPoly):tuple[xmin,xmax:float]=
|
||||||
var bound1,bound2:float
|
var bound1,bound2:float
|
||||||
|
|
||||||
for i in countup(0,deg):
|
for i in countup(0,deg):
|
||||||
var c=abs(p.cofs[i]/d)
|
var c=abs(p.cofs[i]/d)
|
||||||
bound1=max(bound1,c+1.0)
|
bound1=max(bound1,c+1.0)
|
||||||
bound2=bound2+c
|
bound2=bound2+c
|
||||||
|
|
||||||
bound2=max(1.0,bound2)
|
bound2=max(1.0,bound2)
|
||||||
result.xmax=min(bound1,bound2)
|
result.xmax=min(bound1,bound2)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2014 Dominik Picheta
|
# (c) Copyright 2014 Dominik Picheta
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -28,7 +28,7 @@ else:
|
||||||
export fcntl, F_GETFL, O_NONBLOCK, F_SETFL, EAGAIN, EWOULDBLOCK, MSG_NOSIGNAL,
|
export fcntl, F_GETFL, O_NONBLOCK, F_SETFL, EAGAIN, EWOULDBLOCK, MSG_NOSIGNAL,
|
||||||
EINTR, EINPROGRESS, ECONNRESET, EPIPE, ENETRESET
|
EINTR, EINPROGRESS, ECONNRESET, EPIPE, ENETRESET
|
||||||
|
|
||||||
export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen,
|
export SocketHandle, Sockaddr_in, Addrinfo, INADDR_ANY, SockAddr, SockLen,
|
||||||
inet_ntoa, recv, `==`, connect, send, accept, recvfrom, sendto
|
inet_ntoa, recv, `==`, connect, send, accept, recvfrom, sendto
|
||||||
|
|
||||||
export
|
export
|
||||||
|
|
@ -40,22 +40,22 @@ export
|
||||||
MSG_PEEK
|
MSG_PEEK
|
||||||
|
|
||||||
type
|
type
|
||||||
TPort* = distinct uint16 ## port type
|
Port* = distinct uint16 ## port type
|
||||||
|
|
||||||
TDomain* = enum ## domain, which specifies the protocol family of the
|
Domain* = enum ## domain, which specifies the protocol family of the
|
||||||
## created socket. Other domains than those that are listed
|
## created socket. Other domains than those that are listed
|
||||||
## here are unsupported.
|
## here are unsupported.
|
||||||
AF_UNIX, ## for local socket (using a file). Unsupported on Windows.
|
AF_UNIX, ## for local socket (using a file). Unsupported on Windows.
|
||||||
AF_INET = 2, ## for network protocol IPv4 or
|
AF_INET = 2, ## for network protocol IPv4 or
|
||||||
AF_INET6 = 23 ## for network protocol IPv6.
|
AF_INET6 = 23 ## for network protocol IPv6.
|
||||||
|
|
||||||
TType* = enum ## second argument to `socket` proc
|
SockType* = enum ## second argument to `socket` proc
|
||||||
SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets
|
SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets
|
||||||
SOCK_DGRAM = 2, ## datagram service or Datagram Sockets
|
SOCK_DGRAM = 2, ## datagram service or Datagram Sockets
|
||||||
SOCK_RAW = 3, ## raw protocols atop the network layer.
|
SOCK_RAW = 3, ## raw protocols atop the network layer.
|
||||||
SOCK_SEQPACKET = 5 ## reliable sequenced packet service
|
SOCK_SEQPACKET = 5 ## reliable sequenced packet service
|
||||||
|
|
||||||
TProtocol* = enum ## third argument to `socket` proc
|
Protocol* = enum ## third argument to `socket` proc
|
||||||
IPPROTO_TCP = 6, ## Transmission control protocol.
|
IPPROTO_TCP = 6, ## Transmission control protocol.
|
||||||
IPPROTO_UDP = 17, ## User datagram protocol.
|
IPPROTO_UDP = 17, ## User datagram protocol.
|
||||||
IPPROTO_IP, ## Internet protocol. Unsupported on Windows.
|
IPPROTO_IP, ## Internet protocol. Unsupported on Windows.
|
||||||
|
|
@ -63,19 +63,22 @@ type
|
||||||
IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows.
|
IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows.
|
||||||
IPPROTO_ICMP ## Control message protocol. Unsupported on Windows.
|
IPPROTO_ICMP ## Control message protocol. Unsupported on Windows.
|
||||||
|
|
||||||
TServent* {.pure, final.} = object ## information about a service
|
Servent* = object ## information about a service
|
||||||
name*: string
|
name*: string
|
||||||
aliases*: seq[string]
|
aliases*: seq[string]
|
||||||
port*: TPort
|
port*: Port
|
||||||
proto*: string
|
proto*: string
|
||||||
|
|
||||||
Thostent* {.pure, final.} = object ## information about a given host
|
Hostent* = object ## information about a given host
|
||||||
name*: string
|
name*: string
|
||||||
aliases*: seq[string]
|
aliases*: seq[string]
|
||||||
addrtype*: TDomain
|
addrtype*: Domain
|
||||||
length*: int
|
length*: int
|
||||||
addrList*: seq[string]
|
addrList*: seq[string]
|
||||||
|
|
||||||
|
{.deprecated: [TPort: Port, TDomain: Domain, TType: SockType,
|
||||||
|
TProtocol: Protocol, TServent: Servent, THostent: Hostent].}
|
||||||
|
|
||||||
when useWinVersion:
|
when useWinVersion:
|
||||||
let
|
let
|
||||||
osInvalidSocket* = winlean.INVALID_SOCKET
|
osInvalidSocket* = winlean.INVALID_SOCKET
|
||||||
|
|
@ -86,37 +89,37 @@ when useWinVersion:
|
||||||
FIONBIO* = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or
|
FIONBIO* = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or
|
||||||
(102 shl 8) or 126
|
(102 shl 8) or 126
|
||||||
|
|
||||||
proc ioctlsocket*(s: TSocketHandle, cmd: clong,
|
proc ioctlsocket*(s: SocketHandle, cmd: clong,
|
||||||
argptr: ptr clong): cint {.
|
argptr: ptr clong): cint {.
|
||||||
stdcall, importc: "ioctlsocket", dynlib: "ws2_32.dll".}
|
stdcall, importc: "ioctlsocket", dynlib: "ws2_32.dll".}
|
||||||
else:
|
else:
|
||||||
let
|
let
|
||||||
osInvalidSocket* = posix.INVALID_SOCKET
|
osInvalidSocket* = posix.INVALID_SOCKET
|
||||||
|
|
||||||
proc `==`*(a, b: TPort): bool {.borrow.}
|
proc `==`*(a, b: Port): bool {.borrow.}
|
||||||
## ``==`` for ports.
|
## ``==`` for ports.
|
||||||
|
|
||||||
proc `$`*(p: TPort): string {.borrow.}
|
proc `$`*(p: Port): string {.borrow.}
|
||||||
## returns the port number as a string
|
## returns the port number as a string
|
||||||
|
|
||||||
proc toInt*(domain: TDomain): cint
|
proc toInt*(domain: Domain): cint
|
||||||
## Converts the TDomain enum to a platform-dependent ``cint``.
|
## Converts the TDomain enum to a platform-dependent ``cint``.
|
||||||
|
|
||||||
proc toInt*(typ: TType): cint
|
proc toInt*(typ: SockType): cint
|
||||||
## Converts the TType enum to a platform-dependent ``cint``.
|
## Converts the TType enum to a platform-dependent ``cint``.
|
||||||
|
|
||||||
proc toInt*(p: TProtocol): cint
|
proc toInt*(p: Protocol): cint
|
||||||
## Converts the TProtocol enum to a platform-dependent ``cint``.
|
## Converts the TProtocol enum to a platform-dependent ``cint``.
|
||||||
|
|
||||||
when not useWinVersion:
|
when not useWinVersion:
|
||||||
proc toInt(domain: TDomain): cint =
|
proc toInt(domain: Domain): cint =
|
||||||
case domain
|
case domain
|
||||||
of AF_UNIX: result = posix.AF_UNIX
|
of AF_UNIX: result = posix.AF_UNIX
|
||||||
of AF_INET: result = posix.AF_INET
|
of AF_INET: result = posix.AF_INET
|
||||||
of AF_INET6: result = posix.AF_INET6
|
of AF_INET6: result = posix.AF_INET6
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc toInt(typ: TType): cint =
|
proc toInt(typ: SockType): cint =
|
||||||
case typ
|
case typ
|
||||||
of SOCK_STREAM: result = posix.SOCK_STREAM
|
of SOCK_STREAM: result = posix.SOCK_STREAM
|
||||||
of SOCK_DGRAM: result = posix.SOCK_DGRAM
|
of SOCK_DGRAM: result = posix.SOCK_DGRAM
|
||||||
|
|
@ -124,7 +127,7 @@ when not useWinVersion:
|
||||||
of SOCK_RAW: result = posix.SOCK_RAW
|
of SOCK_RAW: result = posix.SOCK_RAW
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc toInt(p: TProtocol): cint =
|
proc toInt(p: Protocol): cint =
|
||||||
case p
|
case p
|
||||||
of IPPROTO_TCP: result = posix.IPPROTO_TCP
|
of IPPROTO_TCP: result = posix.IPPROTO_TCP
|
||||||
of IPPROTO_UDP: result = posix.IPPROTO_UDP
|
of IPPROTO_UDP: result = posix.IPPROTO_UDP
|
||||||
|
|
@ -135,22 +138,22 @@ when not useWinVersion:
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
else:
|
else:
|
||||||
proc toInt(domain: TDomain): cint =
|
proc toInt(domain: Domain): cint =
|
||||||
result = toU16(ord(domain))
|
result = toU16(ord(domain))
|
||||||
|
|
||||||
proc toInt(typ: TType): cint =
|
proc toInt(typ: SockType): cint =
|
||||||
result = cint(ord(typ))
|
result = cint(ord(typ))
|
||||||
|
|
||||||
proc toInt(p: TProtocol): cint =
|
proc toInt(p: Protocol): cint =
|
||||||
result = cint(ord(p))
|
result = cint(ord(p))
|
||||||
|
|
||||||
|
|
||||||
proc newRawSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
|
proc newRawSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
|
||||||
protocol: TProtocol = IPPROTO_TCP): TSocketHandle =
|
protocol: Protocol = IPPROTO_TCP): SocketHandle =
|
||||||
## Creates a new socket; returns `InvalidSocket` if an error occurs.
|
## Creates a new socket; returns `InvalidSocket` if an error occurs.
|
||||||
socket(toInt(domain), toInt(typ), toInt(protocol))
|
socket(toInt(domain), toInt(typ), toInt(protocol))
|
||||||
|
|
||||||
proc close*(socket: TSocketHandle) =
|
proc close*(socket: SocketHandle) =
|
||||||
## closes a socket.
|
## closes a socket.
|
||||||
when useWinVersion:
|
when useWinVersion:
|
||||||
discard winlean.closeSocket(socket)
|
discard winlean.closeSocket(socket)
|
||||||
|
|
@ -159,10 +162,10 @@ proc close*(socket: TSocketHandle) =
|
||||||
# TODO: These values should not be discarded. An EOS should be raised.
|
# TODO: These values should not be discarded. An EOS should be raised.
|
||||||
# http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times
|
# http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times
|
||||||
|
|
||||||
proc bindAddr*(socket: TSocketHandle, name: ptr TSockAddr, namelen: TSockLen): cint =
|
proc bindAddr*(socket: SocketHandle, name: ptr SockAddr, namelen: SockLen): cint =
|
||||||
result = bindSocket(socket, name, namelen)
|
result = bindSocket(socket, name, namelen)
|
||||||
|
|
||||||
proc listen*(socket: TSocketHandle, backlog = SOMAXCONN): cint {.tags: [FReadIO].} =
|
proc listen*(socket: SocketHandle, backlog = SOMAXCONN): cint {.tags: [FReadIO].} =
|
||||||
## Marks ``socket`` as accepting connections.
|
## Marks ``socket`` as accepting connections.
|
||||||
## ``Backlog`` specifies the maximum length of the
|
## ``Backlog`` specifies the maximum length of the
|
||||||
## queue of pending connections.
|
## queue of pending connections.
|
||||||
|
|
@ -171,12 +174,12 @@ proc listen*(socket: TSocketHandle, backlog = SOMAXCONN): cint {.tags: [FReadIO]
|
||||||
else:
|
else:
|
||||||
result = posix.listen(socket, cint(backlog))
|
result = posix.listen(socket, cint(backlog))
|
||||||
|
|
||||||
proc getAddrInfo*(address: string, port: TPort, af: TDomain = AF_INET, typ: TType = SOCK_STREAM,
|
proc getAddrInfo*(address: string, port: Port, af: Domain = AF_INET, typ: SockType = SOCK_STREAM,
|
||||||
prot: TProtocol = IPPROTO_TCP): ptr TAddrInfo =
|
prot: Protocol = IPPROTO_TCP): ptr AddrInfo =
|
||||||
##
|
##
|
||||||
##
|
##
|
||||||
## **Warning**: The resulting ``ptr TAddrInfo`` must be freed using ``dealloc``!
|
## **Warning**: The resulting ``ptr TAddrInfo`` must be freed using ``dealloc``!
|
||||||
var hints: TAddrInfo
|
var hints: AddrInfo
|
||||||
result = nil
|
result = nil
|
||||||
hints.ai_family = toInt(af)
|
hints.ai_family = toInt(af)
|
||||||
hints.ai_socktype = toInt(typ)
|
hints.ai_socktype = toInt(typ)
|
||||||
|
|
@ -184,11 +187,11 @@ proc getAddrInfo*(address: string, port: TPort, af: TDomain = AF_INET, typ: TTyp
|
||||||
var gaiResult = getAddrInfo(address, $port, addr(hints), result)
|
var gaiResult = getAddrInfo(address, $port, addr(hints), result)
|
||||||
if gaiResult != 0'i32:
|
if gaiResult != 0'i32:
|
||||||
when useWinVersion:
|
when useWinVersion:
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
else:
|
else:
|
||||||
raise newException(EOS, $gai_strerror(gaiResult))
|
raise newException(EOS, $gai_strerror(gaiResult))
|
||||||
|
|
||||||
proc dealloc*(ai: ptr TAddrInfo) =
|
proc dealloc*(ai: ptr AddrInfo) =
|
||||||
freeaddrinfo(ai)
|
freeaddrinfo(ai)
|
||||||
|
|
||||||
proc ntohl*(x: int32): int32 =
|
proc ntohl*(x: int32): int32 =
|
||||||
|
|
@ -220,7 +223,7 @@ proc htons*(x: int16): int16 =
|
||||||
## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
|
## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
|
||||||
result = rawsockets.ntohs(x)
|
result = rawsockets.ntohs(x)
|
||||||
|
|
||||||
proc getServByName*(name, proto: string): TServent {.tags: [FReadIO].} =
|
proc getServByName*(name, proto: string): Servent {.tags: [FReadIO].} =
|
||||||
## Searches the database from the beginning and finds the first entry for
|
## Searches the database from the beginning and finds the first entry for
|
||||||
## which the service name specified by ``name`` matches the s_name member
|
## which the service name specified by ``name`` matches the s_name member
|
||||||
## and the protocol name specified by ``proto`` matches the s_proto member.
|
## and the protocol name specified by ``proto`` matches the s_proto member.
|
||||||
|
|
@ -233,10 +236,10 @@ proc getServByName*(name, proto: string): TServent {.tags: [FReadIO].} =
|
||||||
if s == nil: raise newException(EOS, "Service not found.")
|
if s == nil: raise newException(EOS, "Service not found.")
|
||||||
result.name = $s.s_name
|
result.name = $s.s_name
|
||||||
result.aliases = cstringArrayToSeq(s.s_aliases)
|
result.aliases = cstringArrayToSeq(s.s_aliases)
|
||||||
result.port = TPort(s.s_port)
|
result.port = Port(s.s_port)
|
||||||
result.proto = $s.s_proto
|
result.proto = $s.s_proto
|
||||||
|
|
||||||
proc getServByPort*(port: TPort, proto: string): TServent {.tags: [FReadIO].} =
|
proc getServByPort*(port: Port, proto: string): Servent {.tags: [FReadIO].} =
|
||||||
## Searches the database from the beginning and finds the first entry for
|
## Searches the database from the beginning and finds the first entry for
|
||||||
## which the port specified by ``port`` matches the s_port member and the
|
## which the port specified by ``port`` matches the s_port member and the
|
||||||
## protocol name specified by ``proto`` matches the s_proto member.
|
## protocol name specified by ``proto`` matches the s_proto member.
|
||||||
|
|
@ -249,20 +252,20 @@ proc getServByPort*(port: TPort, proto: string): TServent {.tags: [FReadIO].} =
|
||||||
if s == nil: raise newException(EOS, "Service not found.")
|
if s == nil: raise newException(EOS, "Service not found.")
|
||||||
result.name = $s.s_name
|
result.name = $s.s_name
|
||||||
result.aliases = cstringArrayToSeq(s.s_aliases)
|
result.aliases = cstringArrayToSeq(s.s_aliases)
|
||||||
result.port = TPort(s.s_port)
|
result.port = Port(s.s_port)
|
||||||
result.proto = $s.s_proto
|
result.proto = $s.s_proto
|
||||||
|
|
||||||
proc getHostByAddr*(ip: string): Thostent {.tags: [FReadIO].} =
|
proc getHostByAddr*(ip: string): Hostent {.tags: [FReadIO].} =
|
||||||
## This function will lookup the hostname of an IP Address.
|
## This function will lookup the hostname of an IP Address.
|
||||||
var myaddr: TInAddr
|
var myaddr: InAddr
|
||||||
myaddr.s_addr = inet_addr(ip)
|
myaddr.s_addr = inet_addr(ip)
|
||||||
|
|
||||||
when useWinVersion:
|
when useWinVersion:
|
||||||
var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
|
var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
|
||||||
cint(rawsockets.AF_INET))
|
cint(rawsockets.AF_INET))
|
||||||
if s == nil: osError(osLastError())
|
if s == nil: raiseOSError(osLastError())
|
||||||
else:
|
else:
|
||||||
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).TSocklen,
|
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).Socklen,
|
||||||
cint(posix.AF_INET))
|
cint(posix.AF_INET))
|
||||||
if s == nil:
|
if s == nil:
|
||||||
raise newException(EOS, $hstrerror(h_errno))
|
raise newException(EOS, $hstrerror(h_errno))
|
||||||
|
|
@ -270,7 +273,7 @@ proc getHostByAddr*(ip: string): Thostent {.tags: [FReadIO].} =
|
||||||
result.name = $s.h_name
|
result.name = $s.h_name
|
||||||
result.aliases = cstringArrayToSeq(s.h_aliases)
|
result.aliases = cstringArrayToSeq(s.h_aliases)
|
||||||
when useWinVersion:
|
when useWinVersion:
|
||||||
result.addrtype = TDomain(s.h_addrtype)
|
result.addrtype = Domain(s.h_addrtype)
|
||||||
else:
|
else:
|
||||||
if s.h_addrtype == posix.AF_INET:
|
if s.h_addrtype == posix.AF_INET:
|
||||||
result.addrtype = AF_INET
|
result.addrtype = AF_INET
|
||||||
|
|
@ -281,17 +284,17 @@ proc getHostByAddr*(ip: string): Thostent {.tags: [FReadIO].} =
|
||||||
result.addrList = cstringArrayToSeq(s.h_addr_list)
|
result.addrList = cstringArrayToSeq(s.h_addr_list)
|
||||||
result.length = int(s.h_length)
|
result.length = int(s.h_length)
|
||||||
|
|
||||||
proc getHostByName*(name: string): Thostent {.tags: [FReadIO].} =
|
proc getHostByName*(name: string): Hostent {.tags: [FReadIO].} =
|
||||||
## This function will lookup the IP address of a hostname.
|
## This function will lookup the IP address of a hostname.
|
||||||
when useWinVersion:
|
when useWinVersion:
|
||||||
var s = winlean.gethostbyname(name)
|
var s = winlean.gethostbyname(name)
|
||||||
else:
|
else:
|
||||||
var s = posix.gethostbyname(name)
|
var s = posix.gethostbyname(name)
|
||||||
if s == nil: osError(osLastError())
|
if s == nil: raiseOSError(osLastError())
|
||||||
result.name = $s.h_name
|
result.name = $s.h_name
|
||||||
result.aliases = cstringArrayToSeq(s.h_aliases)
|
result.aliases = cstringArrayToSeq(s.h_aliases)
|
||||||
when useWinVersion:
|
when useWinVersion:
|
||||||
result.addrtype = TDomain(s.h_addrtype)
|
result.addrtype = Domain(s.h_addrtype)
|
||||||
else:
|
else:
|
||||||
if s.h_addrtype == posix.AF_INET:
|
if s.h_addrtype == posix.AF_INET:
|
||||||
result.addrtype = AF_INET
|
result.addrtype = AF_INET
|
||||||
|
|
@ -302,69 +305,69 @@ proc getHostByName*(name: string): Thostent {.tags: [FReadIO].} =
|
||||||
result.addrList = cstringArrayToSeq(s.h_addr_list)
|
result.addrList = cstringArrayToSeq(s.h_addr_list)
|
||||||
result.length = int(s.h_length)
|
result.length = int(s.h_length)
|
||||||
|
|
||||||
proc getSockName*(socket: TSocketHandle): TPort =
|
proc getSockName*(socket: SocketHandle): Port =
|
||||||
## returns the socket's associated port number.
|
## returns the socket's associated port number.
|
||||||
var name: Tsockaddr_in
|
var name: Sockaddr_in
|
||||||
when useWinVersion:
|
when useWinVersion:
|
||||||
name.sin_family = int16(ord(AF_INET))
|
name.sin_family = int16(ord(AF_INET))
|
||||||
else:
|
else:
|
||||||
name.sin_family = posix.AF_INET
|
name.sin_family = posix.AF_INET
|
||||||
#name.sin_port = htons(cint16(port))
|
#name.sin_port = htons(cint16(port))
|
||||||
#name.sin_addr.s_addr = htonl(INADDR_ANY)
|
#name.sin_addr.s_addr = htonl(INADDR_ANY)
|
||||||
var namelen = sizeof(name).TSocklen
|
var namelen = sizeof(name).Socklen
|
||||||
if getsockname(socket, cast[ptr TSockAddr](addr(name)),
|
if getsockname(socket, cast[ptr SockAddr](addr(name)),
|
||||||
addr(namelen)) == -1'i32:
|
addr(namelen)) == -1'i32:
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
result = TPort(rawsockets.ntohs(name.sin_port))
|
result = Port(rawsockets.ntohs(name.sin_port))
|
||||||
|
|
||||||
proc getSockOptInt*(socket: TSocketHandle, level, optname: int): int {.
|
proc getSockOptInt*(socket: SocketHandle, level, optname: int): int {.
|
||||||
tags: [FReadIO].} =
|
tags: [FReadIO].} =
|
||||||
## getsockopt for integer options.
|
## getsockopt for integer options.
|
||||||
var res: cint
|
var res: cint
|
||||||
var size = sizeof(res).TSocklen
|
var size = sizeof(res).Socklen
|
||||||
if getsockopt(socket, cint(level), cint(optname),
|
if getsockopt(socket, cint(level), cint(optname),
|
||||||
addr(res), addr(size)) < 0'i32:
|
addr(res), addr(size)) < 0'i32:
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
result = int(res)
|
result = int(res)
|
||||||
|
|
||||||
proc setSockOptInt*(socket: TSocketHandle, level, optname, optval: int) {.
|
proc setSockOptInt*(socket: SocketHandle, level, optname, optval: int) {.
|
||||||
tags: [FWriteIO].} =
|
tags: [FWriteIO].} =
|
||||||
## setsockopt for integer options.
|
## setsockopt for integer options.
|
||||||
var value = cint(optval)
|
var value = cint(optval)
|
||||||
if setsockopt(socket, cint(level), cint(optname), addr(value),
|
if setsockopt(socket, cint(level), cint(optname), addr(value),
|
||||||
sizeof(value).TSocklen) < 0'i32:
|
sizeof(value).Socklen) < 0'i32:
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
proc setBlocking*(s: TSocketHandle, blocking: bool) =
|
proc setBlocking*(s: SocketHandle, blocking: bool) =
|
||||||
## Sets blocking mode on socket.
|
## Sets blocking mode on socket.
|
||||||
##
|
##
|
||||||
## Raises EOS on error.
|
## Raises EOS on error.
|
||||||
when useWinVersion:
|
when useWinVersion:
|
||||||
var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
|
var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
|
||||||
if ioctlsocket(s, FIONBIO, addr(mode)) == -1:
|
if ioctlsocket(s, FIONBIO, addr(mode)) == -1:
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
else: # BSD sockets
|
else: # BSD sockets
|
||||||
var x: int = fcntl(s, F_GETFL, 0)
|
var x: int = fcntl(s, F_GETFL, 0)
|
||||||
if x == -1:
|
if x == -1:
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
else:
|
else:
|
||||||
var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
|
var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
|
||||||
if fcntl(s, F_SETFL, mode) == -1:
|
if fcntl(s, F_SETFL, mode) == -1:
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
proc timeValFromMilliseconds(timeout = 500): Ttimeval =
|
proc timeValFromMilliseconds(timeout = 500): Timeval =
|
||||||
if timeout != -1:
|
if timeout != -1:
|
||||||
var seconds = timeout div 1000
|
var seconds = timeout div 1000
|
||||||
result.tv_sec = seconds.int32
|
result.tv_sec = seconds.int32
|
||||||
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
|
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
|
||||||
|
|
||||||
proc createFdSet(fd: var TFdSet, s: seq[TSocketHandle], m: var int) =
|
proc createFdSet(fd: var FdSet, s: seq[SocketHandle], m: var int) =
|
||||||
FD_ZERO(fd)
|
FD_ZERO(fd)
|
||||||
for i in items(s):
|
for i in items(s):
|
||||||
m = max(m, int(i))
|
m = max(m, int(i))
|
||||||
FD_SET(i, fd)
|
FD_SET(i, fd)
|
||||||
|
|
||||||
proc pruneSocketSet(s: var seq[TSocketHandle], fd: var TFdSet) =
|
proc pruneSocketSet(s: var seq[SocketHandle], fd: var FdSet) =
|
||||||
var i = 0
|
var i = 0
|
||||||
var L = s.len
|
var L = s.len
|
||||||
while i < L:
|
while i < L:
|
||||||
|
|
@ -375,7 +378,7 @@ proc pruneSocketSet(s: var seq[TSocketHandle], fd: var TFdSet) =
|
||||||
inc(i)
|
inc(i)
|
||||||
setLen(s, L)
|
setLen(s, L)
|
||||||
|
|
||||||
proc select*(readfds: var seq[TSocketHandle], timeout = 500): int =
|
proc select*(readfds: var seq[SocketHandle], timeout = 500): int =
|
||||||
## Traditional select function. This function will return the number of
|
## Traditional select function. This function will return the number of
|
||||||
## sockets that are ready to be read from, written to, or which have errors.
|
## sockets that are ready to be read from, written to, or which have errors.
|
||||||
## If there are none; 0 is returned.
|
## If there are none; 0 is returned.
|
||||||
|
|
@ -396,7 +399,7 @@ proc select*(readfds: var seq[TSocketHandle], timeout = 500): int =
|
||||||
|
|
||||||
pruneSocketSet(readfds, (rd))
|
pruneSocketSet(readfds, (rd))
|
||||||
|
|
||||||
proc selectWrite*(writefds: var seq[TSocketHandle],
|
proc selectWrite*(writefds: var seq[SocketHandle],
|
||||||
timeout = 500): int {.tags: [FReadIO].} =
|
timeout = 500): int {.tags: [FReadIO].} =
|
||||||
## When a socket in ``writefds`` is ready to be written to then a non-zero
|
## When a socket in ``writefds`` is ready to be written to then a non-zero
|
||||||
## value will be returned specifying the count of the sockets which can be
|
## value will be returned specifying the count of the sockets which can be
|
||||||
|
|
@ -405,7 +408,7 @@ proc selectWrite*(writefds: var seq[TSocketHandle],
|
||||||
##
|
##
|
||||||
## ``timeout`` is specified in miliseconds and ``-1`` can be specified for
|
## ``timeout`` is specified in miliseconds and ``-1`` can be specified for
|
||||||
## an unlimited time.
|
## an unlimited time.
|
||||||
var tv {.noInit.}: Ttimeval = timeValFromMilliseconds(timeout)
|
var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
|
||||||
|
|
||||||
var wr: TFdSet
|
var wr: TFdSet
|
||||||
var m = 0
|
var m = 0
|
||||||
|
|
@ -419,5 +422,5 @@ proc selectWrite*(writefds: var seq[TSocketHandle],
|
||||||
pruneSocketSet(writefds, (wr))
|
pruneSocketSet(writefds, (wr))
|
||||||
|
|
||||||
when defined(Windows):
|
when defined(Windows):
|
||||||
var wsa: TWSADATA
|
var wsa: WSADATA
|
||||||
if WSAStartup(0x0101'i16, addr wsa) != 0: osError(osLastError())
|
if WSAStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError())
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Dominik Picheta
|
# (c) Copyright 2012 Dominik Picheta
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -20,36 +20,40 @@ const
|
||||||
redisNil* = "\0\0"
|
redisNil* = "\0\0"
|
||||||
|
|
||||||
type
|
type
|
||||||
PPipeline = ref object
|
Pipeline = ref object
|
||||||
enabled: bool
|
enabled: bool
|
||||||
buffer: string
|
buffer: string
|
||||||
expected: int ## number of replies expected if pipelined
|
expected: int ## number of replies expected if pipelined
|
||||||
|
|
||||||
type
|
type
|
||||||
TSendMode = enum
|
SendMode = enum
|
||||||
normal, pipelined, multiple
|
normal, pipelined, multiple
|
||||||
|
|
||||||
type
|
type
|
||||||
TRedis* {.pure, final.} = object
|
Redis* = object
|
||||||
socket: TSocket
|
socket: TSocket
|
||||||
connected: bool
|
connected: bool
|
||||||
pipeline: PPipeline
|
pipeline: Pipeline
|
||||||
|
|
||||||
TRedisStatus* = string
|
RedisStatus* = string
|
||||||
TRedisInteger* = biggestInt
|
RedisInteger* = BiggestInt
|
||||||
TRedisString* = string ## Bulk reply
|
RedisString* = string ## Bulk reply
|
||||||
TRedisList* = seq[TRedisString] ## Multi-bulk reply
|
RedisList* = seq[RedisString] ## Multi-bulk reply
|
||||||
|
|
||||||
EInvalidReply* = object of ESynch ## Invalid reply from redis
|
ReplyError* = object of IOError ## Invalid reply from redis
|
||||||
ERedis* = object of ESynch ## Error in redis
|
RedisError* = object of IOError ## Error in redis
|
||||||
|
|
||||||
proc newPipeline(): PPipeline =
|
{.deprecated: [TSendMode: SendMode, TRedis: Redis, TRedisStatus: RedisStatus,
|
||||||
|
TRedisInteger: RedisInteger, TRedisString: RedisString,
|
||||||
|
TRedisList: RedistList, EInvalidReply: ReplyError, ERedis: RedisError].}
|
||||||
|
|
||||||
|
proc newPipeline(): Pipeline =
|
||||||
new(result)
|
new(result)
|
||||||
result.buffer = ""
|
result.buffer = ""
|
||||||
result.enabled = false
|
result.enabled = false
|
||||||
result.expected = 0
|
result.expected = 0
|
||||||
|
|
||||||
proc open*(host = "localhost", port = 6379.TPort): TRedis =
|
proc open*(host = "localhost", port = 6379.Port): Redis =
|
||||||
## Opens a connection to the redis server.
|
## Opens a connection to the redis server.
|
||||||
result.socket = socket(buffered = false)
|
result.socket = socket(buffered = false)
|
||||||
if result.socket == InvalidSocket:
|
if result.socket == InvalidSocket:
|
||||||
|
|
@ -58,24 +62,24 @@ proc open*(host = "localhost", port = 6379.TPort): TRedis =
|
||||||
result.pipeline = newPipeline()
|
result.pipeline = newPipeline()
|
||||||
|
|
||||||
proc raiseInvalidReply(expected, got: char) =
|
proc raiseInvalidReply(expected, got: char) =
|
||||||
raise newException(EInvalidReply,
|
raise newException(ReplyError,
|
||||||
"Expected '$1' at the beginning of a status reply got '$2'" %
|
"Expected '$1' at the beginning of a status reply got '$2'" %
|
||||||
[$expected, $got])
|
[$expected, $got])
|
||||||
|
|
||||||
proc raiseNoOK(status: string, pipelineEnabled:bool) =
|
proc raiseNoOK(status: string, pipelineEnabled: bool) =
|
||||||
if pipelineEnabled and not (status == "QUEUED" or status == "PIPELINED"):
|
if pipelineEnabled and not (status == "QUEUED" or status == "PIPELINED"):
|
||||||
raise newException(EInvalidReply, "Expected \"QUEUED\" or \"PIPELINED\" got \"$1\"" % status)
|
raise newException(EInvalidReply, "Expected \"QUEUED\" or \"PIPELINED\" got \"$1\"" % status)
|
||||||
elif not pipelineEnabled and status != "OK":
|
elif not pipelineEnabled and status != "OK":
|
||||||
raise newException(EInvalidReply, "Expected \"OK\" got \"$1\"" % status)
|
raise newException(EInvalidReply, "Expected \"OK\" got \"$1\"" % status)
|
||||||
|
|
||||||
template readSocket(r: TRedis, dummyVal:expr): stmt =
|
template readSocket(r: Redis, dummyVal:expr): stmt =
|
||||||
var line {.inject.} :TaintedString = ""
|
var line {.inject.}: TaintedString = ""
|
||||||
if r.pipeline.enabled:
|
if r.pipeline.enabled:
|
||||||
return dummyVal
|
return dummyVal
|
||||||
else:
|
else:
|
||||||
readLine(r.socket, line)
|
readLine(r.socket, line)
|
||||||
|
|
||||||
proc parseStatus(r: TRedis, line: string = ""): TRedisStatus =
|
proc parseStatus(r: Redis, line: string = ""): RedisStatus =
|
||||||
if r.pipeline.enabled:
|
if r.pipeline.enabled:
|
||||||
return "PIPELINED"
|
return "PIPELINED"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2011 Philippe Lhoste
|
# (c) Copyright 2011 Philippe Lhoste
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2010 Andreas Rumpf
|
# (c) Copyright 2010 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -30,13 +30,15 @@ var
|
||||||
cacheEnabled = false
|
cacheEnabled = false
|
||||||
|
|
||||||
type
|
type
|
||||||
PRope* = ref TRope ## empty rope is represented by nil
|
Rope* = ref RopeObj ## empty rope is represented by nil
|
||||||
TRope {.acyclic, final, pure.} = object
|
RopeObj {.acyclic.} = object
|
||||||
left, right: PRope
|
left, right: Rope
|
||||||
length: int
|
length: int
|
||||||
data: string # != nil if a leaf
|
data: string # != nil if a leaf
|
||||||
|
|
||||||
proc isConc(r: PRope): bool {.inline.} = return isNil(r.data)
|
{.deprecated: [PRope: Rope].}
|
||||||
|
|
||||||
|
proc isConc(r: Rope): bool {.inline.} = return isNil(r.data)
|
||||||
|
|
||||||
# Note that the left and right pointers are not needed for leafs.
|
# Note that the left and right pointers are not needed for leafs.
|
||||||
# Leaves have relatively high memory overhead (~30 bytes on a 32
|
# Leaves have relatively high memory overhead (~30 bytes on a 32
|
||||||
|
|
@ -46,25 +48,25 @@ proc isConc(r: PRope): bool {.inline.} = return isNil(r.data)
|
||||||
# performance. But for the caching tree we use the leaf's left and right
|
# performance. But for the caching tree we use the leaf's left and right
|
||||||
# pointers.
|
# pointers.
|
||||||
|
|
||||||
proc len*(a: PRope): int {.rtl, extern: "nro$1".} =
|
proc len*(a: Rope): int {.rtl, extern: "nro$1".} =
|
||||||
## the rope's length
|
## the rope's length
|
||||||
if a == nil: result = 0
|
if a == nil: result = 0
|
||||||
else: result = a.length
|
else: result = a.length
|
||||||
|
|
||||||
proc newRope(): PRope = new(result)
|
proc newRope(): Rope = new(result)
|
||||||
proc newRope(data: string): PRope =
|
proc newRope(data: string): Rope =
|
||||||
new(result)
|
new(result)
|
||||||
result.length = len(data)
|
result.length = len(data)
|
||||||
result.data = data
|
result.data = data
|
||||||
|
|
||||||
var
|
var
|
||||||
cache {.threadvar.}: PRope # the root of the cache tree
|
cache {.threadvar.}: Rope # the root of the cache tree
|
||||||
N {.threadvar.}: PRope # dummy rope needed for splay algorithm
|
N {.threadvar.}: Rope # dummy rope needed for splay algorithm
|
||||||
|
|
||||||
when countCacheMisses:
|
when countCacheMisses:
|
||||||
var misses, hits: int
|
var misses, hits: int
|
||||||
|
|
||||||
proc splay(s: string, tree: PRope, cmpres: var int): PRope =
|
proc splay(s: string, tree: Rope, cmpres: var int): Rope =
|
||||||
var c: int
|
var c: int
|
||||||
var t = tree
|
var t = tree
|
||||||
N.left = nil
|
N.left = nil
|
||||||
|
|
@ -102,7 +104,7 @@ proc splay(s: string, tree: PRope, cmpres: var int): PRope =
|
||||||
t.right = N.left
|
t.right = N.left
|
||||||
result = t
|
result = t
|
||||||
|
|
||||||
proc insertInCache(s: string, tree: PRope): PRope =
|
proc insertInCache(s: string, tree: Rope): Rope =
|
||||||
var t = tree
|
var t = tree
|
||||||
if t == nil:
|
if t == nil:
|
||||||
result = newRope(s)
|
result = newRope(s)
|
||||||
|
|
@ -128,7 +130,7 @@ proc insertInCache(s: string, tree: PRope): PRope =
|
||||||
result.left = t
|
result.left = t
|
||||||
t.right = nil
|
t.right = nil
|
||||||
|
|
||||||
proc rope*(s: string): PRope {.rtl, extern: "nro$1Str".} =
|
proc rope*(s: string): Rope {.rtl, extern: "nro$1Str".} =
|
||||||
## Converts a string to a rope.
|
## Converts a string to a rope.
|
||||||
if s.len == 0:
|
if s.len == 0:
|
||||||
result = nil
|
result = nil
|
||||||
|
|
@ -138,11 +140,11 @@ proc rope*(s: string): PRope {.rtl, extern: "nro$1Str".} =
|
||||||
else:
|
else:
|
||||||
result = newRope(s)
|
result = newRope(s)
|
||||||
|
|
||||||
proc rope*(i: BiggestInt): PRope {.rtl, extern: "nro$1BiggestInt".} =
|
proc rope*(i: BiggestInt): Rope {.rtl, extern: "nro$1BiggestInt".} =
|
||||||
## Converts an int to a rope.
|
## Converts an int to a rope.
|
||||||
result = rope($i)
|
result = rope($i)
|
||||||
|
|
||||||
proc rope*(f: BiggestFloat): PRope {.rtl, extern: "nro$1BiggestFloat".} =
|
proc rope*(f: BiggestFloat): Rope {.rtl, extern: "nro$1BiggestFloat".} =
|
||||||
## Converts a float to a rope.
|
## Converts a float to a rope.
|
||||||
result = rope($f)
|
result = rope($f)
|
||||||
|
|
||||||
|
|
@ -156,7 +158,7 @@ proc disableCache*() {.rtl, extern: "nro$1".} =
|
||||||
cache = nil
|
cache = nil
|
||||||
cacheEnabled = false
|
cacheEnabled = false
|
||||||
|
|
||||||
proc `&`*(a, b: PRope): PRope {.rtl, extern: "nroConcRopeRope".} =
|
proc `&`*(a, b: Rope): Rope {.rtl, extern: "nroConcRopeRope".} =
|
||||||
## the concatenation operator for ropes.
|
## the concatenation operator for ropes.
|
||||||
if a == nil:
|
if a == nil:
|
||||||
result = b
|
result = b
|
||||||
|
|
@ -176,27 +178,27 @@ proc `&`*(a, b: PRope): PRope {.rtl, extern: "nroConcRopeRope".} =
|
||||||
result.left = a
|
result.left = a
|
||||||
result.right = b
|
result.right = b
|
||||||
|
|
||||||
proc `&`*(a: PRope, b: string): PRope {.rtl, extern: "nroConcRopeStr".} =
|
proc `&`*(a: Rope, b: string): Rope {.rtl, extern: "nroConcRopeStr".} =
|
||||||
## the concatenation operator for ropes.
|
## the concatenation operator for ropes.
|
||||||
result = a & rope(b)
|
result = a & rope(b)
|
||||||
|
|
||||||
proc `&`*(a: string, b: PRope): PRope {.rtl, extern: "nroConcStrRope".} =
|
proc `&`*(a: string, b: Rope): Rope {.rtl, extern: "nroConcStrRope".} =
|
||||||
## the concatenation operator for ropes.
|
## the concatenation operator for ropes.
|
||||||
result = rope(a) & b
|
result = rope(a) & b
|
||||||
|
|
||||||
proc `&`*(a: openarray[PRope]): PRope {.rtl, extern: "nroConcOpenArray".} =
|
proc `&`*(a: openarray[Rope]): Rope {.rtl, extern: "nroConcOpenArray".} =
|
||||||
## the concatenation operator for an openarray of ropes.
|
## the concatenation operator for an openarray of ropes.
|
||||||
for i in countup(0, high(a)): result = result & a[i]
|
for i in countup(0, high(a)): result = result & a[i]
|
||||||
|
|
||||||
proc add*(a: var PRope, b: PRope) {.rtl, extern: "nro$1Rope".} =
|
proc add*(a: var Rope, b: Rope) {.rtl, extern: "nro$1Rope".} =
|
||||||
## adds `b` to the rope `a`.
|
## adds `b` to the rope `a`.
|
||||||
a = a & b
|
a = a & b
|
||||||
|
|
||||||
proc add*(a: var PRope, b: string) {.rtl, extern: "nro$1Str".} =
|
proc add*(a: var Rope, b: string) {.rtl, extern: "nro$1Str".} =
|
||||||
## adds `b` to the rope `a`.
|
## adds `b` to the rope `a`.
|
||||||
a = a & b
|
a = a & b
|
||||||
|
|
||||||
proc `[]`*(r: PRope, i: int): char {.rtl, extern: "nroCharAt".} =
|
proc `[]`*(r: Rope, i: int): char {.rtl, extern: "nroCharAt".} =
|
||||||
## returns the character at position `i` in the rope `r`. This is quite
|
## returns the character at position `i` in the rope `r`. This is quite
|
||||||
## expensive! Worst-case: O(n). If ``i >= r.len``, ``\0`` is returned.
|
## expensive! Worst-case: O(n). If ``i >= r.len``, ``\0`` is returned.
|
||||||
var x = r
|
var x = r
|
||||||
|
|
@ -213,7 +215,7 @@ proc `[]`*(r: PRope, i: int): char {.rtl, extern: "nroCharAt".} =
|
||||||
x = x.right
|
x = x.right
|
||||||
dec(j, x.len)
|
dec(j, x.len)
|
||||||
|
|
||||||
iterator leaves*(r: PRope): string =
|
iterator leaves*(r: Rope): string =
|
||||||
## iterates over any leaf string in the rope `r`.
|
## iterates over any leaf string in the rope `r`.
|
||||||
if r != nil:
|
if r != nil:
|
||||||
var stack = @[r]
|
var stack = @[r]
|
||||||
|
|
@ -226,16 +228,16 @@ iterator leaves*(r: PRope): string =
|
||||||
assert(it.data != nil)
|
assert(it.data != nil)
|
||||||
yield it.data
|
yield it.data
|
||||||
|
|
||||||
iterator items*(r: PRope): char =
|
iterator items*(r: Rope): char =
|
||||||
## iterates over any character in the rope `r`.
|
## iterates over any character in the rope `r`.
|
||||||
for s in leaves(r):
|
for s in leaves(r):
|
||||||
for c in items(s): yield c
|
for c in items(s): yield c
|
||||||
|
|
||||||
proc write*(f: TFile, r: PRope) {.rtl, extern: "nro$1".} =
|
proc write*(f: TFile, r: Rope) {.rtl, extern: "nro$1".} =
|
||||||
## writes a rope to a file.
|
## writes a rope to a file.
|
||||||
for s in leaves(r): write(f, s)
|
for s in leaves(r): write(f, s)
|
||||||
|
|
||||||
proc `$`*(r: PRope): string {.rtl, extern: "nroToString".}=
|
proc `$`*(r: Rope): string {.rtl, extern: "nroToString".}=
|
||||||
## converts a rope back to a string.
|
## converts a rope back to a string.
|
||||||
result = newString(r.len)
|
result = newString(r.len)
|
||||||
setLen(result, 0)
|
setLen(result, 0)
|
||||||
|
|
@ -245,11 +247,11 @@ when false:
|
||||||
# Format string caching seems reasonable: All leaves can be shared and format
|
# Format string caching seems reasonable: All leaves can be shared and format
|
||||||
# string parsing has to be done only once. A compiled format string is stored
|
# string parsing has to be done only once. A compiled format string is stored
|
||||||
# as a rope. A negative length is used for the index into the args array.
|
# as a rope. A negative length is used for the index into the args array.
|
||||||
proc compiledArg(idx: int): PRope =
|
proc compiledArg(idx: int): Rope =
|
||||||
new(result)
|
new(result)
|
||||||
result.length = -idx
|
result.length = -idx
|
||||||
|
|
||||||
proc compileFrmt(frmt: string): PRope =
|
proc compileFrmt(frmt: string): Rope =
|
||||||
var i = 0
|
var i = 0
|
||||||
var length = len(frmt)
|
var length = len(frmt)
|
||||||
result = nil
|
result = nil
|
||||||
|
|
@ -289,7 +291,7 @@ when false:
|
||||||
if i - 1 >= start:
|
if i - 1 >= start:
|
||||||
add(result, substr(frmt, start, i-1))
|
add(result, substr(frmt, start, i-1))
|
||||||
|
|
||||||
proc `%`*(frmt: string, args: openarray[PRope]): PRope {.
|
proc `%`*(frmt: string, args: openarray[Rope]): Rope {.
|
||||||
rtl, extern: "nroFormat".} =
|
rtl, extern: "nroFormat".} =
|
||||||
## `%` substitution operator for ropes. Does not support the ``$identifier``
|
## `%` substitution operator for ropes. Does not support the ``$identifier``
|
||||||
## nor ``${identifier}`` notations.
|
## nor ``${identifier}`` notations.
|
||||||
|
|
@ -332,12 +334,12 @@ proc `%`*(frmt: string, args: openarray[PRope]): PRope {.
|
||||||
if i - 1 >= start:
|
if i - 1 >= start:
|
||||||
add(result, substr(frmt, start, i - 1))
|
add(result, substr(frmt, start, i - 1))
|
||||||
|
|
||||||
proc addf*(c: var PRope, frmt: string, args: openarray[PRope]) {.
|
proc addf*(c: var Rope, frmt: string, args: openarray[Rope]) {.
|
||||||
rtl, extern: "nro$1".} =
|
rtl, extern: "nro$1".} =
|
||||||
## shortcut for ``add(c, frmt % args)``.
|
## shortcut for ``add(c, frmt % args)``.
|
||||||
add(c, frmt % args)
|
add(c, frmt % args)
|
||||||
|
|
||||||
proc equalsFile*(r: PRope, f: TFile): bool {.rtl, extern: "nro$1File".} =
|
proc equalsFile*(r: Rope, f: TFile): bool {.rtl, extern: "nro$1File".} =
|
||||||
## returns true if the contents of the file `f` equal `r`.
|
## returns true if the contents of the file `f` equal `r`.
|
||||||
var bufSize = 1024 # reasonable start value
|
var bufSize = 1024 # reasonable start value
|
||||||
var buf = alloc(BufSize)
|
var buf = alloc(BufSize)
|
||||||
|
|
@ -352,7 +354,7 @@ proc equalsFile*(r: PRope, f: TFile): bool {.rtl, extern: "nro$1File".} =
|
||||||
result = readBuffer(f, buf, 1) == 0 # really at the end of file?
|
result = readBuffer(f, buf, 1) == 0 # really at the end of file?
|
||||||
dealloc(buf)
|
dealloc(buf)
|
||||||
|
|
||||||
proc equalsFile*(r: PRope, f: string): bool {.rtl, extern: "nro$1Str".} =
|
proc equalsFile*(r: Rope, f: string): bool {.rtl, extern: "nro$1Str".} =
|
||||||
## returns true if the contents of the file `f` equal `r`. If `f` does not
|
## returns true if the contents of the file `f` equal `r`. If `f` does not
|
||||||
## exist, false is returned.
|
## exist, false is returned.
|
||||||
var bin: TFile
|
var bin: TFile
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2013 Andreas Rumpf, Dominik Picheta
|
# (c) Copyright 2013 Andreas Rumpf, Dominik Picheta
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -9,13 +9,13 @@
|
||||||
|
|
||||||
## This module implements helper procs for SCGI applications. Example:
|
## This module implements helper procs for SCGI applications. Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: Nimrod
|
## .. code-block:: Nim
|
||||||
##
|
##
|
||||||
## import strtabs, sockets, scgi
|
## import strtabs, sockets, scgi
|
||||||
##
|
##
|
||||||
## var counter = 0
|
## var counter = 0
|
||||||
## proc handleRequest(client: TSocket, input: string,
|
## proc handleRequest(client: Socket, input: string,
|
||||||
## headers: PStringTable): bool {.procvar.} =
|
## headers: StringTableRef): bool {.procvar.} =
|
||||||
## inc(counter)
|
## inc(counter)
|
||||||
## client.writeStatusOkTextContent()
|
## client.writeStatusOkTextContent()
|
||||||
## client.send("Hello for the $#th time." % $counter & "\c\L")
|
## client.send("Hello for the $#th time." % $counter & "\c\L")
|
||||||
|
|
@ -31,11 +31,11 @@ include "system/inclrtl"
|
||||||
import sockets, strutils, os, strtabs, asyncio
|
import sockets, strutils, os, strtabs, asyncio
|
||||||
|
|
||||||
type
|
type
|
||||||
EScgi* = object of EIO ## the exception that is raised, if a SCGI error occurs
|
ScgiError* = object of IOError ## the exception that is raised, if a SCGI error occurs
|
||||||
|
|
||||||
proc scgiError*(msg: string) {.noreturn.} =
|
proc raiseScgiError*(msg: string) {.noreturn.} =
|
||||||
## raises an EScgi exception with message `msg`.
|
## raises an ScgiError exception with message `msg`.
|
||||||
var e: ref EScgi
|
var e: ref ScgiError
|
||||||
new(e)
|
new(e)
|
||||||
e.msg = msg
|
e.msg = msg
|
||||||
raise e
|
raise e
|
||||||
|
|
@ -45,7 +45,7 @@ proc parseWord(inp: string, outp: var string, start: int): int =
|
||||||
while inp[result] != '\0': inc(result)
|
while inp[result] != '\0': inc(result)
|
||||||
outp = substr(inp, start, result-1)
|
outp = substr(inp, start, result-1)
|
||||||
|
|
||||||
proc parseHeaders(s: string, L: int): PStringTable =
|
proc parseHeaders(s: string, L: int): StringTableRef =
|
||||||
result = newStringTable()
|
result = newStringTable()
|
||||||
var i = 0
|
var i = 0
|
||||||
while i < L:
|
while i < L:
|
||||||
|
|
@ -54,73 +54,77 @@ proc parseHeaders(s: string, L: int): PStringTable =
|
||||||
i = parseWord(s, val, i)+1
|
i = parseWord(s, val, i)+1
|
||||||
result[key] = val
|
result[key] = val
|
||||||
if s[i] == ',': inc(i)
|
if s[i] == ',': inc(i)
|
||||||
else: scgiError("',' after netstring expected")
|
else: raiseScgiError("',' after netstring expected")
|
||||||
|
|
||||||
proc recvChar(s: TSocket): char =
|
proc recvChar(s: Socket): char =
|
||||||
var c: char
|
var c: char
|
||||||
if recv(s, addr(c), sizeof(c)) == sizeof(c):
|
if recv(s, addr(c), sizeof(c)) == sizeof(c):
|
||||||
result = c
|
result = c
|
||||||
|
|
||||||
type
|
type
|
||||||
TScgiState* = object of TObject ## SCGI state object
|
ScgiState* = object of RootObj ## SCGI state object
|
||||||
server: TSocket
|
server: Socket
|
||||||
bufLen: int
|
bufLen: int
|
||||||
client*: TSocket ## the client socket to send data to
|
client*: Socket ## the client socket to send data to
|
||||||
headers*: PStringTable ## the parsed headers
|
headers*: StringTableRef ## the parsed headers
|
||||||
input*: string ## the input buffer
|
input*: string ## the input buffer
|
||||||
|
|
||||||
|
|
||||||
# Async
|
# Async
|
||||||
|
|
||||||
TClientMode = enum
|
ClientMode = enum
|
||||||
ClientReadChar, ClientReadHeaders, ClientReadContent
|
ClientReadChar, ClientReadHeaders, ClientReadContent
|
||||||
|
|
||||||
PAsyncClient = ref object
|
AsyncClient = ref object
|
||||||
c: PAsyncSocket
|
c: AsyncSocket
|
||||||
mode: TClientMode
|
mode: ClientMode
|
||||||
dataLen: int
|
dataLen: int
|
||||||
headers: PStringTable ## the parsed headers
|
headers: PStringTable ## the parsed headers
|
||||||
input: string ## the input buffer
|
input: string ## the input buffer
|
||||||
|
|
||||||
TAsyncScgiState = object
|
AsyncScgiStateObj = object
|
||||||
handleRequest: proc (client: PAsyncSocket,
|
handleRequest: proc (client: AsyncSocket,
|
||||||
input: string, headers: PStringTable) {.closure,gcsafe.}
|
input: string,
|
||||||
asyncServer: PAsyncSocket
|
headers: StringTableRef) {.closure, gcsafe.}
|
||||||
disp: PDispatcher
|
asyncServer: AsyncSocket
|
||||||
PAsyncScgiState* = ref TAsyncScgiState
|
disp: Dispatcher
|
||||||
|
AsyncScgiState* = ref AsyncScgiStateObj
|
||||||
|
|
||||||
proc recvBuffer(s: var TScgiState, L: int) =
|
{.deprected: [EScgi: ScgiError, TScgiState: ScgiState,
|
||||||
|
PAsyncScgiState: AsyncScgiState, scgiError: raiseScgiError].}
|
||||||
|
|
||||||
|
proc recvBuffer(s: var ScgiState, L: int) =
|
||||||
if L > s.bufLen:
|
if L > s.bufLen:
|
||||||
s.bufLen = L
|
s.bufLen = L
|
||||||
s.input = newString(L)
|
s.input = newString(L)
|
||||||
if L > 0 and recv(s.client, cstring(s.input), L) != L:
|
if L > 0 and recv(s.client, cstring(s.input), L) != L:
|
||||||
scgiError("could not read all data")
|
raiseScgiError("could not read all data")
|
||||||
setLen(s.input, L)
|
setLen(s.input, L)
|
||||||
|
|
||||||
proc open*(s: var TScgiState, port = TPort(4000), address = "127.0.0.1",
|
proc open*(s: var ScgiState, port = Port(4000), address = "127.0.0.1",
|
||||||
reuseAddr = False) =
|
reuseAddr = false) =
|
||||||
## opens a connection.
|
## opens a connection.
|
||||||
s.bufLen = 4000
|
s.bufLen = 4000
|
||||||
s.input = newString(s.buflen) # will be reused
|
s.input = newString(s.buflen) # will be reused
|
||||||
|
|
||||||
s.server = socket()
|
s.server = socket()
|
||||||
if s.server == InvalidSocket: osError(osLastError())
|
if s.server == InvalidSocket: raiseOSError(osLastError())
|
||||||
new(s.client) # Initialise s.client for `next`
|
new(s.client) # Initialise s.client for `next`
|
||||||
if s.server == InvalidSocket: scgiError("could not open socket")
|
if s.server == InvalidSocket: raiseScgiError("could not open socket")
|
||||||
#s.server.connect(connectionName, port)
|
#s.server.connect(connectionName, port)
|
||||||
if reuseAddr:
|
if reuseAddr:
|
||||||
s.server.setSockOpt(OptReuseAddr, True)
|
s.server.setSockOpt(OptReuseAddr, True)
|
||||||
bindAddr(s.server, port, address)
|
bindAddr(s.server, port, address)
|
||||||
listen(s.server)
|
listen(s.server)
|
||||||
|
|
||||||
proc close*(s: var TScgiState) =
|
proc close*(s: var ScgiState) =
|
||||||
## closes the connection.
|
## closes the connection.
|
||||||
s.server.close()
|
s.server.close()
|
||||||
|
|
||||||
proc next*(s: var TScgistate, timeout: int = -1): bool =
|
proc next*(s: var ScgiState, timeout: int = -1): bool =
|
||||||
## proceed to the first/next request. Waits ``timeout`` miliseconds for a
|
## proceed to the first/next request. Waits ``timeout`` miliseconds for a
|
||||||
## request, if ``timeout`` is `-1` then this function will never time out.
|
## request, if ``timeout`` is `-1` then this function will never time out.
|
||||||
## Returns `True` if a new request has been processed.
|
## Returns `true` if a new request has been processed.
|
||||||
var rsocks = @[s.server]
|
var rsocks = @[s.server]
|
||||||
if select(rsocks, timeout) == 1 and rsocks.len == 1:
|
if select(rsocks, timeout) == 1 and rsocks.len == 1:
|
||||||
new(s.client)
|
new(s.client)
|
||||||
|
|
@ -132,17 +136,17 @@ proc next*(s: var TScgistate, timeout: int = -1): bool =
|
||||||
s.client.close()
|
s.client.close()
|
||||||
return false
|
return false
|
||||||
if d notin strutils.digits:
|
if d notin strutils.digits:
|
||||||
if d != ':': scgiError("':' after length expected")
|
if d != ':': raiseScgiError("':' after length expected")
|
||||||
break
|
break
|
||||||
L = L * 10 + ord(d) - ord('0')
|
L = L * 10 + ord(d) - ord('0')
|
||||||
recvBuffer(s, L+1)
|
recvBuffer(s, L+1)
|
||||||
s.headers = parseHeaders(s.input, L)
|
s.headers = parseHeaders(s.input, L)
|
||||||
if s.headers["SCGI"] != "1": scgiError("SCGI Version 1 expected")
|
if s.headers["SCGI"] != "1": raiseScgiError("SCGI Version 1 expected")
|
||||||
L = parseInt(s.headers["CONTENT_LENGTH"])
|
L = parseInt(s.headers["CONTENT_LENGTH"])
|
||||||
recvBuffer(s, L)
|
recvBuffer(s, L)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
proc writeStatusOkTextContent*(c: TSocket, contentType = "text/html") =
|
proc writeStatusOkTextContent*(c: Socket, contentType = "text/html") =
|
||||||
## sends the following string to the socket `c`::
|
## sends the following string to the socket `c`::
|
||||||
##
|
##
|
||||||
## Status: 200 OK\r\LContent-Type: text/html\r\L\r\L
|
## Status: 200 OK\r\LContent-Type: text/html\r\L\r\L
|
||||||
|
|
@ -151,11 +155,11 @@ proc writeStatusOkTextContent*(c: TSocket, contentType = "text/html") =
|
||||||
c.send("Status: 200 OK\r\L" &
|
c.send("Status: 200 OK\r\L" &
|
||||||
"Content-Type: $1\r\L\r\L" % contentType)
|
"Content-Type: $1\r\L\r\L" % contentType)
|
||||||
|
|
||||||
proc run*(handleRequest: proc (client: TSocket, input: string,
|
proc run*(handleRequest: proc (client: Socket, input: string,
|
||||||
headers: PStringTable): bool {.nimcall,gcsafe.},
|
headers: StringTableRef): bool {.nimcall,gcsafe.},
|
||||||
port = TPort(4000)) =
|
port = Port(4000)) =
|
||||||
## encapsulates the SCGI object and main loop.
|
## encapsulates the SCGI object and main loop.
|
||||||
var s: TScgiState
|
var s: ScgiState
|
||||||
s.open(port)
|
s.open(port)
|
||||||
var stop = false
|
var stop = false
|
||||||
while not stop:
|
while not stop:
|
||||||
|
|
@ -166,11 +170,11 @@ proc run*(handleRequest: proc (client: TSocket, input: string,
|
||||||
|
|
||||||
# -- AsyncIO start
|
# -- AsyncIO start
|
||||||
|
|
||||||
proc recvBufferAsync(client: PAsyncClient, L: int): TReadLineResult =
|
proc recvBufferAsync(client: AsyncClient, L: int): ReadLineResult =
|
||||||
result = ReadPartialLine
|
result = ReadPartialLine
|
||||||
var data = ""
|
var data = ""
|
||||||
if L < 1:
|
if L < 1:
|
||||||
scgiError("Cannot read negative or zero length: " & $L)
|
raiseScgiError("Cannot read negative or zero length: " & $L)
|
||||||
let ret = recvAsync(client.c, data, L)
|
let ret = recvAsync(client.c, data, L)
|
||||||
if ret == 0 and data == "":
|
if ret == 0 and data == "":
|
||||||
client.c.close()
|
client.c.close()
|
||||||
|
|
@ -181,16 +185,16 @@ proc recvBufferAsync(client: PAsyncClient, L: int): TReadLineResult =
|
||||||
if ret == L:
|
if ret == L:
|
||||||
return ReadFullLine
|
return ReadFullLine
|
||||||
|
|
||||||
proc checkCloseSocket(client: PAsyncClient) =
|
proc checkCloseSocket(client: AsyncClient) =
|
||||||
if not client.c.isClosed:
|
if not client.c.isClosed:
|
||||||
if client.c.isSendDataBuffered:
|
if client.c.isSendDataBuffered:
|
||||||
client.c.setHandleWrite do (s: PAsyncSocket):
|
client.c.setHandleWrite do (s: AsyncSocket):
|
||||||
if not s.isClosed and not s.isSendDataBuffered:
|
if not s.isClosed and not s.isSendDataBuffered:
|
||||||
s.close()
|
s.close()
|
||||||
s.delHandleWrite()
|
s.delHandleWrite()
|
||||||
else: client.c.close()
|
else: client.c.close()
|
||||||
|
|
||||||
proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) =
|
proc handleClientRead(client: AsyncClient, s: AsyncScgiState) =
|
||||||
case client.mode
|
case client.mode
|
||||||
of ClientReadChar:
|
of ClientReadChar:
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -203,7 +207,7 @@ proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) =
|
||||||
if ret == -1:
|
if ret == -1:
|
||||||
return # No more data available
|
return # No more data available
|
||||||
if d[0] notin strutils.digits:
|
if d[0] notin strutils.digits:
|
||||||
if d[0] != ':': scgiError("':' after length expected")
|
if d[0] != ':': raiseScgiError("':' after length expected")
|
||||||
break
|
break
|
||||||
client.dataLen = client.dataLen * 10 + ord(d[0]) - ord('0')
|
client.dataLen = client.dataLen * 10 + ord(d[0]) - ord('0')
|
||||||
client.mode = ClientReadHeaders
|
client.mode = ClientReadHeaders
|
||||||
|
|
@ -213,7 +217,7 @@ proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) =
|
||||||
case ret
|
case ret
|
||||||
of ReadFullLine:
|
of ReadFullLine:
|
||||||
client.headers = parseHeaders(client.input, client.input.len-1)
|
client.headers = parseHeaders(client.input, client.input.len-1)
|
||||||
if client.headers["SCGI"] != "1": scgiError("SCGI Version 1 expected")
|
if client.headers["SCGI"] != "1": raiseScgiError("SCGI Version 1 expected")
|
||||||
client.input = "" # For next part
|
client.input = "" # For next part
|
||||||
|
|
||||||
let contentLen = parseInt(client.headers["CONTENT_LENGTH"])
|
let contentLen = parseInt(client.headers["CONTENT_LENGTH"])
|
||||||
|
|
@ -236,50 +240,50 @@ proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) =
|
||||||
s.handleRequest(client.c, client.input, client.headers)
|
s.handleRequest(client.c, client.input, client.headers)
|
||||||
checkCloseSocket(client)
|
checkCloseSocket(client)
|
||||||
|
|
||||||
proc handleAccept(sock: PAsyncSocket, s: PAsyncScgiState) =
|
proc handleAccept(sock: AsyncSocket, s: AsyncScgiState) =
|
||||||
var client: PAsyncSocket
|
var client: AsyncSocket
|
||||||
new(client)
|
new(client)
|
||||||
accept(s.asyncServer, client)
|
accept(s.asyncServer, client)
|
||||||
var asyncClient = PAsyncClient(c: client, mode: ClientReadChar, dataLen: 0,
|
var asyncClient = AsyncClient(c: client, mode: ClientReadChar, dataLen: 0,
|
||||||
headers: newStringTable(), input: "")
|
headers: newStringTable(), input: "")
|
||||||
client.handleRead =
|
client.handleRead =
|
||||||
proc (sock: PAsyncSocket) =
|
proc (sock: AsyncSocket) =
|
||||||
handleClientRead(asyncClient, s)
|
handleClientRead(asyncClient, s)
|
||||||
s.disp.register(client)
|
s.disp.register(client)
|
||||||
|
|
||||||
proc open*(handleRequest: proc (client: PAsyncSocket,
|
proc open*(handleRequest: proc (client: AsyncSocket,
|
||||||
input: string, headers: PStringTable) {.
|
input: string, headers: StringTableRef) {.
|
||||||
closure, gcsafe.},
|
closure, gcsafe.},
|
||||||
port = TPort(4000), address = "127.0.0.1",
|
port = Port(4000), address = "127.0.0.1",
|
||||||
reuseAddr = false): PAsyncScgiState =
|
reuseAddr = false): AsyncScgiState =
|
||||||
## Creates an ``PAsyncScgiState`` object which serves as a SCGI server.
|
## Creates an ``AsyncScgiState`` object which serves as a SCGI server.
|
||||||
##
|
##
|
||||||
## After the execution of ``handleRequest`` the client socket will be closed
|
## After the execution of ``handleRequest`` the client socket will be closed
|
||||||
## automatically unless it has already been closed.
|
## automatically unless it has already been closed.
|
||||||
var cres: PAsyncScgiState
|
var cres: AsyncScgiState
|
||||||
new(cres)
|
new(cres)
|
||||||
cres.asyncServer = AsyncSocket()
|
cres.asyncServer = AsyncSocket()
|
||||||
cres.asyncServer.handleAccept = proc (s: PAsyncSocket) = handleAccept(s, cres)
|
cres.asyncServer.handleAccept = proc (s: AsyncSocket) = handleAccept(s, cres)
|
||||||
if reuseAddr:
|
if reuseAddr:
|
||||||
cres.asyncServer.setSockOpt(OptReuseAddr, True)
|
cres.asyncServer.setSockOpt(OptReuseAddr, true)
|
||||||
bindAddr(cres.asyncServer, port, address)
|
bindAddr(cres.asyncServer, port, address)
|
||||||
listen(cres.asyncServer)
|
listen(cres.asyncServer)
|
||||||
cres.handleRequest = handleRequest
|
cres.handleRequest = handleRequest
|
||||||
result = cres
|
result = cres
|
||||||
|
|
||||||
proc register*(d: PDispatcher, s: PAsyncScgiState): PDelegate {.discardable.} =
|
proc register*(d: Dispatcher, s: AsyncScgiState): Delegate {.discardable.} =
|
||||||
## Registers ``s`` with dispatcher ``d``.
|
## Registers ``s`` with dispatcher ``d``.
|
||||||
result = d.register(s.asyncServer)
|
result = d.register(s.asyncServer)
|
||||||
s.disp = d
|
s.disp = d
|
||||||
|
|
||||||
proc close*(s: PAsyncScgiState) =
|
proc close*(s: AsyncScgiState) =
|
||||||
## Closes the ``PAsyncScgiState``.
|
## Closes the ``AsyncScgiState``.
|
||||||
s.asyncServer.close()
|
s.asyncServer.close()
|
||||||
|
|
||||||
when false:
|
when false:
|
||||||
var counter = 0
|
var counter = 0
|
||||||
proc handleRequest(client: TSocket, input: string,
|
proc handleRequest(client: Socket, input: string,
|
||||||
headers: PStringTable): bool {.procvar.} =
|
headers: StringTableRef): bool {.procvar.} =
|
||||||
inc(counter)
|
inc(counter)
|
||||||
client.writeStatusOkTextContent()
|
client.writeStatusOkTextContent()
|
||||||
client.send("Hello for the $#th time." % $counter & "\c\L")
|
client.send("Hello for the $#th time." % $counter & "\c\L")
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2014 Dominik Picheta
|
# (c) Copyright 2014 Dominik Picheta
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -18,57 +18,57 @@ elif defined(windows):
|
||||||
else:
|
else:
|
||||||
import posix
|
import posix
|
||||||
|
|
||||||
proc hash*(x: TSocketHandle): THash {.borrow.}
|
proc hash*(x: SocketHandle): THash {.borrow.}
|
||||||
proc `$`*(x: TSocketHandle): string {.borrow.}
|
proc `$`*(x: SocketHandle): string {.borrow.}
|
||||||
|
|
||||||
type
|
type
|
||||||
TEvent* = enum
|
Event* = enum
|
||||||
EvRead, EvWrite
|
EvRead, EvWrite
|
||||||
|
|
||||||
PSelectorKey* = ref object
|
SelectorKey* = ref object
|
||||||
fd*: TSocketHandle
|
fd*: SocketHandle
|
||||||
events*: set[TEvent] ## The events which ``fd`` listens for.
|
events*: set[Event] ## The events which ``fd`` listens for.
|
||||||
data*: PObject ## User object.
|
data*: RootRef ## User object.
|
||||||
|
|
||||||
TReadyInfo* = tuple[key: PSelectorKey, events: set[TEvent]]
|
ReadyInfo* = tuple[key: SelectorKey, events: set[Event]]
|
||||||
|
|
||||||
when defined(nimdoc):
|
when defined(nimdoc):
|
||||||
type
|
type
|
||||||
PSelector* = ref object
|
Selector* = ref object
|
||||||
## An object which holds file descripters to be checked for read/write
|
## An object which holds file descripters to be checked for read/write
|
||||||
## status.
|
## status.
|
||||||
fds: TTable[TSocketHandle, PSelectorKey]
|
fds: Table[SocketHandle, SelectorKey]
|
||||||
|
|
||||||
proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent],
|
proc register*(s: Selector, fd: SocketHandle, events: set[Event],
|
||||||
data: PObject): PSelectorKey {.discardable.} =
|
data: RootRef): SelectorKey {.discardable.} =
|
||||||
## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent
|
## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent
|
||||||
## ``events``.
|
## ``events``.
|
||||||
|
|
||||||
proc update*(s: PSelector, fd: TSocketHandle,
|
proc update*(s: Selector, fd: SocketHandle,
|
||||||
events: set[TEvent]): PSelectorKey {.discardable.} =
|
events: set[Event]): SelectorKey {.discardable.} =
|
||||||
## Updates the events which ``fd`` wants notifications for.
|
## Updates the events which ``fd`` wants notifications for.
|
||||||
|
|
||||||
proc select*(s: PSelector, timeout: int): seq[TReadyInfo] =
|
proc select*(s: Selector, timeout: int): seq[ReadyInfo] =
|
||||||
## The ``events`` field of the returned ``key`` contains the original events
|
## The ``events`` field of the returned ``key`` contains the original events
|
||||||
## for which the ``fd`` was bound. This is contrary to the ``events`` field
|
## for which the ``fd`` was bound. This is contrary to the ``events`` field
|
||||||
## of the ``TReadyInfo`` tuple which determines which events are ready
|
## of the ``TReadyInfo`` tuple which determines which events are ready
|
||||||
## on the ``fd``.
|
## on the ``fd``.
|
||||||
|
|
||||||
proc contains*(s: PSelector, fd: TSocketHandle): bool =
|
proc contains*(s: Selector, fd: SocketHandle): bool =
|
||||||
## Determines whether selector contains a file descriptor.
|
## Determines whether selector contains a file descriptor.
|
||||||
|
|
||||||
proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey =
|
proc `[]`*(s: Selector, fd: SocketHandle): SelectorKey =
|
||||||
## Retrieves the selector key for ``fd``.
|
## Retrieves the selector key for ``fd``.
|
||||||
|
|
||||||
|
|
||||||
elif defined(linux):
|
elif defined(linux):
|
||||||
type
|
type
|
||||||
PSelector* = ref object
|
Selector* = ref object
|
||||||
epollFD: cint
|
epollFD: cint
|
||||||
events: array[64, epoll_event]
|
events: array[64, epoll_event]
|
||||||
fds: TTable[TSocketHandle, PSelectorKey]
|
fds: Table[SocketHandle, SelectorKey]
|
||||||
|
|
||||||
proc createEventStruct(events: set[TEvent], fd: TSocketHandle): epoll_event =
|
proc createEventStruct(events: set[Event], fd: SocketHandle): epoll_event =
|
||||||
if EvRead in events:
|
if EvRead in events:
|
||||||
result.events = EPOLLIN
|
result.events = EPOLLIN
|
||||||
if EvWrite in events:
|
if EvWrite in events:
|
||||||
|
|
@ -76,22 +76,22 @@ elif defined(linux):
|
||||||
result.events = result.events or EPOLLRDHUP
|
result.events = result.events or EPOLLRDHUP
|
||||||
result.data.fd = fd.cint
|
result.data.fd = fd.cint
|
||||||
|
|
||||||
proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent],
|
proc register*(s: Selector, fd: SocketHandle, events: set[Event],
|
||||||
data: PObject): PSelectorKey {.discardable.} =
|
data: RootRef): SelectorKey {.discardable.} =
|
||||||
## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent
|
## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent
|
||||||
## ``events``.
|
## ``events``.
|
||||||
var event = createEventStruct(events, fd)
|
var event = createEventStruct(events, fd)
|
||||||
if events != {}:
|
if events != {}:
|
||||||
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
|
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
|
||||||
OSError(OSLastError())
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
var key = PSelectorKey(fd: fd, events: events, data: data)
|
var key = SelectorKey(fd: fd, events: events, data: data)
|
||||||
|
|
||||||
s.fds[fd] = key
|
s.fds[fd] = key
|
||||||
result = key
|
result = key
|
||||||
|
|
||||||
proc update*(s: PSelector, fd: TSocketHandle,
|
proc update*(s: Selector, fd: SocketHandle,
|
||||||
events: set[TEvent]): PSelectorKey {.discardable.} =
|
events: set[Event]): SelectorKey {.discardable.} =
|
||||||
## Updates the events which ``fd`` wants notifications for.
|
## Updates the events which ``fd`` wants notifications for.
|
||||||
if s.fds[fd].events != events:
|
if s.fds[fd].events != events:
|
||||||
if events == {}:
|
if events == {}:
|
||||||
|
|
@ -101,7 +101,7 @@ elif defined(linux):
|
||||||
# because its got fds which are waiting for no events and
|
# because its got fds which are waiting for no events and
|
||||||
# are therefore constantly ready. (leading to 100% CPU usage).
|
# are therefore constantly ready. (leading to 100% CPU usage).
|
||||||
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
|
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
|
||||||
OSError(OSLastError())
|
raiseOSError(osLastError())
|
||||||
s.fds[fd].events = events
|
s.fds[fd].events = events
|
||||||
else:
|
else:
|
||||||
var event = createEventStruct(events, fd)
|
var event = createEventStruct(events, fd)
|
||||||
|
|
@ -109,36 +109,36 @@ elif defined(linux):
|
||||||
# This fd is idle. It's not a member of this epoll instance and must
|
# This fd is idle. It's not a member of this epoll instance and must
|
||||||
# be re-registered.
|
# be re-registered.
|
||||||
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
|
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
|
||||||
OSError(OSLastError())
|
raiseOSError(osLastError())
|
||||||
else:
|
else:
|
||||||
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
|
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
|
||||||
OSError(OSLastError())
|
raiseOSError(osLastError())
|
||||||
s.fds[fd].events = events
|
s.fds[fd].events = events
|
||||||
|
|
||||||
result = s.fds[fd]
|
result = s.fds[fd]
|
||||||
|
|
||||||
proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} =
|
proc unregister*(s: Selector, fd: SocketHandle): SelectorKey {.discardable.} =
|
||||||
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
|
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
|
||||||
let err = OSLastError()
|
let err = osLastError()
|
||||||
if err.cint notin {ENOENT, EBADF}: # TODO: Why do we sometimes get an EBADF? Is this normal?
|
if err.cint notin {ENOENT, EBADF}: # TODO: Why do we sometimes get an EBADF? Is this normal?
|
||||||
OSError(err)
|
raiseOSError(err)
|
||||||
result = s.fds[fd]
|
result = s.fds[fd]
|
||||||
s.fds.del(fd)
|
s.fds.del(fd)
|
||||||
|
|
||||||
proc close*(s: PSelector) =
|
proc close*(s: Selector) =
|
||||||
if s.epollFD.close() != 0: OSError(OSLastError())
|
if s.epollFD.close() != 0: raiseOSError(osLastError())
|
||||||
dealloc(addr s.events) # TODO: Test this
|
dealloc(addr s.events) # TODO: Test this
|
||||||
|
|
||||||
proc epollHasFd(s: PSelector, fd: TSocketHandle): bool =
|
proc epollHasFd(s: Selector, fd: SocketHandle): bool =
|
||||||
result = true
|
result = true
|
||||||
var event = createEventStruct(s.fds[fd].events, fd)
|
var event = createEventStruct(s.fds[fd].events, fd)
|
||||||
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
|
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
|
||||||
let err = osLastError()
|
let err = osLastError()
|
||||||
if err.cint in {ENOENT, EBADF}:
|
if err.cint in {ENOENT, EBADF}:
|
||||||
return false
|
return false
|
||||||
OSError(OSLastError())
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
proc select*(s: PSelector, timeout: int): seq[TReadyInfo] =
|
proc select*(s: Selector, timeout: int): seq[ReadyInfo] =
|
||||||
##
|
##
|
||||||
## The ``events`` field of the returned ``key`` contains the original events
|
## The ``events`` field of the returned ``key`` contains the original events
|
||||||
## for which the ``fd`` was bound. This is contrary to the ``events`` field
|
## for which the ``fd`` was bound. This is contrary to the ``events`` field
|
||||||
|
|
@ -146,12 +146,12 @@ elif defined(linux):
|
||||||
## on the ``fd``.
|
## on the ``fd``.
|
||||||
result = @[]
|
result = @[]
|
||||||
let evNum = epoll_wait(s.epollFD, addr s.events[0], 64.cint, timeout.cint)
|
let evNum = epoll_wait(s.epollFD, addr s.events[0], 64.cint, timeout.cint)
|
||||||
if evNum < 0: OSError(OSLastError())
|
if evNum < 0: raiseOSError(osLastError())
|
||||||
if evNum == 0: return @[]
|
if evNum == 0: return @[]
|
||||||
for i in 0 .. <evNum:
|
for i in 0 .. <evNum:
|
||||||
let fd = s.events[i].data.fd.TSocketHandle
|
let fd = s.events[i].data.fd.SocketHandle
|
||||||
|
|
||||||
var evSet: set[TEvent] = {}
|
var evSet: set[Event] = {}
|
||||||
if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
|
if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
|
||||||
if (s.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
|
if (s.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
|
||||||
let selectorKey = s.fds[fd]
|
let selectorKey = s.fds[fd]
|
||||||
|
|
@ -160,15 +160,15 @@ elif defined(linux):
|
||||||
|
|
||||||
#echo("Epoll: ", result[i].key.fd, " ", result[i].events, " ", result[i].key.events)
|
#echo("Epoll: ", result[i].key.fd, " ", result[i].events, " ", result[i].key.events)
|
||||||
|
|
||||||
proc newSelector*(): PSelector =
|
proc newSelector*(): Selector =
|
||||||
new result
|
new result
|
||||||
result.epollFD = epoll_create(64)
|
result.epollFD = epoll_create(64)
|
||||||
#result.events = cast[array[64, epoll_event]](alloc0(sizeof(epoll_event)*64))
|
#result.events = cast[array[64, epoll_event]](alloc0(sizeof(epoll_event)*64))
|
||||||
result.fds = initTable[TSocketHandle, PSelectorKey]()
|
result.fds = initTable[SocketHandle, SelectorKey]()
|
||||||
if result.epollFD < 0:
|
if result.epollFD < 0:
|
||||||
OSError(OSLastError())
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
proc contains*(s: PSelector, fd: TSocketHandle): bool =
|
proc contains*(s: Selector, fd: SocketHandle): bool =
|
||||||
## Determines whether selector contains a file descriptor.
|
## Determines whether selector contains a file descriptor.
|
||||||
if s.fds.hasKey(fd):
|
if s.fds.hasKey(fd):
|
||||||
# Ensure the underlying epoll instance still contains this fd.
|
# Ensure the underlying epoll instance still contains this fd.
|
||||||
|
|
@ -179,46 +179,46 @@ elif defined(linux):
|
||||||
else:
|
else:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey =
|
proc `[]`*(s: Selector, fd: SocketHandle): SelectorKey =
|
||||||
## Retrieves the selector key for ``fd``.
|
## Retrieves the selector key for ``fd``.
|
||||||
return s.fds[fd]
|
return s.fds[fd]
|
||||||
|
|
||||||
elif not defined(nimdoc):
|
elif not defined(nimdoc):
|
||||||
# TODO: kqueue for bsd/mac os x.
|
# TODO: kqueue for bsd/mac os x.
|
||||||
type
|
type
|
||||||
PSelector* = ref object
|
Selector* = ref object
|
||||||
fds: TTable[TSocketHandle, PSelectorKey]
|
fds: Table[SocketHandle, SelectorKey]
|
||||||
|
|
||||||
proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent],
|
proc register*(s: Selector, fd: SocketHandle, events: set[Event],
|
||||||
data: PObject): PSelectorKey {.discardable.} =
|
data: RootRef): SelectorKey {.discardable.} =
|
||||||
if s.fds.hasKey(fd):
|
if s.fds.hasKey(fd):
|
||||||
raise newException(EInvalidValue, "File descriptor already exists.")
|
raise newException(ValueError, "File descriptor already exists.")
|
||||||
var sk = PSelectorKey(fd: fd, events: events, data: data)
|
var sk = SelectorKey(fd: fd, events: events, data: data)
|
||||||
s.fds[fd] = sk
|
s.fds[fd] = sk
|
||||||
result = sk
|
result = sk
|
||||||
|
|
||||||
proc update*(s: PSelector, fd: TSocketHandle,
|
proc update*(s: Selector, fd: SocketHandle,
|
||||||
events: set[TEvent]): PSelectorKey {.discardable.} =
|
events: set[Event]): SelectorKey {.discardable.} =
|
||||||
## Updates the events which ``fd`` wants notifications for.
|
## Updates the events which ``fd`` wants notifications for.
|
||||||
if not s.fds.hasKey(fd):
|
if not s.fds.hasKey(fd):
|
||||||
raise newException(EInvalidValue, "File descriptor not found.")
|
raise newException(ValueError, "File descriptor not found.")
|
||||||
|
|
||||||
s.fds[fd].events = events
|
s.fds[fd].events = events
|
||||||
result = s.fds[fd]
|
result = s.fds[fd]
|
||||||
|
|
||||||
proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} =
|
proc unregister*(s: Selector, fd: SocketHandle): SelectorKey {.discardable.} =
|
||||||
result = s.fds[fd]
|
result = s.fds[fd]
|
||||||
s.fds.del(fd)
|
s.fds.del(fd)
|
||||||
|
|
||||||
proc close*(s: PSelector) = nil
|
proc close*(s: Selector) = discard
|
||||||
|
|
||||||
proc timeValFromMilliseconds(timeout: int): TTimeVal =
|
proc timeValFromMilliseconds(timeout: int): TimeVal =
|
||||||
if timeout != -1:
|
if timeout != -1:
|
||||||
var seconds = timeout div 1000
|
var seconds = timeout div 1000
|
||||||
result.tv_sec = seconds.int32
|
result.tv_sec = seconds.int32
|
||||||
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
|
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
|
||||||
|
|
||||||
proc createFdSet(rd, wr: var TFdSet, fds: TTable[TSocketHandle, PSelectorKey],
|
proc createFdSet(rd, wr: var FdSet, fds: Table[SocketHandle, SelectorKey],
|
||||||
m: var int) =
|
m: var int) =
|
||||||
FD_ZERO(rd); FD_ZERO(wr)
|
FD_ZERO(rd); FD_ZERO(wr)
|
||||||
for k, v in pairs(fds):
|
for k, v in pairs(fds):
|
||||||
|
|
@ -229,22 +229,22 @@ elif not defined(nimdoc):
|
||||||
m = max(m, int(k))
|
m = max(m, int(k))
|
||||||
FD_SET(k, wr)
|
FD_SET(k, wr)
|
||||||
|
|
||||||
proc getReadyFDs(rd, wr: var TFdSet, fds: TTable[TSocketHandle, PSelectorKey]):
|
proc getReadyFDs(rd, wr: var FdSet, fds: Table[SocketHandle, SelectorKey]):
|
||||||
seq[TReadyInfo] =
|
seq[ReadyInfo] =
|
||||||
result = @[]
|
result = @[]
|
||||||
for k, v in pairs(fds):
|
for k, v in pairs(fds):
|
||||||
var events: set[TEvent] = {}
|
var events: set[Event] = {}
|
||||||
if FD_ISSET(k, rd) != 0'i32:
|
if FD_ISSET(k, rd) != 0'i32:
|
||||||
events = events + {EvRead}
|
events = events + {EvRead}
|
||||||
if FD_ISSET(k, wr) != 0'i32:
|
if FD_ISSET(k, wr) != 0'i32:
|
||||||
events = events + {EvWrite}
|
events = events + {EvWrite}
|
||||||
result.add((v, events))
|
result.add((v, events))
|
||||||
|
|
||||||
proc select(fds: TTable[TSocketHandle, PSelectorKey], timeout = 500):
|
proc select(fds: Table[SocketHandle, SelectorKey], timeout = 500):
|
||||||
seq[TReadyInfo] =
|
seq[ReadyInfo] =
|
||||||
var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout)
|
var tv {.noInit.}: TimeVal = timeValFromMilliseconds(timeout)
|
||||||
|
|
||||||
var rd, wr: TFdSet
|
var rd, wr: FdSet
|
||||||
var m = 0
|
var m = 0
|
||||||
createFdSet(rd, wr, fds, m)
|
createFdSet(rd, wr, fds, m)
|
||||||
|
|
||||||
|
|
@ -255,26 +255,26 @@ elif not defined(nimdoc):
|
||||||
retCode = int(select(cint(m+1), addr(rd), addr(wr), nil, nil))
|
retCode = int(select(cint(m+1), addr(rd), addr(wr), nil, nil))
|
||||||
|
|
||||||
if retCode < 0:
|
if retCode < 0:
|
||||||
OSError(OSLastError())
|
raiseOSError(osLastError())
|
||||||
elif retCode == 0:
|
elif retCode == 0:
|
||||||
return @[]
|
return @[]
|
||||||
else:
|
else:
|
||||||
return getReadyFDs(rd, wr, fds)
|
return getReadyFDs(rd, wr, fds)
|
||||||
|
|
||||||
proc select*(s: PSelector, timeout: int): seq[TReadyInfo] =
|
proc select*(s: Selector, timeout: int): seq[ReadyInfo] =
|
||||||
result = select(s.fds, timeout)
|
result = select(s.fds, timeout)
|
||||||
|
|
||||||
proc newSelector*(): PSelector =
|
proc newSelector*(): Selector =
|
||||||
new result
|
new result
|
||||||
result.fds = initTable[TSocketHandle, PSelectorKey]()
|
result.fds = initTable[SocketHandle, SelectorKey]()
|
||||||
|
|
||||||
proc contains*(s: PSelector, fd: TSocketHandle): bool =
|
proc contains*(s: Selector, fd: SocketHandle): bool =
|
||||||
return s.fds.hasKey(fd)
|
return s.fds.hasKey(fd)
|
||||||
|
|
||||||
proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey =
|
proc `[]`*(s: Selector, fd: SocketHandle): SelectorKey =
|
||||||
return s.fds[fd]
|
return s.fds[fd]
|
||||||
|
|
||||||
proc contains*(s: PSelector, key: PSelectorKey): bool =
|
proc contains*(s: Selector, key: SelectorKey): bool =
|
||||||
## Determines whether selector contains this selector key. More accurate
|
## Determines whether selector contains this selector key. More accurate
|
||||||
## than checking if the file descriptor is in the selector because it
|
## than checking if the file descriptor is in the selector because it
|
||||||
## ensures that the keys are equal. File descriptors may not always be
|
## ensures that the keys are equal. File descriptors may not always be
|
||||||
|
|
@ -282,20 +282,24 @@ proc contains*(s: PSelector, key: PSelectorKey): bool =
|
||||||
## the new one may have the same value.
|
## the new one may have the same value.
|
||||||
return key.fd in s and s.fds[key.fd] == key
|
return key.fd in s and s.fds[key.fd] == key
|
||||||
|
|
||||||
|
{.deprecated: [TEvent: Event, PSelectorKey: SelectorKey,
|
||||||
|
TReadyInfo: ReadyInfo, PSelector: Selector].}
|
||||||
|
|
||||||
|
|
||||||
when isMainModule and not defined(nimdoc):
|
when isMainModule and not defined(nimdoc):
|
||||||
# Select()
|
# Select()
|
||||||
import sockets
|
import sockets
|
||||||
type
|
type
|
||||||
PSockWrapper = ref object of PObject
|
SockWrapper = ref object of RootObj
|
||||||
sock: TSocket
|
sock: Socket
|
||||||
|
|
||||||
var sock = socket()
|
var sock = socket()
|
||||||
if sock == sockets.InvalidSocket: osError(osLastError())
|
if sock == sockets.InvalidSocket: raiseOSError(osLastError())
|
||||||
#sock.setBlocking(false)
|
#sock.setBlocking(false)
|
||||||
sock.connect("irc.freenode.net", TPort(6667))
|
sock.connect("irc.freenode.net", Port(6667))
|
||||||
|
|
||||||
var selector = newSelector()
|
var selector = newSelector()
|
||||||
var data = PSockWrapper(sock: sock)
|
var data = SockWrapper(sock: sock)
|
||||||
let key = selector.register(sock.getFD, {EvWrite}, data)
|
let key = selector.register(sock.getFD, {EvWrite}, data)
|
||||||
var i = 0
|
var i = 0
|
||||||
while true:
|
while true:
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Dominik Picheta
|
# (c) Copyright 2012 Dominik Picheta
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -16,11 +16,11 @@
|
||||||
## Example gmail use:
|
## Example gmail use:
|
||||||
##
|
##
|
||||||
##
|
##
|
||||||
## .. code-block:: Nimrod
|
## .. code-block:: Nim
|
||||||
## var msg = createMessage("Hello from Nimrod's SMTP",
|
## var msg = createMessage("Hello from Nim's SMTP",
|
||||||
## "Hello!.\n Is this awesome or what?",
|
## "Hello!.\n Is this awesome or what?",
|
||||||
## @["foo@gmail.com"])
|
## @["foo@gmail.com"])
|
||||||
## var smtp = connect("smtp.gmail.com", 465, True, True)
|
## var smtp = connect("smtp.gmail.com", 465, true, true)
|
||||||
## smtp.auth("username", "password")
|
## smtp.auth("username", "password")
|
||||||
## smtp.sendmail("username@gmail.com", @["foo@gmail.com"], $msg)
|
## smtp.sendmail("username@gmail.com", @["foo@gmail.com"], $msg)
|
||||||
##
|
##
|
||||||
|
|
@ -31,18 +31,20 @@
|
||||||
import sockets, strutils, strtabs, base64, os
|
import sockets, strutils, strtabs, base64, os
|
||||||
|
|
||||||
type
|
type
|
||||||
TSMTP* {.final.} = object
|
Smtp* = object
|
||||||
sock: TSocket
|
sock: Socket
|
||||||
debug: Bool
|
debug: bool
|
||||||
|
|
||||||
TMessage* {.final.} = object
|
Message* = object
|
||||||
msgTo: seq[string]
|
msgTo: seq[string]
|
||||||
msgCc: seq[string]
|
msgCc: seq[string]
|
||||||
msgSubject: string
|
msgSubject: string
|
||||||
msgOtherHeaders: PStringTable
|
msgOtherHeaders: StringTableRef
|
||||||
msgBody: string
|
msgBody: string
|
||||||
|
|
||||||
EInvalidReply* = object of EIO
|
ReplyError* = object of IOError
|
||||||
|
|
||||||
|
{.deprecated: [EInvalidReply: ReplyError, TMessage: Message, TSMTP: Smtp].}
|
||||||
|
|
||||||
proc debugSend(smtp: TSMTP, cmd: string) =
|
proc debugSend(smtp: TSMTP, cmd: string) =
|
||||||
if smtp.debug:
|
if smtp.debug:
|
||||||
|
|
@ -170,7 +172,7 @@ when isMainModule:
|
||||||
#smtp.sendmail("root@localhost", @["dominik@localhost"], $msg)
|
#smtp.sendmail("root@localhost", @["dominik@localhost"], $msg)
|
||||||
|
|
||||||
#echo(decode("a17sm3701420wbe.12"))
|
#echo(decode("a17sm3701420wbe.12"))
|
||||||
var msg = createMessage("Hello from Nimrod's SMTP!",
|
var msg = createMessage("Hello from Nim's SMTP!",
|
||||||
"Hello!!!!.\n Is this awesome or what?",
|
"Hello!!!!.\n Is this awesome or what?",
|
||||||
@["someone@yahoo.com", "someone@gmail.com"])
|
@["someone@yahoo.com", "someone@gmail.com"])
|
||||||
echo(msg)
|
echo(msg)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2013 Andreas Rumpf, Dominik Picheta
|
# (c) Copyright 2013 Andreas Rumpf, Dominik Picheta
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -14,8 +14,8 @@
|
||||||
## the ``socket`` function to `false`. Be aware that some functions may not yet
|
## the ``socket`` function to `false`. Be aware that some functions may not yet
|
||||||
## support buffered sockets (mainly the recvFrom function).
|
## support buffered sockets (mainly the recvFrom function).
|
||||||
##
|
##
|
||||||
## Most procedures raise EOS on error, but some may return ``-1`` or a boolean
|
## Most procedures raise OSError on error, but some may return ``-1`` or a
|
||||||
## ``false``.
|
## boolean ``false``.
|
||||||
##
|
##
|
||||||
## SSL is supported through the OpenSSL library. This support can be activated
|
## SSL is supported through the OpenSSL library. This support can be activated
|
||||||
## by compiling with the ``-d:ssl`` switch. When an SSL socket is used it will
|
## by compiling with the ``-d:ssl`` switch. When an SSL socket is used it will
|
||||||
|
|
@ -47,25 +47,29 @@ else:
|
||||||
|
|
||||||
when defined(ssl):
|
when defined(ssl):
|
||||||
type
|
type
|
||||||
ESSL* = object of ESynch
|
SSLError* = object of Exception
|
||||||
|
|
||||||
TSSLCVerifyMode* = enum
|
SSLCVerifyMode* = enum
|
||||||
CVerifyNone, CVerifyPeer
|
CVerifyNone, CVerifyPeer
|
||||||
|
|
||||||
TSSLProtVersion* = enum
|
SSLProtVersion* = enum
|
||||||
protSSLv2, protSSLv3, protTLSv1, protSSLv23
|
protSSLv2, protSSLv3, protTLSv1, protSSLv23
|
||||||
|
|
||||||
PSSLContext* = distinct PSSLCTX
|
SSLContext* = distinct PSSLCTX
|
||||||
|
|
||||||
TSSLAcceptResult* = enum
|
SSLAcceptResult* = enum
|
||||||
AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess
|
AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess
|
||||||
|
|
||||||
|
{.deprecated: [ESSL: SSLError, TSSLCVerifyMode: SSLCVerifyMode,
|
||||||
|
TSSLProtVersion: SSLProtVersion, PSSLContext: SSLContext,
|
||||||
|
TSSLAcceptResult: SSLAcceptResult].}
|
||||||
|
|
||||||
const
|
const
|
||||||
BufferSize*: int = 4000 ## size of a buffered socket's buffer
|
BufferSize*: int = 4000 ## size of a buffered socket's buffer
|
||||||
|
|
||||||
type
|
type
|
||||||
TSocketImpl = object ## socket type
|
TSocketImpl = object ## socket type
|
||||||
fd: TSocketHandle
|
fd: SocketHandle
|
||||||
case isBuffered: bool # determines whether this socket is buffered.
|
case isBuffered: bool # determines whether this socket is buffered.
|
||||||
of true:
|
of true:
|
||||||
buffer: array[0..BufferSize, char]
|
buffer: array[0..BufferSize, char]
|
||||||
|
|
@ -76,31 +80,31 @@ type
|
||||||
case isSsl: bool
|
case isSsl: bool
|
||||||
of true:
|
of true:
|
||||||
sslHandle: PSSL
|
sslHandle: PSSL
|
||||||
sslContext: PSSLContext
|
sslContext: SSLContext
|
||||||
sslNoHandshake: bool # True if needs handshake.
|
sslNoHandshake: bool # True if needs handshake.
|
||||||
sslHasPeekChar: bool
|
sslHasPeekChar: bool
|
||||||
sslPeekChar: char
|
sslPeekChar: char
|
||||||
of false: nil
|
of false: nil
|
||||||
nonblocking: bool
|
nonblocking: bool
|
||||||
|
|
||||||
TSocket* = ref TSocketImpl
|
Socket* = ref TSocketImpl
|
||||||
|
|
||||||
TPort* = distinct uint16 ## port type
|
Port* = distinct uint16 ## port type
|
||||||
|
|
||||||
TDomain* = enum ## domain, which specifies the protocol family of the
|
Domain* = enum ## domain, which specifies the protocol family of the
|
||||||
## created socket. Other domains than those that are listed
|
## created socket. Other domains than those that are listed
|
||||||
## here are unsupported.
|
## here are unsupported.
|
||||||
AF_UNIX, ## for local socket (using a file). Unsupported on Windows.
|
AF_UNIX, ## for local socket (using a file). Unsupported on Windows.
|
||||||
AF_INET = 2, ## for network protocol IPv4 or
|
AF_INET = 2, ## for network protocol IPv4 or
|
||||||
AF_INET6 = 23 ## for network protocol IPv6.
|
AF_INET6 = 23 ## for network protocol IPv6.
|
||||||
|
|
||||||
TType* = enum ## second argument to `socket` proc
|
SockType* = enum ## second argument to `socket` proc
|
||||||
SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets
|
SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets
|
||||||
SOCK_DGRAM = 2, ## datagram service or Datagram Sockets
|
SOCK_DGRAM = 2, ## datagram service or Datagram Sockets
|
||||||
SOCK_RAW = 3, ## raw protocols atop the network layer.
|
SOCK_RAW = 3, ## raw protocols atop the network layer.
|
||||||
SOCK_SEQPACKET = 5 ## reliable sequenced packet service
|
SOCK_SEQPACKET = 5 ## reliable sequenced packet service
|
||||||
|
|
||||||
TProtocol* = enum ## third argument to `socket` proc
|
Protocol* = enum ## third argument to `socket` proc
|
||||||
IPPROTO_TCP = 6, ## Transmission control protocol.
|
IPPROTO_TCP = 6, ## Transmission control protocol.
|
||||||
IPPROTO_UDP = 17, ## User datagram protocol.
|
IPPROTO_UDP = 17, ## User datagram protocol.
|
||||||
IPPROTO_IP, ## Internet protocol. Unsupported on Windows.
|
IPPROTO_IP, ## Internet protocol. Unsupported on Windows.
|
||||||
|
|
@ -108,33 +112,39 @@ type
|
||||||
IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows.
|
IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows.
|
||||||
IPPROTO_ICMP ## Control message protocol. Unsupported on Windows.
|
IPPROTO_ICMP ## Control message protocol. Unsupported on Windows.
|
||||||
|
|
||||||
TServent* {.pure, final.} = object ## information about a service
|
Servent* = object ## information about a service
|
||||||
name*: string
|
name*: string
|
||||||
aliases*: seq[string]
|
aliases*: seq[string]
|
||||||
port*: TPort
|
port*: Port
|
||||||
proto*: string
|
proto*: string
|
||||||
|
|
||||||
Thostent* {.pure, final.} = object ## information about a given host
|
Hostent* = object ## information about a given host
|
||||||
name*: string
|
name*: string
|
||||||
aliases*: seq[string]
|
aliases*: seq[string]
|
||||||
addrtype*: TDomain
|
addrtype*: Domain
|
||||||
length*: int
|
length*: int
|
||||||
addrList*: seq[string]
|
addrList*: seq[string]
|
||||||
|
|
||||||
TSOBool* = enum ## Boolean socket options.
|
SOBool* = enum ## Boolean socket options.
|
||||||
OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive,
|
OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive,
|
||||||
OptOOBInline, OptReuseAddr
|
OptOOBInline, OptReuseAddr
|
||||||
|
|
||||||
TRecvLineResult* = enum ## result for recvLineAsync
|
RecvLineResult* = enum ## result for recvLineAsync
|
||||||
RecvFullLine, RecvPartialLine, RecvDisconnected, RecvFail
|
RecvFullLine, RecvPartialLine, RecvDisconnected, RecvFail
|
||||||
|
|
||||||
TReadLineResult* = enum ## result for readLineAsync
|
ReadLineResult* = enum ## result for readLineAsync
|
||||||
ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone
|
ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone
|
||||||
|
|
||||||
ETimeout* = object of Exception
|
TimeoutError* = object of Exception
|
||||||
|
|
||||||
|
{.deprecated: [TSocket: Socket, TType: SockType, TPort: Port, TDomain: Domain,
|
||||||
|
TProtocol: Protocol, TServent: Servent, THostent: Hostent,
|
||||||
|
TSOBool: SOBool, TRecvLineResult: RecvLineResult,
|
||||||
|
TReadLineResult: ReadLineResult, ETimeout: TimeoutError].}
|
||||||
|
|
||||||
|
|
||||||
let
|
let
|
||||||
invalidSocket*: TSocket = nil ## invalid socket
|
invalidSocket*: Socket = nil ## invalid socket
|
||||||
|
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
let
|
let
|
||||||
|
|
@ -143,7 +153,7 @@ else:
|
||||||
let
|
let
|
||||||
osInvalidSocket = posix.INVALID_SOCKET
|
osInvalidSocket = posix.INVALID_SOCKET
|
||||||
|
|
||||||
proc newTSocket(fd: TSocketHandle, isBuff: bool): TSocket =
|
proc newTSocket(fd: SocketHandle, isBuff: bool): Socket =
|
||||||
if fd == osInvalidSocket:
|
if fd == osInvalidSocket:
|
||||||
return nil
|
return nil
|
||||||
new(result)
|
new(result)
|
||||||
|
|
@ -153,10 +163,10 @@ proc newTSocket(fd: TSocketHandle, isBuff: bool): TSocket =
|
||||||
result.currPos = 0
|
result.currPos = 0
|
||||||
result.nonblocking = false
|
result.nonblocking = false
|
||||||
|
|
||||||
proc `==`*(a, b: TPort): bool {.borrow.}
|
proc `==`*(a, b: Port): bool {.borrow.}
|
||||||
## ``==`` for ports.
|
## ``==`` for ports.
|
||||||
|
|
||||||
proc `$`*(p: TPort): string {.borrow.}
|
proc `$`*(p: Port): string {.borrow.}
|
||||||
## returns the port number as a string
|
## returns the port number as a string
|
||||||
|
|
||||||
proc ntohl*(x: int32): int32 =
|
proc ntohl*(x: int32): int32 =
|
||||||
|
|
@ -189,14 +199,14 @@ proc htons*(x: int16): int16 =
|
||||||
result = sockets.ntohs(x)
|
result = sockets.ntohs(x)
|
||||||
|
|
||||||
when defined(Posix):
|
when defined(Posix):
|
||||||
proc toInt(domain: TDomain): cint =
|
proc toInt(domain: Domain): cint =
|
||||||
case domain
|
case domain
|
||||||
of AF_UNIX: result = posix.AF_UNIX
|
of AF_UNIX: result = posix.AF_UNIX
|
||||||
of AF_INET: result = posix.AF_INET
|
of AF_INET: result = posix.AF_INET
|
||||||
of AF_INET6: result = posix.AF_INET6
|
of AF_INET6: result = posix.AF_INET6
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc toInt(typ: TType): cint =
|
proc toInt(typ: SockType): cint =
|
||||||
case typ
|
case typ
|
||||||
of SOCK_STREAM: result = posix.SOCK_STREAM
|
of SOCK_STREAM: result = posix.SOCK_STREAM
|
||||||
of SOCK_DGRAM: result = posix.SOCK_DGRAM
|
of SOCK_DGRAM: result = posix.SOCK_DGRAM
|
||||||
|
|
@ -204,7 +214,7 @@ when defined(Posix):
|
||||||
of SOCK_RAW: result = posix.SOCK_RAW
|
of SOCK_RAW: result = posix.SOCK_RAW
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc toInt(p: TProtocol): cint =
|
proc toInt(p: Protocol): cint =
|
||||||
case p
|
case p
|
||||||
of IPPROTO_TCP: result = posix.IPPROTO_TCP
|
of IPPROTO_TCP: result = posix.IPPROTO_TCP
|
||||||
of IPPROTO_UDP: result = posix.IPPROTO_UDP
|
of IPPROTO_UDP: result = posix.IPPROTO_UDP
|
||||||
|
|
@ -215,13 +225,13 @@ when defined(Posix):
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
else:
|
else:
|
||||||
proc toInt(domain: TDomain): cint =
|
proc toInt(domain: Domain): cint =
|
||||||
result = toU16(ord(domain))
|
result = toU16(ord(domain))
|
||||||
|
|
||||||
proc toInt(typ: TType): cint =
|
proc toInt(typ: SockType): cint =
|
||||||
result = cint(ord(typ))
|
result = cint(ord(typ))
|
||||||
|
|
||||||
proc toInt(p: TProtocol): cint =
|
proc toInt(p: Protocol): cint =
|
||||||
result = cint(ord(p))
|
result = cint(ord(p))
|
||||||
|
|
||||||
proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
|
proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
|
||||||
|
|
@ -336,7 +346,7 @@ when defined(ssl):
|
||||||
if SSLSetFd(socket.sslHandle, socket.fd) != 1:
|
if SSLSetFd(socket.sslHandle, socket.fd) != 1:
|
||||||
SSLError()
|
SSLError()
|
||||||
|
|
||||||
proc socketError*(socket: TSocket, err: int = -1, async = false) =
|
proc raiseSocketError*(socket: TSocket, err: int = -1, async = false) =
|
||||||
## Raises proper errors based on return values of ``recv`` functions.
|
## Raises proper errors based on return values of ``recv`` functions.
|
||||||
##
|
##
|
||||||
## If ``async`` is ``True`` no error will be thrown in the case when the
|
## If ``async`` is ``True`` no error will be thrown in the case when the
|
||||||
|
|
@ -370,18 +380,18 @@ proc socketError*(socket: TSocket, err: int = -1, async = false) =
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
if lastError.int32 == WSAEWOULDBLOCK:
|
if lastError.int32 == WSAEWOULDBLOCK:
|
||||||
return
|
return
|
||||||
else: osError(lastError)
|
else: raiseOSError(lastError)
|
||||||
else:
|
else:
|
||||||
if lastError.int32 == EAGAIN or lastError.int32 == EWOULDBLOCK:
|
if lastError.int32 == EAGAIN or lastError.int32 == EWOULDBLOCK:
|
||||||
return
|
return
|
||||||
else: osError(lastError)
|
else: raiseOSError(lastError)
|
||||||
else: osError(lastError)
|
else: raiseOSError(lastError)
|
||||||
|
|
||||||
proc listen*(socket: TSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
|
proc listen*(socket: TSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
|
||||||
## Marks ``socket`` as accepting connections.
|
## Marks ``socket`` as accepting connections.
|
||||||
## ``Backlog`` specifies the maximum length of the
|
## ``Backlog`` specifies the maximum length of the
|
||||||
## queue of pending connections.
|
## queue of pending connections.
|
||||||
if listen(socket.fd, cint(backlog)) < 0'i32: osError(osLastError())
|
if listen(socket.fd, cint(backlog)) < 0'i32: raiseOSError(osLastError())
|
||||||
|
|
||||||
proc invalidIp4(s: string) {.noreturn, noinline.} =
|
proc invalidIp4(s: string) {.noreturn, noinline.} =
|
||||||
raise newException(ValueError, "invalid ip4 address: " & s)
|
raise newException(ValueError, "invalid ip4 address: " & s)
|
||||||
|
|
@ -420,7 +430,7 @@ template gaiNim(a, p, h, list: expr): stmt =
|
||||||
var gaiResult = getaddrinfo(a, $p, addr(h), list)
|
var gaiResult = getaddrinfo(a, $p, addr(h), list)
|
||||||
if gaiResult != 0'i32:
|
if gaiResult != 0'i32:
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
else:
|
else:
|
||||||
raise newException(OSError, $gai_strerror(gaiResult))
|
raise newException(OSError, $gai_strerror(gaiResult))
|
||||||
|
|
||||||
|
|
@ -440,7 +450,7 @@ proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {.
|
||||||
name.sin_addr.s_addr = sockets.htonl(INADDR_ANY)
|
name.sin_addr.s_addr = sockets.htonl(INADDR_ANY)
|
||||||
if bindSocket(socket.fd, cast[ptr TSockAddr](addr(name)),
|
if bindSocket(socket.fd, cast[ptr TSockAddr](addr(name)),
|
||||||
sizeof(name).TSocklen) < 0'i32:
|
sizeof(name).TSocklen) < 0'i32:
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
else:
|
else:
|
||||||
var hints: Taddrinfo
|
var hints: Taddrinfo
|
||||||
var aiList: ptr Taddrinfo = nil
|
var aiList: ptr Taddrinfo = nil
|
||||||
|
|
@ -449,7 +459,7 @@ proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {.
|
||||||
hints.ai_protocol = toInt(IPPROTO_TCP)
|
hints.ai_protocol = toInt(IPPROTO_TCP)
|
||||||
gaiNim(address, port, hints, aiList)
|
gaiNim(address, port, hints, aiList)
|
||||||
if bindSocket(socket.fd, aiList.ai_addr, aiList.ai_addrlen.TSocklen) < 0'i32:
|
if bindSocket(socket.fd, aiList.ai_addr, aiList.ai_addrlen.TSocklen) < 0'i32:
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
proc getSockName*(socket: TSocket): TPort =
|
proc getSockName*(socket: TSocket): TPort =
|
||||||
## returns the socket's associated port number.
|
## returns the socket's associated port number.
|
||||||
|
|
@ -463,7 +473,7 @@ proc getSockName*(socket: TSocket): TPort =
|
||||||
var namelen = sizeof(name).TSocklen
|
var namelen = sizeof(name).TSocklen
|
||||||
if getsockname(socket.fd, cast[ptr TSockAddr](addr(name)),
|
if getsockname(socket.fd, cast[ptr TSockAddr](addr(name)),
|
||||||
addr(namelen)) == -1'i32:
|
addr(namelen)) == -1'i32:
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
result = TPort(sockets.ntohs(name.sin_port))
|
result = TPort(sockets.ntohs(name.sin_port))
|
||||||
|
|
||||||
template acceptAddrPlain(noClientRet, successRet: expr,
|
template acceptAddrPlain(noClientRet, successRet: expr,
|
||||||
|
|
@ -484,7 +494,7 @@ template acceptAddrPlain(noClientRet, successRet: expr,
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
return noClientRet
|
return noClientRet
|
||||||
else: osError(err)
|
else: raiseOSError(err)
|
||||||
else:
|
else:
|
||||||
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
|
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
|
||||||
client = invalidSocket
|
client = invalidSocket
|
||||||
|
|
@ -493,7 +503,7 @@ template acceptAddrPlain(noClientRet, successRet: expr,
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
return noClientRet
|
return noClientRet
|
||||||
else: osError(err)
|
else: raiseOSError(err)
|
||||||
else:
|
else:
|
||||||
client.fd = sock
|
client.fd = sock
|
||||||
client.isBuffered = server.isBuffered
|
client.isBuffered = server.isBuffered
|
||||||
|
|
@ -680,7 +690,7 @@ proc getHostByAddr*(ip: string): Thostent {.tags: [ReadIOEffect].} =
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
|
var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
|
||||||
cint(sockets.AF_INET))
|
cint(sockets.AF_INET))
|
||||||
if s == nil: osError(osLastError())
|
if s == nil: raiseOSError(osLastError())
|
||||||
else:
|
else:
|
||||||
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).TSocklen,
|
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).TSocklen,
|
||||||
cint(posix.AF_INET))
|
cint(posix.AF_INET))
|
||||||
|
|
@ -707,7 +717,7 @@ proc getHostByName*(name: string): Thostent {.tags: [ReadIOEffect].} =
|
||||||
var s = winlean.gethostbyname(name)
|
var s = winlean.gethostbyname(name)
|
||||||
else:
|
else:
|
||||||
var s = posix.gethostbyname(name)
|
var s = posix.gethostbyname(name)
|
||||||
if s == nil: osError(osLastError())
|
if s == nil: raiseOSError(osLastError())
|
||||||
result.name = $s.h_name
|
result.name = $s.h_name
|
||||||
result.aliases = cstringArrayToSeq(s.h_aliases)
|
result.aliases = cstringArrayToSeq(s.h_aliases)
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
|
|
@ -729,7 +739,7 @@ proc getSockOptInt*(socket: TSocket, level, optname: int): int {.
|
||||||
var size = sizeof(res).TSocklen
|
var size = sizeof(res).TSocklen
|
||||||
if getsockopt(socket.fd, cint(level), cint(optname),
|
if getsockopt(socket.fd, cint(level), cint(optname),
|
||||||
addr(res), addr(size)) < 0'i32:
|
addr(res), addr(size)) < 0'i32:
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
result = int(res)
|
result = int(res)
|
||||||
|
|
||||||
proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {.
|
proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {.
|
||||||
|
|
@ -738,7 +748,7 @@ proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {.
|
||||||
var value = cint(optval)
|
var value = cint(optval)
|
||||||
if setsockopt(socket.fd, cint(level), cint(optname), addr(value),
|
if setsockopt(socket.fd, cint(level), cint(optname), addr(value),
|
||||||
sizeof(value).TSocklen) < 0'i32:
|
sizeof(value).TSocklen) < 0'i32:
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
proc toCInt(opt: TSOBool): cint =
|
proc toCInt(opt: TSOBool): cint =
|
||||||
case opt
|
case opt
|
||||||
|
|
@ -757,7 +767,7 @@ proc getSockOpt*(socket: TSocket, opt: TSOBool, level = SOL_SOCKET): bool {.
|
||||||
var size = sizeof(res).TSocklen
|
var size = sizeof(res).TSocklen
|
||||||
if getsockopt(socket.fd, cint(level), toCInt(opt),
|
if getsockopt(socket.fd, cint(level), toCInt(opt),
|
||||||
addr(res), addr(size)) < 0'i32:
|
addr(res), addr(size)) < 0'i32:
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
result = res != 0
|
result = res != 0
|
||||||
|
|
||||||
proc setSockOpt*(socket: TSocket, opt: TSOBool, value: bool, level = SOL_SOCKET) {.
|
proc setSockOpt*(socket: TSocket, opt: TSOBool, value: bool, level = SOL_SOCKET) {.
|
||||||
|
|
@ -766,7 +776,7 @@ proc setSockOpt*(socket: TSocket, opt: TSOBool, value: bool, level = SOL_SOCKET)
|
||||||
var valuei = cint(if value: 1 else: 0)
|
var valuei = cint(if value: 1 else: 0)
|
||||||
if setsockopt(socket.fd, cint(level), toCInt(opt), addr(valuei),
|
if setsockopt(socket.fd, cint(level), toCInt(opt), addr(valuei),
|
||||||
sizeof(valuei).TSocklen) < 0'i32:
|
sizeof(valuei).TSocklen) < 0'i32:
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
proc connect*(socket: TSocket, address: string, port = TPort(0),
|
proc connect*(socket: TSocket, address: string, port = TPort(0),
|
||||||
af: TDomain = AF_INET) {.tags: [ReadIOEffect].} =
|
af: TDomain = AF_INET) {.tags: [ReadIOEffect].} =
|
||||||
|
|
@ -784,7 +794,7 @@ proc connect*(socket: TSocket, address: string, port = TPort(0),
|
||||||
gaiNim(address, port, hints, aiList)
|
gaiNim(address, port, hints, aiList)
|
||||||
# try all possibilities:
|
# try all possibilities:
|
||||||
var success = false
|
var success = false
|
||||||
var lastError: TOSErrorCode
|
var lastError: OSErrorCode
|
||||||
var it = aiList
|
var it = aiList
|
||||||
while it != nil:
|
while it != nil:
|
||||||
if connect(socket.fd, it.ai_addr, it.ai_addrlen.TSocklen) == 0'i32:
|
if connect(socket.fd, it.ai_addr, it.ai_addrlen.TSocklen) == 0'i32:
|
||||||
|
|
@ -794,7 +804,7 @@ proc connect*(socket: TSocket, address: string, port = TPort(0),
|
||||||
it = it.ai_next
|
it = it.ai_next
|
||||||
|
|
||||||
freeaddrinfo(aiList)
|
freeaddrinfo(aiList)
|
||||||
if not success: osError(lastError)
|
if not success: raiseOSError(lastError)
|
||||||
|
|
||||||
when defined(ssl):
|
when defined(ssl):
|
||||||
if socket.isSSL:
|
if socket.isSSL:
|
||||||
|
|
@ -847,7 +857,7 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0),
|
||||||
gaiNim(name, port, hints, aiList)
|
gaiNim(name, port, hints, aiList)
|
||||||
# try all possibilities:
|
# try all possibilities:
|
||||||
var success = false
|
var success = false
|
||||||
var lastError: TOSErrorCode
|
var lastError: OSErrorCode
|
||||||
var it = aiList
|
var it = aiList
|
||||||
while it != nil:
|
while it != nil:
|
||||||
var ret = connect(socket.fd, it.ai_addr, it.ai_addrlen.TSocklen)
|
var ret = connect(socket.fd, it.ai_addr, it.ai_addrlen.TSocklen)
|
||||||
|
|
@ -869,7 +879,7 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0),
|
||||||
it = it.ai_next
|
it = it.ai_next
|
||||||
|
|
||||||
freeaddrinfo(aiList)
|
freeaddrinfo(aiList)
|
||||||
if not success: osError(lastError)
|
if not success: raiseOSError(lastError)
|
||||||
when defined(ssl):
|
when defined(ssl):
|
||||||
if socket.isSSL:
|
if socket.isSSL:
|
||||||
socket.sslNoHandshake = true
|
socket.sslNoHandshake = true
|
||||||
|
|
@ -1133,7 +1143,7 @@ proc waitFor(socket: TSocket, waited: var float, timeout, size: int,
|
||||||
result = min(result, size)
|
result = min(result, size)
|
||||||
else:
|
else:
|
||||||
if timeout - int(waited * 1000.0) < 1:
|
if timeout - int(waited * 1000.0) < 1:
|
||||||
raise newException(ETimeout, "Call to '" & funcName & "' timed out.")
|
raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
|
||||||
|
|
||||||
when defined(ssl):
|
when defined(ssl):
|
||||||
if socket.isSSL:
|
if socket.isSSL:
|
||||||
|
|
@ -1147,9 +1157,9 @@ proc waitFor(socket: TSocket, waited: var float, timeout, size: int,
|
||||||
var s = @[socket]
|
var s = @[socket]
|
||||||
var startTime = epochTime()
|
var startTime = epochTime()
|
||||||
let selRet = select(s, timeout - int(waited * 1000.0))
|
let selRet = select(s, timeout - int(waited * 1000.0))
|
||||||
if selRet < 0: osError(osLastError())
|
if selRet < 0: raiseOSError(osLastError())
|
||||||
if selRet != 1:
|
if selRet != 1:
|
||||||
raise newException(ETimeout, "Call to '" & funcName & "' timed out.")
|
raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
|
||||||
waited += (epochTime() - startTime)
|
waited += (epochTime() - startTime)
|
||||||
|
|
||||||
proc recv*(socket: TSocket, data: pointer, size: int, timeout: int): int {.
|
proc recv*(socket: TSocket, data: pointer, size: int, timeout: int): int {.
|
||||||
|
|
@ -1185,7 +1195,7 @@ proc recv*(socket: TSocket, data: var string, size: int, timeout = -1): int =
|
||||||
result = recv(socket, cstring(data), size, timeout)
|
result = recv(socket, cstring(data), size, timeout)
|
||||||
if result < 0:
|
if result < 0:
|
||||||
data.setLen(0)
|
data.setLen(0)
|
||||||
socket.socketError(result)
|
socket.raiseSocketError(result)
|
||||||
data.setLen(result)
|
data.setLen(result)
|
||||||
|
|
||||||
proc recvAsync*(socket: TSocket, data: var string, size: int): int =
|
proc recvAsync*(socket: TSocket, data: var string, size: int): int =
|
||||||
|
|
@ -1199,7 +1209,7 @@ proc recvAsync*(socket: TSocket, data: var string, size: int): int =
|
||||||
result = recv(socket, cstring(data), size)
|
result = recv(socket, cstring(data), size)
|
||||||
if result < 0:
|
if result < 0:
|
||||||
data.setLen(0)
|
data.setLen(0)
|
||||||
socket.socketError(async = true)
|
socket.raiseSocketError(async = true)
|
||||||
result = -1
|
result = -1
|
||||||
data.setLen(result)
|
data.setLen(result)
|
||||||
|
|
||||||
|
|
@ -1296,14 +1306,14 @@ proc readLine*(socket: TSocket, line: var TaintedString, timeout = -1) {.
|
||||||
var c: char
|
var c: char
|
||||||
discard waitFor(socket, waited, timeout, 1, "readLine")
|
discard waitFor(socket, waited, timeout, 1, "readLine")
|
||||||
var n = recv(socket, addr(c), 1)
|
var n = recv(socket, addr(c), 1)
|
||||||
if n < 0: socket.socketError()
|
if n < 0: socket.raiseSocketError()
|
||||||
elif n == 0: return
|
elif n == 0: return
|
||||||
if c == '\r':
|
if c == '\r':
|
||||||
discard waitFor(socket, waited, timeout, 1, "readLine")
|
discard waitFor(socket, waited, timeout, 1, "readLine")
|
||||||
n = peekChar(socket, c)
|
n = peekChar(socket, c)
|
||||||
if n > 0 and c == '\L':
|
if n > 0 and c == '\L':
|
||||||
discard recv(socket, addr(c), 1)
|
discard recv(socket, addr(c), 1)
|
||||||
elif n <= 0: socket.socketError()
|
elif n <= 0: socket.raiseSocketError()
|
||||||
addNLIfEmpty()
|
addNLIfEmpty()
|
||||||
return
|
return
|
||||||
elif c == '\L':
|
elif c == '\L':
|
||||||
|
|
@ -1357,7 +1367,7 @@ proc readLineAsync*(socket: TSocket,
|
||||||
setLen(line.string, 0)
|
setLen(line.string, 0)
|
||||||
|
|
||||||
template errorOrNone =
|
template errorOrNone =
|
||||||
socket.socketError(async = true)
|
socket.raiseSocketError(async = true)
|
||||||
return ReadNone
|
return ReadNone
|
||||||
|
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -1390,7 +1400,7 @@ proc recv*(socket: TSocket): TaintedString {.tags: [ReadIOEffect], deprecated.}
|
||||||
var pos = 0
|
var pos = 0
|
||||||
while true:
|
while true:
|
||||||
var bytesRead = recv(socket, addr(string(result)[pos]), bufSize-1)
|
var bytesRead = recv(socket, addr(string(result)[pos]), bufSize-1)
|
||||||
if bytesRead == -1: osError(osLastError())
|
if bytesRead == -1: raiseOSError(osLastError())
|
||||||
setLen(result.string, pos + bytesRead)
|
setLen(result.string, pos + bytesRead)
|
||||||
if bytesRead != bufSize-1: break
|
if bytesRead != bufSize-1: break
|
||||||
# increase capacity:
|
# increase capacity:
|
||||||
|
|
@ -1463,11 +1473,11 @@ proc recvAsync*(socket: TSocket, s: var TaintedString): bool {.
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
if err.int32 == WSAEWOULDBLOCK:
|
if err.int32 == WSAEWOULDBLOCK:
|
||||||
return false
|
return false
|
||||||
else: osError(err)
|
else: raiseOSError(err)
|
||||||
else:
|
else:
|
||||||
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
|
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
|
||||||
return false
|
return false
|
||||||
else: osError(err)
|
else: raiseOSError(err)
|
||||||
|
|
||||||
setLen(s.string, pos + bytesRead)
|
setLen(s.string, pos + bytesRead)
|
||||||
if bytesRead != bufSize-1: break
|
if bytesRead != bufSize-1: break
|
||||||
|
|
@ -1516,11 +1526,11 @@ proc recvFromAsync*(socket: TSocket, data: var string, length: int,
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
if err.int32 == WSAEWOULDBLOCK:
|
if err.int32 == WSAEWOULDBLOCK:
|
||||||
return false
|
return false
|
||||||
else: osError(err)
|
else: raiseOSError(err)
|
||||||
else:
|
else:
|
||||||
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
|
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
|
||||||
return false
|
return false
|
||||||
else: osError(err)
|
else: raiseOSError(err)
|
||||||
|
|
||||||
proc skip*(socket: TSocket) {.tags: [ReadIOEffect], deprecated.} =
|
proc skip*(socket: TSocket) {.tags: [ReadIOEffect], deprecated.} =
|
||||||
## skips all the data that is pending for the socket
|
## skips all the data that is pending for the socket
|
||||||
|
|
@ -1570,7 +1580,7 @@ proc send*(socket: TSocket, data: string) {.tags: [WriteIOEffect].} =
|
||||||
if socket.isSSL:
|
if socket.isSSL:
|
||||||
SSLError()
|
SSLError()
|
||||||
|
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
if sent != data.len:
|
if sent != data.len:
|
||||||
raise newException(OSError, "Could not send all data.")
|
raise newException(OSError, "Could not send all data.")
|
||||||
|
|
@ -1607,11 +1617,11 @@ proc sendAsync*(socket: TSocket, data: string): int {.tags: [WriteIOEffect].} =
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
if err.int32 == WSAEINPROGRESS:
|
if err.int32 == WSAEINPROGRESS:
|
||||||
return 0
|
return 0
|
||||||
else: osError(err)
|
else: raiseOSError(err)
|
||||||
else:
|
else:
|
||||||
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
|
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
|
||||||
return 0
|
return 0
|
||||||
else: osError(err)
|
else: raiseOSError(err)
|
||||||
|
|
||||||
|
|
||||||
proc trySend*(socket: TSocket, data: string): bool {.tags: [WriteIOEffect].} =
|
proc trySend*(socket: TSocket, data: string): bool {.tags: [WriteIOEffect].} =
|
||||||
|
|
@ -1667,15 +1677,15 @@ proc setBlocking(s: TSocket, blocking: bool) =
|
||||||
when defined(Windows):
|
when defined(Windows):
|
||||||
var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
|
var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
|
||||||
if ioctlsocket(s.fd, FIONBIO, addr(mode)) == -1:
|
if ioctlsocket(s.fd, FIONBIO, addr(mode)) == -1:
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
else: # BSD sockets
|
else: # BSD sockets
|
||||||
var x: int = fcntl(s.fd, F_GETFL, 0)
|
var x: int = fcntl(s.fd, F_GETFL, 0)
|
||||||
if x == -1:
|
if x == -1:
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
else:
|
else:
|
||||||
var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
|
var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
|
||||||
if fcntl(s.fd, F_SETFL, mode) == -1:
|
if fcntl(s.fd, F_SETFL, mode) == -1:
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
s.nonblocking = not blocking
|
s.nonblocking = not blocking
|
||||||
|
|
||||||
discard """ proc setReuseAddr*(s: TSocket) =
|
discard """ proc setReuseAddr*(s: TSocket) =
|
||||||
|
|
@ -1715,6 +1725,6 @@ proc isBlocking*(socket: TSocket): bool = not socket.nonblocking
|
||||||
|
|
||||||
when defined(Windows):
|
when defined(Windows):
|
||||||
var wsa: TWSAData
|
var wsa: TWSAData
|
||||||
if wsaStartup(0x0101'i16, addr wsa) != 0: osError(osLastError())
|
if wsaStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError())
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -8,8 +8,8 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
## This module provides a stream interface and two implementations thereof:
|
## This module provides a stream interface and two implementations thereof:
|
||||||
## the `PFileStream` and the `PStringStream` which implement the stream
|
## the `FileStream` and the `StringStream` which implement the stream
|
||||||
## interface for Nimrod file objects (`TFile`) and strings. Other modules
|
## interface for Nimrod file objects (`File`) and strings. Other modules
|
||||||
## may provide other implementations for this standard stream interface.
|
## may provide other implementations for this standard stream interface.
|
||||||
|
|
||||||
include "system/inclrtl"
|
include "system/inclrtl"
|
||||||
|
|
@ -19,81 +19,83 @@ proc newEIO(msg: string): ref IOError =
|
||||||
result.msg = msg
|
result.msg = msg
|
||||||
|
|
||||||
type
|
type
|
||||||
PStream* = ref TStream
|
Stream* = ref StreamObj
|
||||||
TStream* = object of RootObj ## Stream interface that supports
|
StreamObj* = object of RootObj ## Stream interface that supports
|
||||||
## writing or reading. Note that these fields
|
## writing or reading. Note that these fields
|
||||||
## here shouldn't be used directly. They are
|
## here shouldn't be used directly. They are
|
||||||
## accessible so that a stream implementation
|
## accessible so that a stream implementation
|
||||||
## can override them.
|
## can override them.
|
||||||
closeImpl*: proc (s: PStream) {.nimcall, tags: [], gcsafe.}
|
closeImpl*: proc (s: Stream) {.nimcall, tags: [], gcsafe.}
|
||||||
atEndImpl*: proc (s: PStream): bool {.nimcall, tags: [], gcsafe.}
|
atEndImpl*: proc (s: Stream): bool {.nimcall, tags: [], gcsafe.}
|
||||||
setPositionImpl*: proc (s: PStream, pos: int) {.nimcall, tags: [], gcsafe.}
|
setPositionImpl*: proc (s: Stream, pos: int) {.nimcall, tags: [], gcsafe.}
|
||||||
getPositionImpl*: proc (s: PStream): int {.nimcall, tags: [], gcsafe.}
|
getPositionImpl*: proc (s: Stream): int {.nimcall, tags: [], gcsafe.}
|
||||||
readDataImpl*: proc (s: PStream, buffer: pointer,
|
readDataImpl*: proc (s: Stream, buffer: pointer,
|
||||||
bufLen: int): int {.nimcall, tags: [ReadIOEffect], gcsafe.}
|
bufLen: int): int {.nimcall, tags: [ReadIOEffect], gcsafe.}
|
||||||
writeDataImpl*: proc (s: PStream, buffer: pointer, bufLen: int) {.nimcall,
|
writeDataImpl*: proc (s: Stream, buffer: pointer, bufLen: int) {.nimcall,
|
||||||
tags: [WriteIOEffect], gcsafe.}
|
tags: [WriteIOEffect], gcsafe.}
|
||||||
flushImpl*: proc (s: PStream) {.nimcall, tags: [WriteIOEffect], gcsafe.}
|
flushImpl*: proc (s: Stream) {.nimcall, tags: [WriteIOEffect], gcsafe.}
|
||||||
|
|
||||||
proc flush*(s: PStream) =
|
{.deprecated: [PStream: Stream, TStream: StreamObj].}
|
||||||
|
|
||||||
|
proc flush*(s: Stream) =
|
||||||
## flushes the buffers that the stream `s` might use.
|
## flushes the buffers that the stream `s` might use.
|
||||||
if not isNil(s.flushImpl): s.flushImpl(s)
|
if not isNil(s.flushImpl): s.flushImpl(s)
|
||||||
|
|
||||||
proc close*(s: PStream) =
|
proc close*(s: Stream) =
|
||||||
## closes the stream `s`.
|
## closes the stream `s`.
|
||||||
if not isNil(s.closeImpl): s.closeImpl(s)
|
if not isNil(s.closeImpl): s.closeImpl(s)
|
||||||
|
|
||||||
proc close*(s, unused: PStream) {.deprecated.} =
|
proc close*(s, unused: Stream) {.deprecated.} =
|
||||||
## closes the stream `s`.
|
## closes the stream `s`.
|
||||||
s.closeImpl(s)
|
s.closeImpl(s)
|
||||||
|
|
||||||
proc atEnd*(s: PStream): bool =
|
proc atEnd*(s: Stream): bool =
|
||||||
## checks if more data can be read from `f`. Returns true if all data has
|
## checks if more data can be read from `f`. Returns true if all data has
|
||||||
## been read.
|
## been read.
|
||||||
result = s.atEndImpl(s)
|
result = s.atEndImpl(s)
|
||||||
|
|
||||||
proc atEnd*(s, unused: PStream): bool {.deprecated.} =
|
proc atEnd*(s, unused: Stream): bool {.deprecated.} =
|
||||||
## checks if more data can be read from `f`. Returns true if all data has
|
## checks if more data can be read from `f`. Returns true if all data has
|
||||||
## been read.
|
## been read.
|
||||||
result = s.atEndImpl(s)
|
result = s.atEndImpl(s)
|
||||||
|
|
||||||
proc setPosition*(s: PStream, pos: int) =
|
proc setPosition*(s: Stream, pos: int) =
|
||||||
## sets the position `pos` of the stream `s`.
|
## sets the position `pos` of the stream `s`.
|
||||||
s.setPositionImpl(s, pos)
|
s.setPositionImpl(s, pos)
|
||||||
|
|
||||||
proc setPosition*(s, unused: PStream, pos: int) {.deprecated.} =
|
proc setPosition*(s, unused: Stream, pos: int) {.deprecated.} =
|
||||||
## sets the position `pos` of the stream `s`.
|
## sets the position `pos` of the stream `s`.
|
||||||
s.setPositionImpl(s, pos)
|
s.setPositionImpl(s, pos)
|
||||||
|
|
||||||
proc getPosition*(s: PStream): int =
|
proc getPosition*(s: Stream): int =
|
||||||
## retrieves the current position in the stream `s`.
|
## retrieves the current position in the stream `s`.
|
||||||
result = s.getPositionImpl(s)
|
result = s.getPositionImpl(s)
|
||||||
|
|
||||||
proc getPosition*(s, unused: PStream): int {.deprecated.} =
|
proc getPosition*(s, unused: Stream): int {.deprecated.} =
|
||||||
## retrieves the current position in the stream `s`.
|
## retrieves the current position in the stream `s`.
|
||||||
result = s.getPositionImpl(s)
|
result = s.getPositionImpl(s)
|
||||||
|
|
||||||
proc readData*(s: PStream, buffer: pointer, bufLen: int): int =
|
proc readData*(s: Stream, buffer: pointer, bufLen: int): int =
|
||||||
## low level proc that reads data into an untyped `buffer` of `bufLen` size.
|
## low level proc that reads data into an untyped `buffer` of `bufLen` size.
|
||||||
result = s.readDataImpl(s, buffer, bufLen)
|
result = s.readDataImpl(s, buffer, bufLen)
|
||||||
|
|
||||||
proc readData*(s, unused: PStream, buffer: pointer,
|
proc readData*(s, unused: Stream, buffer: pointer,
|
||||||
bufLen: int): int {.deprecated.} =
|
bufLen: int): int {.deprecated.} =
|
||||||
## low level proc that reads data into an untyped `buffer` of `bufLen` size.
|
## low level proc that reads data into an untyped `buffer` of `bufLen` size.
|
||||||
result = s.readDataImpl(s, buffer, bufLen)
|
result = s.readDataImpl(s, buffer, bufLen)
|
||||||
|
|
||||||
proc writeData*(s: PStream, buffer: pointer, bufLen: int) =
|
proc writeData*(s: Stream, buffer: pointer, bufLen: int) =
|
||||||
## low level proc that writes an untyped `buffer` of `bufLen` size
|
## low level proc that writes an untyped `buffer` of `bufLen` size
|
||||||
## to the stream `s`.
|
## to the stream `s`.
|
||||||
s.writeDataImpl(s, buffer, bufLen)
|
s.writeDataImpl(s, buffer, bufLen)
|
||||||
|
|
||||||
proc writeData*(s, unused: PStream, buffer: pointer,
|
proc writeData*(s, unused: Stream, buffer: pointer,
|
||||||
bufLen: int) {.deprecated.} =
|
bufLen: int) {.deprecated.} =
|
||||||
## low level proc that writes an untyped `buffer` of `bufLen` size
|
## low level proc that writes an untyped `buffer` of `bufLen` size
|
||||||
## to the stream `s`.
|
## to the stream `s`.
|
||||||
s.writeDataImpl(s, buffer, bufLen)
|
s.writeDataImpl(s, buffer, bufLen)
|
||||||
|
|
||||||
proc write*[T](s: PStream, x: T) =
|
proc write*[T](s: Stream, x: T) =
|
||||||
## generic write procedure. Writes `x` to the stream `s`. Implementation:
|
## generic write procedure. Writes `x` to the stream `s`. Implementation:
|
||||||
##
|
##
|
||||||
## .. code-block:: Nimrod
|
## .. code-block:: Nimrod
|
||||||
|
|
@ -103,63 +105,63 @@ proc write*[T](s: PStream, x: T) =
|
||||||
shallowCopy(y, x)
|
shallowCopy(y, x)
|
||||||
writeData(s, addr(y), sizeof(y))
|
writeData(s, addr(y), sizeof(y))
|
||||||
|
|
||||||
proc write*(s: PStream, x: string) =
|
proc write*(s: Stream, x: string) =
|
||||||
## writes the string `x` to the the stream `s`. No length field or
|
## writes the string `x` to the the stream `s`. No length field or
|
||||||
## terminating zero is written.
|
## terminating zero is written.
|
||||||
writeData(s, cstring(x), x.len)
|
writeData(s, cstring(x), x.len)
|
||||||
|
|
||||||
proc writeln*(s: PStream, args: varargs[string, `$`]) =
|
proc writeln*(s: Stream, args: varargs[string, `$`]) =
|
||||||
## writes one or more strings to the the stream `s` followed
|
## writes one or more strings to the the stream `s` followed
|
||||||
## by a new line. No length field or terminating zero is written.
|
## by a new line. No length field or terminating zero is written.
|
||||||
for str in args: write(s, str)
|
for str in args: write(s, str)
|
||||||
write(s, "\n")
|
write(s, "\n")
|
||||||
|
|
||||||
proc read[T](s: PStream, result: var T) =
|
proc read[T](s: Stream, result: var T) =
|
||||||
## generic read procedure. Reads `result` from the stream `s`.
|
## generic read procedure. Reads `result` from the stream `s`.
|
||||||
if readData(s, addr(result), sizeof(T)) != sizeof(T):
|
if readData(s, addr(result), sizeof(T)) != sizeof(T):
|
||||||
raise newEIO("cannot read from stream")
|
raise newEIO("cannot read from stream")
|
||||||
|
|
||||||
proc readChar*(s: PStream): char =
|
proc readChar*(s: Stream): char =
|
||||||
## reads a char from the stream `s`. Raises `EIO` if an error occured.
|
## reads a char from the stream `s`. Raises `EIO` if an error occured.
|
||||||
## Returns '\0' as an EOF marker.
|
## Returns '\0' as an EOF marker.
|
||||||
if readData(s, addr(result), sizeof(result)) != 1: result = '\0'
|
if readData(s, addr(result), sizeof(result)) != 1: result = '\0'
|
||||||
|
|
||||||
proc readBool*(s: PStream): bool =
|
proc readBool*(s: Stream): bool =
|
||||||
## reads a bool from the stream `s`. Raises `EIO` if an error occured.
|
## reads a bool from the stream `s`. Raises `EIO` if an error occured.
|
||||||
read(s, result)
|
read(s, result)
|
||||||
|
|
||||||
proc readInt8*(s: PStream): int8 =
|
proc readInt8*(s: Stream): int8 =
|
||||||
## reads an int8 from the stream `s`. Raises `EIO` if an error occured.
|
## reads an int8 from the stream `s`. Raises `EIO` if an error occured.
|
||||||
read(s, result)
|
read(s, result)
|
||||||
|
|
||||||
proc readInt16*(s: PStream): int16 =
|
proc readInt16*(s: Stream): int16 =
|
||||||
## reads an int16 from the stream `s`. Raises `EIO` if an error occured.
|
## reads an int16 from the stream `s`. Raises `EIO` if an error occured.
|
||||||
read(s, result)
|
read(s, result)
|
||||||
|
|
||||||
proc readInt32*(s: PStream): int32 =
|
proc readInt32*(s: Stream): int32 =
|
||||||
## reads an int32 from the stream `s`. Raises `EIO` if an error occured.
|
## reads an int32 from the stream `s`. Raises `EIO` if an error occured.
|
||||||
read(s, result)
|
read(s, result)
|
||||||
|
|
||||||
proc readInt64*(s: PStream): int64 =
|
proc readInt64*(s: Stream): int64 =
|
||||||
## reads an int64 from the stream `s`. Raises `EIO` if an error occured.
|
## reads an int64 from the stream `s`. Raises `EIO` if an error occured.
|
||||||
read(s, result)
|
read(s, result)
|
||||||
|
|
||||||
proc readFloat32*(s: PStream): float32 =
|
proc readFloat32*(s: Stream): float32 =
|
||||||
## reads a float32 from the stream `s`. Raises `EIO` if an error occured.
|
## reads a float32 from the stream `s`. Raises `EIO` if an error occured.
|
||||||
read(s, result)
|
read(s, result)
|
||||||
|
|
||||||
proc readFloat64*(s: PStream): float64 =
|
proc readFloat64*(s: Stream): float64 =
|
||||||
## reads a float64 from the stream `s`. Raises `EIO` if an error occured.
|
## reads a float64 from the stream `s`. Raises `EIO` if an error occured.
|
||||||
read(s, result)
|
read(s, result)
|
||||||
|
|
||||||
proc readStr*(s: PStream, length: int): TaintedString =
|
proc readStr*(s: Stream, length: int): TaintedString =
|
||||||
## reads a string of length `length` from the stream `s`. Raises `EIO` if
|
## reads a string of length `length` from the stream `s`. Raises `EIO` if
|
||||||
## an error occured.
|
## an error occured.
|
||||||
result = newString(length).TaintedString
|
result = newString(length).TaintedString
|
||||||
var L = readData(s, addr(string(result)[0]), length)
|
var L = readData(s, addr(string(result)[0]), length)
|
||||||
if L != length: setLen(result.string, L)
|
if L != length: setLen(result.string, L)
|
||||||
|
|
||||||
proc readLine*(s: PStream, line: var TaintedString): bool =
|
proc readLine*(s: Stream, line: var TaintedString): bool =
|
||||||
## reads a line of text from the stream `s` into `line`. `line` must not be
|
## reads a line of text from the stream `s` into `line`. `line` must not be
|
||||||
## ``nil``! May throw an IO exception.
|
## ``nil``! May throw an IO exception.
|
||||||
## A line of text may be delimited by ``CR``, ``LF`` or
|
## A line of text may be delimited by ``CR``, ``LF`` or
|
||||||
|
|
@ -179,7 +181,7 @@ proc readLine*(s: PStream, line: var TaintedString): bool =
|
||||||
line.string.add(c)
|
line.string.add(c)
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc readLine*(s: PStream): TaintedString =
|
proc readLine*(s: Stream): TaintedString =
|
||||||
## Reads a line from a stream `s`. Note: This is not very efficient. Raises
|
## Reads a line from a stream `s`. Note: This is not very efficient. Raises
|
||||||
## `EIO` if an error occured.
|
## `EIO` if an error occured.
|
||||||
result = TaintedString""
|
result = TaintedString""
|
||||||
|
|
@ -194,42 +196,44 @@ proc readLine*(s: PStream): TaintedString =
|
||||||
result.string.add(c)
|
result.string.add(c)
|
||||||
|
|
||||||
type
|
type
|
||||||
PStringStream* = ref TStringStream ## a stream that encapsulates a string
|
StringStream* = ref StringStreamObj ## a stream that encapsulates a string
|
||||||
TStringStream* = object of TStream
|
StringStreamObj* = object of StreamObj
|
||||||
data*: string
|
data*: string
|
||||||
pos: int
|
pos: int
|
||||||
|
|
||||||
proc ssAtEnd(s: PStream): bool =
|
{.deprecated: [PStringStream: StringStream, TStringStream: StringStreamObj].}
|
||||||
var s = PStringStream(s)
|
|
||||||
|
proc ssAtEnd(s: Stream): bool =
|
||||||
|
var s = StringStream(s)
|
||||||
return s.pos >= s.data.len
|
return s.pos >= s.data.len
|
||||||
|
|
||||||
proc ssSetPosition(s: PStream, pos: int) =
|
proc ssSetPosition(s: Stream, pos: int) =
|
||||||
var s = PStringStream(s)
|
var s = StringStream(s)
|
||||||
s.pos = clamp(pos, 0, s.data.high)
|
s.pos = clamp(pos, 0, s.data.high)
|
||||||
|
|
||||||
proc ssGetPosition(s: PStream): int =
|
proc ssGetPosition(s: Stream): int =
|
||||||
var s = PStringStream(s)
|
var s = StringStream(s)
|
||||||
return s.pos
|
return s.pos
|
||||||
|
|
||||||
proc ssReadData(s: PStream, buffer: pointer, bufLen: int): int =
|
proc ssReadData(s: Stream, buffer: pointer, bufLen: int): int =
|
||||||
var s = PStringStream(s)
|
var s = StringStream(s)
|
||||||
result = min(bufLen, s.data.len - s.pos)
|
result = min(bufLen, s.data.len - s.pos)
|
||||||
if result > 0:
|
if result > 0:
|
||||||
copyMem(buffer, addr(s.data[s.pos]), result)
|
copyMem(buffer, addr(s.data[s.pos]), result)
|
||||||
inc(s.pos, result)
|
inc(s.pos, result)
|
||||||
|
|
||||||
proc ssWriteData(s: PStream, buffer: pointer, bufLen: int) =
|
proc ssWriteData(s: Stream, buffer: pointer, bufLen: int) =
|
||||||
var s = PStringStream(s)
|
var s = StringStream(s)
|
||||||
if bufLen > 0:
|
if bufLen > 0:
|
||||||
setLen(s.data, s.data.len + bufLen)
|
setLen(s.data, s.data.len + bufLen)
|
||||||
copyMem(addr(s.data[s.pos]), buffer, bufLen)
|
copyMem(addr(s.data[s.pos]), buffer, bufLen)
|
||||||
inc(s.pos, bufLen)
|
inc(s.pos, bufLen)
|
||||||
|
|
||||||
proc ssClose(s: PStream) =
|
proc ssClose(s: Stream) =
|
||||||
var s = PStringStream(s)
|
var s = StringStream(s)
|
||||||
s.data = nil
|
s.data = nil
|
||||||
|
|
||||||
proc newStringStream*(s: string = ""): PStringStream =
|
proc newStringStream*(s: string = ""): StringStream =
|
||||||
## creates a new stream from the string `s`.
|
## creates a new stream from the string `s`.
|
||||||
new(result)
|
new(result)
|
||||||
result.data = s
|
result.data = s
|
||||||
|
|
@ -244,27 +248,28 @@ proc newStringStream*(s: string = ""): PStringStream =
|
||||||
when not defined(js):
|
when not defined(js):
|
||||||
|
|
||||||
type
|
type
|
||||||
PFileStream* = ref TFileStream ## a stream that encapsulates a `TFile`
|
FileStream* = ref FileStreamObj ## a stream that encapsulates a `TFile`
|
||||||
TFileStream* = object of TStream
|
FileStreamObj* = object of Stream
|
||||||
f: File
|
f: File
|
||||||
|
{.deprecated: [PFileStream: FileStream, TFileStream: FileStreamObj].}
|
||||||
|
|
||||||
proc fsClose(s: PStream) =
|
proc fsClose(s: Stream) =
|
||||||
if PFileStream(s).f != nil:
|
if FileStream(s).f != nil:
|
||||||
close(PFileStream(s).f)
|
close(FileStream(s).f)
|
||||||
PFileStream(s).f = nil
|
FileStream(s).f = nil
|
||||||
proc fsFlush(s: PStream) = flushFile(PFileStream(s).f)
|
proc fsFlush(s: Stream) = flushFile(FileStream(s).f)
|
||||||
proc fsAtEnd(s: PStream): bool = return endOfFile(PFileStream(s).f)
|
proc fsAtEnd(s: Stream): bool = return endOfFile(FileStream(s).f)
|
||||||
proc fsSetPosition(s: PStream, pos: int) = setFilePos(PFileStream(s).f, pos)
|
proc fsSetPosition(s: Stream, pos: int) = setFilePos(FileStream(s).f, pos)
|
||||||
proc fsGetPosition(s: PStream): int = return int(getFilePos(PFileStream(s).f))
|
proc fsGetPosition(s: Stream): int = return int(getFilePos(FileStream(s).f))
|
||||||
|
|
||||||
proc fsReadData(s: PStream, buffer: pointer, bufLen: int): int =
|
proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int =
|
||||||
result = readBuffer(PFileStream(s).f, buffer, bufLen)
|
result = readBuffer(FileStream(s).f, buffer, bufLen)
|
||||||
|
|
||||||
proc fsWriteData(s: PStream, buffer: pointer, bufLen: int) =
|
proc fsWriteData(s: Stream, buffer: pointer, bufLen: int) =
|
||||||
if writeBuffer(PFileStream(s).f, buffer, bufLen) != bufLen:
|
if writeBuffer(FileStream(s).f, buffer, bufLen) != bufLen:
|
||||||
raise newEIO("cannot write to stream")
|
raise newEIO("cannot write to stream")
|
||||||
|
|
||||||
proc newFileStream*(f: File): PFileStream =
|
proc newFileStream*(f: File): FileStream =
|
||||||
## creates a new stream from the file `f`.
|
## creates a new stream from the file `f`.
|
||||||
new(result)
|
new(result)
|
||||||
result.f = f
|
result.f = f
|
||||||
|
|
@ -276,7 +281,7 @@ when not defined(js):
|
||||||
result.writeDataImpl = fsWriteData
|
result.writeDataImpl = fsWriteData
|
||||||
result.flushImpl = fsFlush
|
result.flushImpl = fsFlush
|
||||||
|
|
||||||
proc newFileStream*(filename: string, mode: FileMode): PFileStream =
|
proc newFileStream*(filename: string, mode: FileMode): FileStream =
|
||||||
## creates a new stream from the file named `filename` with the mode `mode`.
|
## creates a new stream from the file named `filename` with the mode `mode`.
|
||||||
## If the file cannot be opened, nil is returned. See the `system
|
## If the file cannot be opened, nil is returned. See the `system
|
||||||
## <system.html>`_ module for a list of available TFileMode enums.
|
## <system.html>`_ module for a list of available TFileMode enums.
|
||||||
|
|
@ -288,45 +293,47 @@ when true:
|
||||||
discard
|
discard
|
||||||
else:
|
else:
|
||||||
type
|
type
|
||||||
TFileHandle* = cint ## Operating system file handle
|
FileHandleStream* = ref FileHandleStreamObj
|
||||||
PFileHandleStream* = ref TFileHandleStream
|
FileHandleStreamObj* = object of Stream
|
||||||
TFileHandleStream* = object of TStream
|
handle*: FileHandle
|
||||||
handle*: TFileHandle
|
|
||||||
pos: int
|
pos: int
|
||||||
|
|
||||||
proc newEOS(msg: string): ref EOS =
|
{.deprecated: [PFileHandleStream: FileHandleStream,
|
||||||
|
TFileHandleStream: FileHandleStreamObj].}
|
||||||
|
|
||||||
|
proc newEOS(msg: string): ref OSError =
|
||||||
new(result)
|
new(result)
|
||||||
result.msg = msg
|
result.msg = msg
|
||||||
|
|
||||||
proc hsGetPosition(s: PFileHandleStream): int =
|
proc hsGetPosition(s: FileHandleStream): int =
|
||||||
return s.pos
|
return s.pos
|
||||||
|
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
# do not import windows as this increases compile times:
|
# do not import windows as this increases compile times:
|
||||||
nil
|
discard
|
||||||
else:
|
else:
|
||||||
import posix
|
import posix
|
||||||
|
|
||||||
proc hsSetPosition(s: PFileHandleStream, pos: int) =
|
proc hsSetPosition(s: FileHandleStream, pos: int) =
|
||||||
discard lseek(s.handle, pos, SEEK_SET)
|
discard lseek(s.handle, pos, SEEK_SET)
|
||||||
|
|
||||||
proc hsClose(s: PFileHandleStream) = discard close(s.handle)
|
proc hsClose(s: FileHandleStream) = discard close(s.handle)
|
||||||
proc hsAtEnd(s: PFileHandleStream): bool =
|
proc hsAtEnd(s: FileHandleStream): bool =
|
||||||
var pos = hsGetPosition(s)
|
var pos = hsGetPosition(s)
|
||||||
var theEnd = lseek(s.handle, 0, SEEK_END)
|
var theEnd = lseek(s.handle, 0, SEEK_END)
|
||||||
result = pos >= theEnd
|
result = pos >= theEnd
|
||||||
hsSetPosition(s, pos) # set position back
|
hsSetPosition(s, pos) # set position back
|
||||||
|
|
||||||
proc hsReadData(s: PFileHandleStream, buffer: pointer, bufLen: int): int =
|
proc hsReadData(s: FileHandleStream, buffer: pointer, bufLen: int): int =
|
||||||
result = posix.read(s.handle, buffer, bufLen)
|
result = posix.read(s.handle, buffer, bufLen)
|
||||||
inc(s.pos, result)
|
inc(s.pos, result)
|
||||||
|
|
||||||
proc hsWriteData(s: PFileHandleStream, buffer: pointer, bufLen: int) =
|
proc hsWriteData(s: FileHandleStream, buffer: pointer, bufLen: int) =
|
||||||
if posix.write(s.handle, buffer, bufLen) != bufLen:
|
if posix.write(s.handle, buffer, bufLen) != bufLen:
|
||||||
raise newEIO("cannot write to stream")
|
raise newEIO("cannot write to stream")
|
||||||
inc(s.pos, bufLen)
|
inc(s.pos, bufLen)
|
||||||
|
|
||||||
proc newFileHandleStream*(handle: TFileHandle): PFileHandleStream =
|
proc newFileHandleStream*(handle: FileHandle): FileHandleStream =
|
||||||
new(result)
|
new(result)
|
||||||
result.handle = handle
|
result.handle = handle
|
||||||
result.pos = 0
|
result.pos = 0
|
||||||
|
|
@ -338,9 +345,9 @@ else:
|
||||||
result.writeData = hsWriteData
|
result.writeData = hsWriteData
|
||||||
|
|
||||||
proc newFileHandleStream*(filename: string,
|
proc newFileHandleStream*(filename: string,
|
||||||
mode: TFileMode): PFileHandleStream =
|
mode: FileMode): FileHandleStream =
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
nil
|
discard
|
||||||
else:
|
else:
|
||||||
var flags: cint
|
var flags: cint
|
||||||
case mode
|
case mode
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -18,20 +18,23 @@ import
|
||||||
include "system/inclrtl"
|
include "system/inclrtl"
|
||||||
|
|
||||||
type
|
type
|
||||||
TStringTableMode* = enum ## describes the tables operation mode
|
StringTableMode* = enum ## describes the tables operation mode
|
||||||
modeCaseSensitive, ## the table is case sensitive
|
modeCaseSensitive, ## the table is case sensitive
|
||||||
modeCaseInsensitive, ## the table is case insensitive
|
modeCaseInsensitive, ## the table is case insensitive
|
||||||
modeStyleInsensitive ## the table is style insensitive
|
modeStyleInsensitive ## the table is style insensitive
|
||||||
TKeyValuePair = tuple[key, val: string]
|
KeyValuePair = tuple[key, val: string]
|
||||||
TKeyValuePairSeq = seq[TKeyValuePair]
|
KeyValuePairSeq = seq[KeyValuePair]
|
||||||
TStringTable* = object of RootObj
|
StringTableObj* = object of RootObj
|
||||||
counter: int
|
counter: int
|
||||||
data: TKeyValuePairSeq
|
data: KeyValuePairSeq
|
||||||
mode: TStringTableMode
|
mode: StringTableMode
|
||||||
|
|
||||||
PStringTable* = ref TStringTable ## use this type to declare string tables
|
StringTableRef* = ref StringTableObj ## use this type to declare string tables
|
||||||
|
|
||||||
proc len*(t: PStringTable): int {.rtl, extern: "nst$1".} =
|
{.deprecated: [TStringTableMode: StringTableMode,
|
||||||
|
TStringTable: StringTableObj, PStringTable: StringTableRef].}
|
||||||
|
|
||||||
|
proc len*(t: StringTableRef): int {.rtl, extern: "nst$1".} =
|
||||||
## returns the number of keys in `t`.
|
## returns the number of keys in `t`.
|
||||||
result = t.counter
|
result = t.counter
|
||||||
|
|
||||||
|
|
@ -54,7 +57,7 @@ iterator values*(t: PStringTable): string =
|
||||||
yield t.data[h].val
|
yield t.data[h].val
|
||||||
|
|
||||||
type
|
type
|
||||||
TFormatFlag* = enum ## flags for the `%` operator
|
FormatFlag* = enum ## flags for the `%` operator
|
||||||
useEnvironment, ## use environment variable if the ``$key``
|
useEnvironment, ## use environment variable if the ``$key``
|
||||||
## is not found in the table
|
## is not found in the table
|
||||||
useEmpty, ## use the empty string as a default, thus it
|
useEmpty, ## use the empty string as a default, thus it
|
||||||
|
|
@ -63,6 +66,8 @@ type
|
||||||
useKey ## do not replace ``$key`` if it is not found
|
useKey ## do not replace ``$key`` if it is not found
|
||||||
## in the table (or in the environment)
|
## in the table (or in the environment)
|
||||||
|
|
||||||
|
{.deprecated: [TFormatFlag: FormatFlag].}
|
||||||
|
|
||||||
# implementation
|
# implementation
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -23,7 +23,7 @@ import parseutils
|
||||||
include "system/inclrtl"
|
include "system/inclrtl"
|
||||||
|
|
||||||
type
|
type
|
||||||
TCharSet* = set[char] # for compatibility with Nim
|
TCharSet* {.deprecated.} = set[char] # for compatibility with Nim
|
||||||
|
|
||||||
const
|
const
|
||||||
Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'}
|
Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'}
|
||||||
|
|
@ -54,7 +54,7 @@ const
|
||||||
## make the `find() proc <#find,string,set[char],int>`_ find **invalid**
|
## make the `find() proc <#find,string,set[char],int>`_ find **invalid**
|
||||||
## characters in strings. Example:
|
## characters in strings. Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## let invalid = AllChars - Digits
|
## let invalid = AllChars - Digits
|
||||||
## doAssert "01234".find(invalid) == -1
|
## doAssert "01234".find(invalid) == -1
|
||||||
## doAssert "01A34".find(invalid) == 2
|
## doAssert "01A34".find(invalid) == 2
|
||||||
|
|
@ -205,7 +205,7 @@ iterator split*(s: string, seps: set[char] = Whitespace): string =
|
||||||
## a single split point and leading/trailing separators will be ignored.
|
## a single split point and leading/trailing separators will be ignored.
|
||||||
## The following example:
|
## The following example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## for word in split(" this is an example "):
|
## for word in split(" this is an example "):
|
||||||
## writeln(stdout, word)
|
## writeln(stdout, word)
|
||||||
##
|
##
|
||||||
|
|
@ -219,13 +219,13 @@ iterator split*(s: string, seps: set[char] = Whitespace): string =
|
||||||
##
|
##
|
||||||
## And the following code:
|
## And the following code:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## for word in split(";;this;is;an;;example;;;", {';'}):
|
## for word in split(";;this;is;an;;example;;;", {';'}):
|
||||||
## writeln(stdout, word)
|
## writeln(stdout, word)
|
||||||
##
|
##
|
||||||
## ...produces the same output as the first example. The code:
|
## ...produces the same output as the first example. The code:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## let date = "2012-11-20T22:08:08.398990"
|
## let date = "2012-11-20T22:08:08.398990"
|
||||||
## let separators = {' ', '-', ':', 'T'}
|
## let separators = {' ', '-', ':', 'T'}
|
||||||
## for number in split(date, separators):
|
## for number in split(date, separators):
|
||||||
|
|
@ -258,7 +258,7 @@ iterator split*(s: string, sep: char): string =
|
||||||
## characters, this proc will not coalesce groups of the
|
## characters, this proc will not coalesce groups of the
|
||||||
## separator, returning a string for each found character. The code:
|
## separator, returning a string for each found character. The code:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## for word in split(";;this;is;an;;example;;;", ';'):
|
## for word in split(";;this;is;an;;example;;;", ';'):
|
||||||
## writeln(stdout, word)
|
## writeln(stdout, word)
|
||||||
##
|
##
|
||||||
|
|
@ -308,13 +308,13 @@ iterator splitLines*(s: string): string =
|
||||||
##
|
##
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## for line in splitLines("\nthis\nis\nan\n\nexample\n"):
|
## for line in splitLines("\nthis\nis\nan\n\nexample\n"):
|
||||||
## writeln(stdout, line)
|
## writeln(stdout, line)
|
||||||
##
|
##
|
||||||
## Results in:
|
## Results in:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## ""
|
## ""
|
||||||
## "this"
|
## "this"
|
||||||
## "is"
|
## "is"
|
||||||
|
|
@ -417,7 +417,7 @@ proc parseInt*(s: string): int {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuParseInt".} =
|
rtl, extern: "nsuParseInt".} =
|
||||||
## Parses a decimal integer value contained in `s`.
|
## Parses a decimal integer value contained in `s`.
|
||||||
##
|
##
|
||||||
## If `s` is not a valid integer, `EInvalidValue` is raised.
|
## If `s` is not a valid integer, `ValueError` is raised.
|
||||||
var L = parseutils.parseInt(s, result, 0)
|
var L = parseutils.parseInt(s, result, 0)
|
||||||
if L != s.len or L == 0:
|
if L != s.len or L == 0:
|
||||||
raise newException(ValueError, "invalid integer: " & s)
|
raise newException(ValueError, "invalid integer: " & s)
|
||||||
|
|
@ -426,7 +426,7 @@ proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuParseBiggestInt".} =
|
rtl, extern: "nsuParseBiggestInt".} =
|
||||||
## Parses a decimal integer value contained in `s`.
|
## Parses a decimal integer value contained in `s`.
|
||||||
##
|
##
|
||||||
## If `s` is not a valid integer, `EInvalidValue` is raised.
|
## If `s` is not a valid integer, `ValueError` is raised.
|
||||||
var L = parseutils.parseBiggestInt(s, result, 0)
|
var L = parseutils.parseBiggestInt(s, result, 0)
|
||||||
if L != s.len or L == 0:
|
if L != s.len or L == 0:
|
||||||
raise newException(ValueError, "invalid integer: " & s)
|
raise newException(ValueError, "invalid integer: " & s)
|
||||||
|
|
@ -434,7 +434,7 @@ proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect, procvar,
|
||||||
proc parseFloat*(s: string): float {.noSideEffect, procvar,
|
proc parseFloat*(s: string): float {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuParseFloat".} =
|
rtl, extern: "nsuParseFloat".} =
|
||||||
## Parses a decimal floating point value contained in `s`. If `s` is not
|
## Parses a decimal floating point value contained in `s`. If `s` is not
|
||||||
## a valid floating point number, `EInvalidValue` is raised. ``NAN``,
|
## a valid floating point number, `ValueError` is raised. ``NAN``,
|
||||||
## ``INF``, ``-INF`` are also supported (case insensitive comparison).
|
## ``INF``, ``-INF`` are also supported (case insensitive comparison).
|
||||||
var L = parseutils.parseFloat(s, result, 0)
|
var L = parseutils.parseFloat(s, result, 0)
|
||||||
if L != s.len or L == 0:
|
if L != s.len or L == 0:
|
||||||
|
|
@ -444,7 +444,7 @@ proc parseHexInt*(s: string): int {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuParseHexInt".} =
|
rtl, extern: "nsuParseHexInt".} =
|
||||||
## Parses a hexadecimal integer value contained in `s`.
|
## Parses a hexadecimal integer value contained in `s`.
|
||||||
##
|
##
|
||||||
## If `s` is not a valid integer, `EInvalidValue` is raised. `s` can have one
|
## If `s` is not a valid integer, `ValueError` is raised. `s` can have one
|
||||||
## of the following optional prefixes: ``0x``, ``0X``, ``#``. Underscores
|
## of the following optional prefixes: ``0x``, ``0X``, ``#``. Underscores
|
||||||
## within `s` are ignored.
|
## within `s` are ignored.
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
@ -471,7 +471,7 @@ proc parseBool*(s: string): bool =
|
||||||
## If ``s`` is one of the following values: ``y, yes, true, 1, on``, then
|
## If ``s`` is one of the following values: ``y, yes, true, 1, on``, then
|
||||||
## returns `true`. If ``s`` is one of the following values: ``n, no, false,
|
## returns `true`. If ``s`` is one of the following values: ``n, no, false,
|
||||||
## 0, off``, then returns `false`. If ``s`` is something else a
|
## 0, off``, then returns `false`. If ``s`` is something else a
|
||||||
## ``EInvalidValue`` exception is raised.
|
## ``ValueError`` exception is raised.
|
||||||
case normalize(s)
|
case normalize(s)
|
||||||
of "y", "yes", "true", "1", "on": result = true
|
of "y", "yes", "true", "1", "on": result = true
|
||||||
of "n", "no", "false", "0", "off": result = false
|
of "n", "no", "false", "0", "off": result = false
|
||||||
|
|
@ -480,7 +480,7 @@ proc parseBool*(s: string): bool =
|
||||||
proc parseEnum*[T: enum](s: string): T =
|
proc parseEnum*[T: enum](s: string): T =
|
||||||
## Parses an enum ``T``.
|
## Parses an enum ``T``.
|
||||||
##
|
##
|
||||||
## Raises ``EInvalidValue`` for an invalid value in `s`. The comparison is
|
## Raises ``ValueError`` for an invalid value in `s`. The comparison is
|
||||||
## done in a style insensitive way.
|
## done in a style insensitive way.
|
||||||
for e in low(T)..high(T):
|
for e in low(T)..high(T):
|
||||||
if cmpIgnoreStyle(s, $e) == 0:
|
if cmpIgnoreStyle(s, $e) == 0:
|
||||||
|
|
@ -502,7 +502,7 @@ proc repeatChar*(count: int, c: char = ' '): string {.noSideEffect,
|
||||||
## Returns a string of length `count` consisting only of
|
## Returns a string of length `count` consisting only of
|
||||||
## the character `c`. You can use this proc to left align strings. Example:
|
## the character `c`. You can use this proc to left align strings. Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## let
|
## let
|
||||||
## width = 15
|
## width = 15
|
||||||
## text1 = "Hello user!"
|
## text1 = "Hello user!"
|
||||||
|
|
@ -527,7 +527,7 @@ proc align*(s: string, count: int, padding = ' '): string {.
|
||||||
## returned unchanged. If you need to left align a string use the `repeatChar
|
## returned unchanged. If you need to left align a string use the `repeatChar
|
||||||
## proc <#repeatChar>`_. Example:
|
## proc <#repeatChar>`_. Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## assert align("abc", 4) == " abc"
|
## assert align("abc", 4) == " abc"
|
||||||
## assert align("a", 0) == "a"
|
## assert align("a", 0) == "a"
|
||||||
## assert align("1232", 6) == " 1232"
|
## assert align("1232", 6) == " 1232"
|
||||||
|
|
@ -547,13 +547,13 @@ iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[
|
||||||
## Substrings are separated by a substring containing only `seps`.
|
## Substrings are separated by a substring containing only `seps`.
|
||||||
## Examples:
|
## Examples:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## for word in tokenize(" this is an example "):
|
## for word in tokenize(" this is an example "):
|
||||||
## writeln(stdout, word)
|
## writeln(stdout, word)
|
||||||
##
|
##
|
||||||
## Results in:
|
## Results in:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## (" ", true)
|
## (" ", true)
|
||||||
## ("this", false)
|
## ("this", false)
|
||||||
## (" ", true)
|
## (" ", true)
|
||||||
|
|
@ -676,7 +676,7 @@ proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect,
|
||||||
##
|
##
|
||||||
## A shorthand for:
|
## A shorthand for:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## if dest.len > startLen: add(dest, sep)
|
## if dest.len > startLen: add(dest, sep)
|
||||||
##
|
##
|
||||||
## This is often useful for generating some code where the items need to
|
## This is often useful for generating some code where the items need to
|
||||||
|
|
@ -684,7 +684,7 @@ proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect,
|
||||||
## `startLen`. The following example creates a string describing
|
## `startLen`. The following example creates a string describing
|
||||||
## an array of integers:
|
## an array of integers:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## var arr = "["
|
## var arr = "["
|
||||||
## for x in items([2, 3, 5, 7, 11]):
|
## for x in items([2, 3, 5, 7, 11]):
|
||||||
## addSep(arr, startLen=len("["))
|
## addSep(arr, startLen=len("["))
|
||||||
|
|
@ -692,7 +692,7 @@ proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect,
|
||||||
## add(arr, "]")
|
## add(arr, "]")
|
||||||
if dest.len > startLen: add(dest, sep)
|
if dest.len > startLen: add(dest, sep)
|
||||||
|
|
||||||
proc allCharsInSet*(s: string, theSet: TCharSet): bool =
|
proc allCharsInSet*(s: string, theSet: set[char]): bool =
|
||||||
## Returns true iff each character of `s` is in the set `theSet`.
|
## Returns true iff each character of `s` is in the set `theSet`.
|
||||||
for c in items(s):
|
for c in items(s):
|
||||||
if c notin theSet: return false
|
if c notin theSet: return false
|
||||||
|
|
@ -739,14 +739,14 @@ proc join*(a: openArray[string]): string {.
|
||||||
result = ""
|
result = ""
|
||||||
|
|
||||||
type
|
type
|
||||||
TSkipTable = array[char, int]
|
SkipTable = array[char, int]
|
||||||
|
|
||||||
proc preprocessSub(sub: string, a: var TSkipTable) =
|
proc preprocessSub(sub: string, a: var SkipTable) =
|
||||||
var m = len(sub)
|
var m = len(sub)
|
||||||
for i in 0..0xff: a[chr(i)] = m+1
|
for i in 0..0xff: a[chr(i)] = m+1
|
||||||
for i in 0..m-1: a[sub[i]] = m-i
|
for i in 0..m-1: a[sub[i]] = m-i
|
||||||
|
|
||||||
proc findAux(s, sub: string, start: int, a: TSkipTable): int =
|
proc findAux(s, sub: string, start: int, a: SkipTable): int =
|
||||||
# Fast "quick search" algorithm:
|
# Fast "quick search" algorithm:
|
||||||
var
|
var
|
||||||
m = len(sub)
|
m = len(sub)
|
||||||
|
|
@ -762,7 +762,7 @@ proc findAux(s, sub: string, start: int, a: TSkipTable): int =
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
proc find*(s, sub: string, start: int = 0): int {.noSideEffect,
|
proc find*(s, sub: string, start: int = 0): int {.noSideEffect,
|
||||||
rtl, extern: "nsuFindStr", operator: 6.} =
|
rtl, extern: "nsuFindStr".} =
|
||||||
## Searches for `sub` in `s` starting at position `start`.
|
## Searches for `sub` in `s` starting at position `start`.
|
||||||
##
|
##
|
||||||
## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
|
## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
|
||||||
|
|
@ -827,9 +827,9 @@ proc contains*(s: string, chars: set[char]): bool {.noSideEffect.} =
|
||||||
return find(s, chars) >= 0
|
return find(s, chars) >= 0
|
||||||
|
|
||||||
proc replace*(s, sub: string, by = ""): string {.noSideEffect,
|
proc replace*(s, sub: string, by = ""): string {.noSideEffect,
|
||||||
rtl, extern: "nsuReplaceStr", operator: 1.} =
|
rtl, extern: "nsuReplaceStr".} =
|
||||||
## Replaces `sub` in `s` by the string `by`.
|
## Replaces `sub` in `s` by the string `by`.
|
||||||
var a {.noinit.}: TSkipTable
|
var a {.noinit.}: SkipTable
|
||||||
result = ""
|
result = ""
|
||||||
preprocessSub(sub, a)
|
preprocessSub(sub, a)
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
@ -862,7 +862,7 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
|
||||||
## (comparable to ``\\w`` in regular expressions), otherwise it is not
|
## (comparable to ``\\w`` in regular expressions), otherwise it is not
|
||||||
## replaced.
|
## replaced.
|
||||||
const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
|
const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
|
||||||
var a {.noinit.}: TSkipTable
|
var a {.noinit.}: SkipTable
|
||||||
result = ""
|
result = ""
|
||||||
preprocessSub(sub, a)
|
preprocessSub(sub, a)
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
@ -899,7 +899,7 @@ proc parseOctInt*(s: string): int {.noSideEffect,
|
||||||
rtl, extern: "nsuParseOctInt".} =
|
rtl, extern: "nsuParseOctInt".} =
|
||||||
## Parses an octal integer value contained in `s`.
|
## Parses an octal integer value contained in `s`.
|
||||||
##
|
##
|
||||||
## If `s` is not a valid integer, `EInvalidValue` is raised. `s` can have one
|
## If `s` is not a valid integer, `ValueError` is raised. `s` can have one
|
||||||
## of the following optional prefixes: ``0o``, ``0O``. Underscores within
|
## of the following optional prefixes: ``0o``, ``0O``. Underscores within
|
||||||
## `s` are ignored.
|
## `s` are ignored.
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
@ -999,7 +999,7 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
|
||||||
## operations.
|
## operations.
|
||||||
##
|
##
|
||||||
## If `s` does not begin with ``prefix`` and end with ``suffix`` a
|
## If `s` does not begin with ``prefix`` and end with ``suffix`` a
|
||||||
## EInvalidValue exception will be raised.
|
## ValueError exception will be raised.
|
||||||
result = newStringOfCap(s.len)
|
result = newStringOfCap(s.len)
|
||||||
var i = 0
|
var i = 0
|
||||||
if s[0 .. prefix.len-1] != prefix:
|
if s[0 .. prefix.len-1] != prefix:
|
||||||
|
|
@ -1138,12 +1138,14 @@ proc c_sprintf(buf, frmt: cstring) {.header: "<stdio.h>", importc: "sprintf",
|
||||||
varargs, noSideEffect.}
|
varargs, noSideEffect.}
|
||||||
|
|
||||||
type
|
type
|
||||||
TFloatFormat* = enum ## the different modes of floating point formating
|
FloatFormatMode* = enum ## the different modes of floating point formating
|
||||||
ffDefault, ## use the shorter floating point notation
|
ffDefault, ## use the shorter floating point notation
|
||||||
ffDecimal, ## use decimal floating point notation
|
ffDecimal, ## use decimal floating point notation
|
||||||
ffScientific ## use scientific notation (using ``e`` character)
|
ffScientific ## use scientific notation (using ``e`` character)
|
||||||
|
|
||||||
proc formatBiggestFloat*(f: BiggestFloat, format: TFloatFormat = ffDefault,
|
{.deprecated: [TFloatFormat: FloatFormatMode].}
|
||||||
|
|
||||||
|
proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
|
||||||
precision: range[0..32] = 16): string {.
|
precision: range[0..32] = 16): string {.
|
||||||
noSideEffect, operator: 2, rtl, extern: "nsu$1".} =
|
noSideEffect, operator: 2, rtl, extern: "nsu$1".} =
|
||||||
## Converts a floating point value `f` to a string.
|
## Converts a floating point value `f` to a string.
|
||||||
|
|
@ -1174,7 +1176,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: TFloatFormat = ffDefault,
|
||||||
c_sprintf(buf, frmtstr, f)
|
c_sprintf(buf, frmtstr, f)
|
||||||
result = $buf
|
result = $buf
|
||||||
|
|
||||||
proc formatFloat*(f: float, format: TFloatFormat = ffDefault,
|
proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
|
||||||
precision: range[0..32] = 16): string {.
|
precision: range[0..32] = 16): string {.
|
||||||
noSideEffect, operator: 2, rtl, extern: "nsu$1".} =
|
noSideEffect, operator: 2, rtl, extern: "nsu$1".} =
|
||||||
## Converts a floating point value `f` to a string.
|
## Converts a floating point value `f` to a string.
|
||||||
|
|
@ -1190,7 +1192,7 @@ proc formatFloat*(f: float, format: TFloatFormat = ffDefault,
|
||||||
proc formatSize*(bytes: BiggestInt, decimalSep = '.'): string =
|
proc formatSize*(bytes: BiggestInt, decimalSep = '.'): string =
|
||||||
## Rounds and formats `bytes`. Examples:
|
## Rounds and formats `bytes`. Examples:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
##
|
##
|
||||||
## formatSize(1'i64 shl 31 + 300'i64) == "2.204GB"
|
## formatSize(1'i64 shl 31 + 300'i64) == "2.204GB"
|
||||||
## formatSize(4096) == "4KB"
|
## formatSize(4096) == "4KB"
|
||||||
|
|
@ -1280,12 +1282,12 @@ proc `%` *(formatstr: string, a: openArray[string]): string {.noSideEffect,
|
||||||
##
|
##
|
||||||
## This is best explained by an example:
|
## This is best explained by an example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## "$1 eats $2." % ["The cat", "fish"]
|
## "$1 eats $2." % ["The cat", "fish"]
|
||||||
##
|
##
|
||||||
## Results in:
|
## Results in:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## "The cat eats fish."
|
## "The cat eats fish."
|
||||||
##
|
##
|
||||||
## The substitution variables (the thing after the ``$``) are enumerated
|
## The substitution variables (the thing after the ``$``) are enumerated
|
||||||
|
|
@ -1294,7 +1296,7 @@ proc `%` *(formatstr: string, a: openArray[string]): string {.noSideEffect,
|
||||||
## The notation ``$#`` can be used to refer to the next substitution
|
## The notation ``$#`` can be used to refer to the next substitution
|
||||||
## variable:
|
## variable:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## "$# eats $#." % ["The cat", "fish"]
|
## "$# eats $#." % ["The cat", "fish"]
|
||||||
##
|
##
|
||||||
## Substitution variables can also be words (that is
|
## Substitution variables can also be words (that is
|
||||||
|
|
@ -1302,15 +1304,15 @@ proc `%` *(formatstr: string, a: openArray[string]): string {.noSideEffect,
|
||||||
## indices are keys and with odd indices are the corresponding values.
|
## indices are keys and with odd indices are the corresponding values.
|
||||||
## An example:
|
## An example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## "$animal eats $food." % ["animal", "The cat", "food", "fish"]
|
## "$animal eats $food." % ["animal", "The cat", "food", "fish"]
|
||||||
##
|
##
|
||||||
## Results in:
|
## Results in:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nim
|
||||||
## "The cat eats fish."
|
## "The cat eats fish."
|
||||||
##
|
##
|
||||||
## The variables are compared with `cmpIgnoreStyle`. `EInvalidValue` is
|
## The variables are compared with `cmpIgnoreStyle`. `ValueError` is
|
||||||
## raised if an ill-formed format string has been passed to the `%` operator.
|
## raised if an ill-formed format string has been passed to the `%` operator.
|
||||||
result = newStringOfCap(formatstr.len + a.len shl 4)
|
result = newStringOfCap(formatstr.len + a.len shl 4)
|
||||||
addf(result, formatstr, a)
|
addf(result, formatstr, a)
|
||||||
|
|
@ -1350,7 +1352,7 @@ when isMainModule:
|
||||||
doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz "
|
doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz "
|
||||||
doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc"
|
doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc"
|
||||||
|
|
||||||
type TMyEnum = enum enA, enB, enC, enuD, enE
|
type MyEnum = enum enA, enB, enC, enuD, enE
|
||||||
doAssert parseEnum[TMyEnum]("enu_D") == enuD
|
doAssert parseEnum[MyEnum]("enu_D") == enuD
|
||||||
|
|
||||||
doAssert parseEnum("invalid enum value", enC) == enC
|
doAssert parseEnum("invalid enum value", enC) == enC
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## Nimrod support for `substitution expressions`:idx: (`subex`:idx:).
|
## Nim support for `substitution expressions`:idx: (`subex`:idx:).
|
||||||
##
|
##
|
||||||
## .. include:: ../doc/subexes.txt
|
## .. include:: ../doc/subexes.txt
|
||||||
##
|
##
|
||||||
|
|
@ -28,11 +28,13 @@ proc findNormalized(x: string, inArray: openarray[string]): int =
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
type
|
type
|
||||||
EInvalidSubex* = object of EInvalidValue ## exception that is raised for
|
SubexError* = object of ValueError ## exception that is raised for
|
||||||
## an invalid subex
|
## an invalid subex
|
||||||
|
|
||||||
|
{.deprecated: [EInvalidSubex: SubexError].}
|
||||||
|
|
||||||
proc raiseInvalidFormat(msg: string) {.noinline.} =
|
proc raiseInvalidFormat(msg: string) {.noinline.} =
|
||||||
raise newException(EInvalidSubex, "invalid format string: " & msg)
|
raise newException(SubexError, "invalid format string: " & msg)
|
||||||
|
|
||||||
type
|
type
|
||||||
TFormatParser = object {.pure, final.}
|
TFormatParser = object {.pure, final.}
|
||||||
|
|
@ -291,14 +293,16 @@ proc scanDollar(p: var TFormatParser, a: openarray[string], s: var string) =
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TSubex* = distinct string ## string that contains a substitution expression
|
Subex* = distinct string ## string that contains a substitution expression
|
||||||
|
|
||||||
proc subex*(s: string): TSubex =
|
{.deprecated: [TSubex: Subex].}
|
||||||
|
|
||||||
|
proc subex*(s: string): Subex =
|
||||||
## constructs a *substitution expression* from `s`. Currently this performs
|
## constructs a *substitution expression* from `s`. Currently this performs
|
||||||
## no syntax checking but this may change in later versions.
|
## no syntax checking but this may change in later versions.
|
||||||
result = TSubex(s)
|
result = Subex(s)
|
||||||
|
|
||||||
proc addf*(s: var string, formatstr: TSubex, a: varargs[string, `$`]) {.
|
proc addf*(s: var string, formatstr: Subex, a: varargs[string, `$`]) {.
|
||||||
noSideEffect, rtl, extern: "nfrmtAddf".} =
|
noSideEffect, rtl, extern: "nfrmtAddf".} =
|
||||||
## The same as ``add(s, formatstr % a)``, but more efficient.
|
## The same as ``add(s, formatstr % a)``, but more efficient.
|
||||||
var p: TFormatParser
|
var p: TFormatParser
|
||||||
|
|
@ -312,7 +316,7 @@ proc addf*(s: var string, formatstr: TSubex, a: varargs[string, `$`]) {.
|
||||||
emitChar(p, s, p.f[i])
|
emitChar(p, s, p.f[i])
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
||||||
proc `%` *(formatstr: TSubex, a: openarray[string]): string {.noSideEffect,
|
proc `%` *(formatstr: Subex, a: openarray[string]): string {.noSideEffect,
|
||||||
rtl, extern: "nfrmtFormatOpenArray".} =
|
rtl, extern: "nfrmtFormatOpenArray".} =
|
||||||
## The `substitution`:idx: operator performs string substitutions in
|
## The `substitution`:idx: operator performs string substitutions in
|
||||||
## `formatstr` and returns a modified `formatstr`. This is often called
|
## `formatstr` and returns a modified `formatstr`. This is often called
|
||||||
|
|
@ -321,13 +325,13 @@ proc `%` *(formatstr: TSubex, a: openarray[string]): string {.noSideEffect,
|
||||||
result = newStringOfCap(formatstr.string.len + a.len shl 4)
|
result = newStringOfCap(formatstr.string.len + a.len shl 4)
|
||||||
addf(result, formatstr, a)
|
addf(result, formatstr, a)
|
||||||
|
|
||||||
proc `%` *(formatstr: TSubex, a: string): string {.noSideEffect,
|
proc `%` *(formatstr: Subex, a: string): string {.noSideEffect,
|
||||||
rtl, extern: "nfrmtFormatSingleElem".} =
|
rtl, extern: "nfrmtFormatSingleElem".} =
|
||||||
## This is the same as ``formatstr % [a]``.
|
## This is the same as ``formatstr % [a]``.
|
||||||
result = newStringOfCap(formatstr.string.len + a.len)
|
result = newStringOfCap(formatstr.string.len + a.len)
|
||||||
addf(result, formatstr, [a])
|
addf(result, formatstr, [a])
|
||||||
|
|
||||||
proc format*(formatstr: TSubex, a: varargs[string, `$`]): string {.noSideEffect,
|
proc format*(formatstr: Subex, a: varargs[string, `$`]): string {.noSideEffect,
|
||||||
rtl, extern: "nfrmtFormatVarargs".} =
|
rtl, extern: "nfrmtFormatVarargs".} =
|
||||||
## The `substitution`:idx: operator performs string substitutions in
|
## The `substitution`:idx: operator performs string substitutions in
|
||||||
## `formatstr` and returns a modified `formatstr`. This is often called
|
## `formatstr` and returns a modified `formatstr`. This is often called
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -27,11 +27,11 @@ when defined(windows):
|
||||||
var hTemp = GetStdHandle(STD_OUTPUT_HANDLE)
|
var hTemp = GetStdHandle(STD_OUTPUT_HANDLE)
|
||||||
if DuplicateHandle(GetCurrentProcess(), hTemp, GetCurrentProcess(),
|
if DuplicateHandle(GetCurrentProcess(), hTemp, GetCurrentProcess(),
|
||||||
addr(conHandle), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
|
addr(conHandle), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
proc getCursorPos(): tuple [x,y: int] =
|
proc getCursorPos(): tuple [x,y: int] =
|
||||||
var c: TCONSOLESCREENBUFFERINFO
|
var c: TCONSOLESCREENBUFFERINFO
|
||||||
if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0: osError(osLastError())
|
if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0: raiseOSError(osLastError())
|
||||||
return (int(c.dwCursorPosition.x), int(c.dwCursorPosition.y))
|
return (int(c.dwCursorPosition.x), int(c.dwCursorPosition.y))
|
||||||
|
|
||||||
proc getAttributes(): int16 =
|
proc getAttributes(): int16 =
|
||||||
|
|
@ -51,7 +51,7 @@ proc setCursorPos*(x, y: int) =
|
||||||
var c: TCOORD
|
var c: TCOORD
|
||||||
c.x = int16(x)
|
c.x = int16(x)
|
||||||
c.y = int16(y)
|
c.y = int16(y)
|
||||||
if SetConsoleCursorPosition(conHandle, c) == 0: osError(osLastError())
|
if SetConsoleCursorPosition(conHandle, c) == 0: raiseOSError(osLastError())
|
||||||
else:
|
else:
|
||||||
stdout.write("\e[" & $y & ';' & $x & 'f')
|
stdout.write("\e[" & $y & ';' & $x & 'f')
|
||||||
|
|
||||||
|
|
@ -61,10 +61,10 @@ proc setCursorXPos*(x: int) =
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
var scrbuf: TCONSOLESCREENBUFFERINFO
|
var scrbuf: TCONSOLESCREENBUFFERINFO
|
||||||
var hStdout = conHandle
|
var hStdout = conHandle
|
||||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: osError(osLastError())
|
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: raiseOSError(osLastError())
|
||||||
var origin = scrbuf.dwCursorPosition
|
var origin = scrbuf.dwCursorPosition
|
||||||
origin.x = int16(x)
|
origin.x = int16(x)
|
||||||
if SetConsoleCursorPosition(conHandle, origin) == 0: osError(osLastError())
|
if SetConsoleCursorPosition(conHandle, origin) == 0: raiseOSError(osLastError())
|
||||||
else:
|
else:
|
||||||
stdout.write("\e[" & $x & 'G')
|
stdout.write("\e[" & $x & 'G')
|
||||||
|
|
||||||
|
|
@ -75,10 +75,10 @@ when defined(windows):
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
var scrbuf: TCONSOLESCREENBUFFERINFO
|
var scrbuf: TCONSOLESCREENBUFFERINFO
|
||||||
var hStdout = conHandle
|
var hStdout = conHandle
|
||||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: osError(osLastError())
|
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: raiseOSError(osLastError())
|
||||||
var origin = scrbuf.dwCursorPosition
|
var origin = scrbuf.dwCursorPosition
|
||||||
origin.y = int16(y)
|
origin.y = int16(y)
|
||||||
if SetConsoleCursorPosition(conHandle, origin) == 0: osError(osLastError())
|
if SetConsoleCursorPosition(conHandle, origin) == 0: raiseOSError(osLastError())
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
|
@ -155,18 +155,18 @@ proc eraseLine* =
|
||||||
var scrbuf: TCONSOLESCREENBUFFERINFO
|
var scrbuf: TCONSOLESCREENBUFFERINFO
|
||||||
var numwrote: DWORD
|
var numwrote: DWORD
|
||||||
var hStdout = conHandle
|
var hStdout = conHandle
|
||||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: osError(osLastError())
|
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: raiseOSError(osLastError())
|
||||||
var origin = scrbuf.dwCursorPosition
|
var origin = scrbuf.dwCursorPosition
|
||||||
origin.x = 0'i16
|
origin.x = 0'i16
|
||||||
if SetConsoleCursorPosition(conHandle, origin) == 0: osError(osLastError())
|
if SetConsoleCursorPosition(conHandle, origin) == 0: raiseOSError(osLastError())
|
||||||
var ht = scrbuf.dwSize.Y - origin.Y
|
var ht = scrbuf.dwSize.Y - origin.Y
|
||||||
var wt = scrbuf.dwSize.X - origin.X
|
var wt = scrbuf.dwSize.X - origin.X
|
||||||
if FillConsoleOutputCharacter(hStdout,' ', ht*wt,
|
if FillConsoleOutputCharacter(hStdout,' ', ht*wt,
|
||||||
origin, addr(numwrote)) == 0:
|
origin, addr(numwrote)) == 0:
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, ht * wt,
|
if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, ht * wt,
|
||||||
scrbuf.dwCursorPosition, addr(numwrote)) == 0:
|
scrbuf.dwCursorPosition, addr(numwrote)) == 0:
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
else:
|
else:
|
||||||
stdout.write("\e[2K")
|
stdout.write("\e[2K")
|
||||||
setCursorXPos(0)
|
setCursorXPos(0)
|
||||||
|
|
@ -178,14 +178,14 @@ proc eraseScreen* =
|
||||||
var numwrote: DWORD
|
var numwrote: DWORD
|
||||||
var origin: TCOORD # is inititalized to 0, 0
|
var origin: TCOORD # is inititalized to 0, 0
|
||||||
var hStdout = conHandle
|
var hStdout = conHandle
|
||||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: osError(osLastError())
|
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: raiseOSError(osLastError())
|
||||||
if FillConsoleOutputCharacter(hStdout, ' ', scrbuf.dwSize.X*scrbuf.dwSize.Y,
|
if FillConsoleOutputCharacter(hStdout, ' ', scrbuf.dwSize.X*scrbuf.dwSize.Y,
|
||||||
origin, addr(numwrote)) == 0:
|
origin, addr(numwrote)) == 0:
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes,
|
if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes,
|
||||||
scrbuf.dwSize.X * scrbuf.dwSize.Y,
|
scrbuf.dwSize.X * scrbuf.dwSize.Y,
|
||||||
origin, addr(numwrote)) == 0:
|
origin, addr(numwrote)) == 0:
|
||||||
osError(osLastError())
|
raiseOSError(osLastError())
|
||||||
setCursorXPos(0)
|
setCursorXPos(0)
|
||||||
else:
|
else:
|
||||||
stdout.write("\e[2J")
|
stdout.write("\e[2J")
|
||||||
|
|
@ -199,7 +199,7 @@ proc resetAttributes* {.noconv.} =
|
||||||
stdout.write("\e[0m")
|
stdout.write("\e[0m")
|
||||||
|
|
||||||
type
|
type
|
||||||
TStyle* = enum ## different styles for text output
|
Style* = enum ## different styles for text output
|
||||||
styleBright = 1, ## bright text
|
styleBright = 1, ## bright text
|
||||||
styleDim, ## dim text
|
styleDim, ## dim text
|
||||||
styleUnknown, ## unknown
|
styleUnknown, ## unknown
|
||||||
|
|
@ -208,6 +208,8 @@ type
|
||||||
styleReverse = 7, ## unknown
|
styleReverse = 7, ## unknown
|
||||||
styleHidden ## hidden text
|
styleHidden ## hidden text
|
||||||
|
|
||||||
|
{.deprecated: [TStyle: Style].}
|
||||||
|
|
||||||
when not defined(windows):
|
when not defined(windows):
|
||||||
var
|
var
|
||||||
# XXX: These better be thread-local
|
# XXX: These better be thread-local
|
||||||
|
|
@ -244,7 +246,7 @@ proc writeStyled*(txt: string, style: set[TStyle] = {styleBright}) =
|
||||||
stdout.write("\e[" & $ord(gBG) & 'm')
|
stdout.write("\e[" & $ord(gBG) & 'm')
|
||||||
|
|
||||||
type
|
type
|
||||||
TForegroundColor* = enum ## terminal's foreground colors
|
ForegroundColor* = enum ## terminal's foreground colors
|
||||||
fgBlack = 30, ## black
|
fgBlack = 30, ## black
|
||||||
fgRed, ## red
|
fgRed, ## red
|
||||||
fgGreen, ## green
|
fgGreen, ## green
|
||||||
|
|
@ -254,7 +256,7 @@ type
|
||||||
fgCyan, ## cyan
|
fgCyan, ## cyan
|
||||||
fgWhite ## white
|
fgWhite ## white
|
||||||
|
|
||||||
TBackgroundColor* = enum ## terminal's background colors
|
BackgroundColor* = enum ## terminal's background colors
|
||||||
bgBlack = 40, ## black
|
bgBlack = 40, ## black
|
||||||
bgRed, ## red
|
bgRed, ## red
|
||||||
bgGreen, ## green
|
bgGreen, ## green
|
||||||
|
|
@ -264,13 +266,16 @@ type
|
||||||
bgCyan, ## cyan
|
bgCyan, ## cyan
|
||||||
bgWhite ## white
|
bgWhite ## white
|
||||||
|
|
||||||
proc setForegroundColor*(fg: TForegroundColor, bright=false) =
|
{.deprecated: [TForegroundColor: ForegroundColor,
|
||||||
|
TBackgroundColor: BackgroundColor].}
|
||||||
|
|
||||||
|
proc setForegroundColor*(fg: ForegroundColor, bright=false) =
|
||||||
## sets the terminal's foreground color
|
## sets the terminal's foreground color
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
var old = getAttributes() and not 0x0007
|
var old = getAttributes() and not 0x0007
|
||||||
if bright:
|
if bright:
|
||||||
old = old or FOREGROUND_INTENSITY
|
old = old or FOREGROUND_INTENSITY
|
||||||
const lookup: array [TForegroundColor, int] = [
|
const lookup: array [ForegroundColor, int] = [
|
||||||
0,
|
0,
|
||||||
(FOREGROUND_RED),
|
(FOREGROUND_RED),
|
||||||
(FOREGROUND_GREEN),
|
(FOREGROUND_GREEN),
|
||||||
|
|
@ -285,13 +290,13 @@ proc setForegroundColor*(fg: TForegroundColor, bright=false) =
|
||||||
if bright: inc(gFG, 60)
|
if bright: inc(gFG, 60)
|
||||||
stdout.write("\e[" & $gFG & 'm')
|
stdout.write("\e[" & $gFG & 'm')
|
||||||
|
|
||||||
proc setBackgroundColor*(bg: TBackgroundColor, bright=false) =
|
proc setBackgroundColor*(bg: BackgroundColor, bright=false) =
|
||||||
## sets the terminal's background color
|
## sets the terminal's background color
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
var old = getAttributes() and not 0x0070
|
var old = getAttributes() and not 0x0070
|
||||||
if bright:
|
if bright:
|
||||||
old = old or BACKGROUND_INTENSITY
|
old = old or BACKGROUND_INTENSITY
|
||||||
const lookup: array [TBackgroundColor, int] = [
|
const lookup: array [BackgroundColor, int] = [
|
||||||
0,
|
0,
|
||||||
(BACKGROUND_RED),
|
(BACKGROUND_RED),
|
||||||
(BACKGROUND_GREEN),
|
(BACKGROUND_GREEN),
|
||||||
|
|
@ -306,22 +311,22 @@ proc setBackgroundColor*(bg: TBackgroundColor, bright=false) =
|
||||||
if bright: inc(gBG, 60)
|
if bright: inc(gBG, 60)
|
||||||
stdout.write("\e[" & $gBG & 'm')
|
stdout.write("\e[" & $gBG & 'm')
|
||||||
|
|
||||||
proc isatty*(f: TFile): bool =
|
proc isatty*(f: File): bool =
|
||||||
## returns true if `f` is associated with a terminal device.
|
## returns true if `f` is associated with a terminal device.
|
||||||
when defined(posix):
|
when defined(posix):
|
||||||
proc isatty(fildes: TFileHandle): cint {.
|
proc isatty(fildes: FileHandle): cint {.
|
||||||
importc: "isatty", header: "<unistd.h>".}
|
importc: "isatty", header: "<unistd.h>".}
|
||||||
else:
|
else:
|
||||||
proc isatty(fildes: TFileHandle): cint {.
|
proc isatty(fildes: FileHandle): cint {.
|
||||||
importc: "_isatty", header: "<io.h>".}
|
importc: "_isatty", header: "<io.h>".}
|
||||||
|
|
||||||
result = isatty(fileHandle(f)) != 0'i32
|
result = isatty(getFileHandle(f)) != 0'i32
|
||||||
|
|
||||||
proc styledEchoProcessArg(s: string) = write stdout, s
|
proc styledEchoProcessArg(s: string) = write stdout, s
|
||||||
proc styledEchoProcessArg(style: TStyle) = setStyle({style})
|
proc styledEchoProcessArg(style: Style) = setStyle({style})
|
||||||
proc styledEchoProcessArg(style: set[TStyle]) = setStyle style
|
proc styledEchoProcessArg(style: set[Style]) = setStyle style
|
||||||
proc styledEchoProcessArg(color: TForegroundColor) = setForegroundColor color
|
proc styledEchoProcessArg(color: ForegroundColor) = setForegroundColor color
|
||||||
proc styledEchoProcessArg(color: TBackgroundColor) = setBackgroundColor color
|
proc styledEchoProcessArg(color: BackgroundColor) = setBackgroundColor color
|
||||||
|
|
||||||
macro styledEcho*(m: varargs[expr]): stmt =
|
macro styledEcho*(m: varargs[expr]): stmt =
|
||||||
## to be documented.
|
## to be documented.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2013 Andreas Rumpf
|
# (c) Copyright 2013 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -21,9 +21,9 @@ import
|
||||||
include "system/inclrtl"
|
include "system/inclrtl"
|
||||||
|
|
||||||
type
|
type
|
||||||
TMonth* = enum ## represents a month
|
Month* = enum ## represents a month
|
||||||
mJan, mFeb, mMar, mApr, mMay, mJun, mJul, mAug, mSep, mOct, mNov, mDec
|
mJan, mFeb, mMar, mApr, mMay, mJun, mJul, mAug, mSep, mOct, mNov, mDec
|
||||||
TWeekDay* = enum ## represents a weekday
|
WeekDay* = enum ## represents a weekday
|
||||||
dMon, dTue, dWed, dThu, dFri, dSat, dSun
|
dMon, dTue, dWed, dThu, dFri, dSat, dSun
|
||||||
|
|
||||||
var
|
var
|
||||||
|
|
@ -32,12 +32,12 @@ var
|
||||||
|
|
||||||
when defined(posix) and not defined(JS):
|
when defined(posix) and not defined(JS):
|
||||||
type
|
type
|
||||||
TTimeImpl {.importc: "time_t", header: "<sys/time.h>".} = int
|
TimeImpl {.importc: "time_t", header: "<sys/time.h>".} = int
|
||||||
TTime* = distinct TTimeImpl ## distinct type that represents a time
|
Time* = distinct TimeImpl ## distinct type that represents a time
|
||||||
## measured as number of seconds since the epoch
|
## measured as number of seconds since the epoch
|
||||||
|
|
||||||
Ttimeval {.importc: "struct timeval", header: "<sys/select.h>",
|
Timeval {.importc: "struct timeval",
|
||||||
final, pure.} = object ## struct timeval
|
header: "<sys/select.h>".} = object ## struct timeval
|
||||||
tv_sec: int ## Seconds.
|
tv_sec: int ## Seconds.
|
||||||
tv_usec: int ## Microseconds.
|
tv_usec: int ## Microseconds.
|
||||||
|
|
||||||
|
|
@ -45,7 +45,7 @@ when defined(posix) and not defined(JS):
|
||||||
# Ok, we could, but I don't want circular dependencies.
|
# Ok, we could, but I don't want circular dependencies.
|
||||||
# And gettimeofday() is not defined in the posix module anyway. Sigh.
|
# And gettimeofday() is not defined in the posix module anyway. Sigh.
|
||||||
|
|
||||||
proc posix_gettimeofday(tp: var Ttimeval, unused: pointer = nil) {.
|
proc posix_gettimeofday(tp: var Timeval, unused: pointer = nil) {.
|
||||||
importc: "gettimeofday", header: "<sys/time.h>".}
|
importc: "gettimeofday", header: "<sys/time.h>".}
|
||||||
|
|
||||||
elif defined(windows):
|
elif defined(windows):
|
||||||
|
|
@ -53,16 +53,16 @@ elif defined(windows):
|
||||||
|
|
||||||
when defined(vcc):
|
when defined(vcc):
|
||||||
# newest version of Visual C++ defines time_t to be of 64 bits
|
# newest version of Visual C++ defines time_t to be of 64 bits
|
||||||
type TTimeImpl {.importc: "time_t", header: "<time.h>".} = int64
|
type TimeImpl {.importc: "time_t", header: "<time.h>".} = int64
|
||||||
else:
|
else:
|
||||||
type TTimeImpl {.importc: "time_t", header: "<time.h>".} = int32
|
type TimeImpl {.importc: "time_t", header: "<time.h>".} = int32
|
||||||
|
|
||||||
type
|
type
|
||||||
TTime* = distinct TTimeImpl
|
Time* = distinct TimeImpl
|
||||||
|
|
||||||
elif defined(JS):
|
elif defined(JS):
|
||||||
type
|
type
|
||||||
TTime* {.final, importc.} = object
|
Time* {.importc.} = object
|
||||||
getDay: proc (): int {.tags: [], raises: [], gcsafe.}
|
getDay: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
getFullYear: proc (): int {.tags: [], raises: [], gcsafe.}
|
getFullYear: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
getHours: proc (): int {.tags: [], raises: [], gcsafe.}
|
getHours: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
|
|
@ -82,7 +82,7 @@ elif defined(JS):
|
||||||
getUTCSeconds: proc (): int {.tags: [], raises: [], gcsafe.}
|
getUTCSeconds: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
getUTCDay: proc (): int {.tags: [], raises: [], gcsafe.}
|
getUTCDay: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
getYear: proc (): int {.tags: [], raises: [], gcsafe.}
|
getYear: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||||
parse: proc (s: cstring): TTime {.tags: [], raises: [], gcsafe.}
|
parse: proc (s: cstring): Time {.tags: [], raises: [], gcsafe.}
|
||||||
setDate: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
setDate: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
||||||
setFullYear: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
setFullYear: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
||||||
setHours: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
setHours: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
||||||
|
|
@ -103,7 +103,7 @@ elif defined(JS):
|
||||||
toLocaleString: proc (): cstring {.tags: [], raises: [], gcsafe.}
|
toLocaleString: proc (): cstring {.tags: [], raises: [], gcsafe.}
|
||||||
|
|
||||||
type
|
type
|
||||||
TTimeInfo* = object of RootObj ## represents a time in different parts
|
TimeInfo* = object of RootObj ## represents a time in different parts
|
||||||
second*: range[0..61] ## The number of seconds after the minute,
|
second*: range[0..61] ## The number of seconds after the minute,
|
||||||
## normally in the range 0 to 59, but can
|
## normally in the range 0 to 59, but can
|
||||||
## be up to 61 to allow for leap seconds.
|
## be up to 61 to allow for leap seconds.
|
||||||
|
|
@ -112,9 +112,9 @@ type
|
||||||
hour*: range[0..23] ## The number of hours past midnight,
|
hour*: range[0..23] ## The number of hours past midnight,
|
||||||
## in the range 0 to 23.
|
## in the range 0 to 23.
|
||||||
monthday*: range[1..31] ## The day of the month, in the range 1 to 31.
|
monthday*: range[1..31] ## The day of the month, in the range 1 to 31.
|
||||||
month*: TMonth ## The current month.
|
month*: Month ## The current month.
|
||||||
year*: range[-10_000..10_000] ## The current year.
|
year*: range[-10_000..10_000] ## The current year.
|
||||||
weekday*: TWeekDay ## The current day of the week.
|
weekday*: WeekDay ## The current day of the week.
|
||||||
yearday*: range[0..365] ## The number of days since January 1,
|
yearday*: range[0..365] ## The number of days since January 1,
|
||||||
## in the range 0 to 365.
|
## in the range 0 to 365.
|
||||||
## Always 0 if the target is JS.
|
## Always 0 if the target is JS.
|
||||||
|
|
@ -127,7 +127,7 @@ type
|
||||||
## I make some assumptions about the data in here. Either
|
## I make some assumptions about the data in here. Either
|
||||||
## everything should be positive or everything negative. Zero is
|
## everything should be positive or everything negative. Zero is
|
||||||
## fine too. Mixed signs will lead to unexpected results.
|
## fine too. Mixed signs will lead to unexpected results.
|
||||||
TTimeInterval* {.pure.} = object ## a time interval
|
TimeInterval* = object ## a time interval
|
||||||
miliseconds*: int ## The number of miliseconds
|
miliseconds*: int ## The number of miliseconds
|
||||||
seconds*: int ## The number of seconds
|
seconds*: int ## The number of seconds
|
||||||
minutes*: int ## The number of minutes
|
minutes*: int ## The number of minutes
|
||||||
|
|
@ -136,55 +136,58 @@ type
|
||||||
months*: int ## The number of months
|
months*: int ## The number of months
|
||||||
years*: int ## The number of years
|
years*: int ## The number of years
|
||||||
|
|
||||||
proc getTime*(): TTime {.tags: [TimeEffect], gcsafe.}
|
{.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time, TTimeval: Timeval,
|
||||||
|
TTimeInterval: TimeInterval, TTimeInfo: TimeInfo].}
|
||||||
|
|
||||||
|
proc getTime*(): Time {.tags: [TimeEffect], gcsafe.}
|
||||||
## gets the current calendar time as a UNIX epoch value (number of seconds
|
## gets the current calendar time as a UNIX epoch value (number of seconds
|
||||||
## elapsed since 1970) with integer precission. Use epochTime for higher
|
## elapsed since 1970) with integer precission. Use epochTime for higher
|
||||||
## resolution.
|
## resolution.
|
||||||
proc getLocalTime*(t: TTime): TTimeInfo {.tags: [TimeEffect], raises: [], gcsafe.}
|
proc getLocalTime*(t: Time): TimeInfo {.tags: [TimeEffect], raises: [], gcsafe.}
|
||||||
## converts the calendar time `t` to broken-time representation,
|
## converts the calendar time `t` to broken-time representation,
|
||||||
## expressed relative to the user's specified time zone.
|
## expressed relative to the user's specified time zone.
|
||||||
proc getGMTime*(t: TTime): TTimeInfo {.tags: [TimeEffect], raises: [], gcsafe.}
|
proc getGMTime*(t: Time): TimeInfo {.tags: [TimeEffect], raises: [], gcsafe.}
|
||||||
## converts the calendar time `t` to broken-down time representation,
|
## converts the calendar time `t` to broken-down time representation,
|
||||||
## expressed in Coordinated Universal Time (UTC).
|
## expressed in Coordinated Universal Time (UTC).
|
||||||
|
|
||||||
proc timeInfoToTime*(timeInfo: TTimeInfo): TTime {.tags: [], gcsafe.}
|
proc timeInfoToTime*(timeInfo: TimeInfo): Time {.tags: [], gcsafe.}
|
||||||
## converts a broken-down time structure to
|
## converts a broken-down time structure to
|
||||||
## calendar time representation. The function ignores the specified
|
## calendar time representation. The function ignores the specified
|
||||||
## contents of the structure members `weekday` and `yearday` and recomputes
|
## contents of the structure members `weekday` and `yearday` and recomputes
|
||||||
## them from the other information in the broken-down time structure.
|
## them from the other information in the broken-down time structure.
|
||||||
|
|
||||||
proc fromSeconds*(since1970: float): TTime {.tags: [], raises: [], gcsafe.}
|
proc fromSeconds*(since1970: float): Time {.tags: [], raises: [], gcsafe.}
|
||||||
## Takes a float which contains the number of seconds since the unix epoch and
|
## Takes a float which contains the number of seconds since the unix epoch and
|
||||||
## returns a time object.
|
## returns a time object.
|
||||||
|
|
||||||
proc fromSeconds*(since1970: int64): TTime {.tags: [], raises: [], gcsafe.} =
|
proc fromSeconds*(since1970: int64): Time {.tags: [], raises: [], gcsafe.} =
|
||||||
## Takes an int which contains the number of seconds since the unix epoch and
|
## Takes an int which contains the number of seconds since the unix epoch and
|
||||||
## returns a time object.
|
## returns a time object.
|
||||||
fromSeconds(float(since1970))
|
fromSeconds(float(since1970))
|
||||||
|
|
||||||
proc toSeconds*(time: TTime): float {.tags: [], raises: [], gcsafe.}
|
proc toSeconds*(time: Time): float {.tags: [], raises: [], gcsafe.}
|
||||||
## Returns the time in seconds since the unix epoch.
|
## Returns the time in seconds since the unix epoch.
|
||||||
|
|
||||||
proc `$` *(timeInfo: TTimeInfo): string {.tags: [], raises: [], gcsafe.}
|
proc `$` *(timeInfo: TimeInfo): string {.tags: [], raises: [], gcsafe.}
|
||||||
## converts a `TTimeInfo` object to a string representation.
|
## converts a `TimeInfo` object to a string representation.
|
||||||
proc `$` *(time: TTime): string {.tags: [], raises: [], gcsafe.}
|
proc `$` *(time: Time): string {.tags: [], raises: [], gcsafe.}
|
||||||
## converts a calendar time to a string representation.
|
## converts a calendar time to a string representation.
|
||||||
|
|
||||||
proc `-`*(a, b: TTime): int64 {.
|
proc `-`*(a, b: Time): int64 {.
|
||||||
rtl, extern: "ntDiffTime", tags: [], raises: [].}
|
rtl, extern: "ntDiffTime", tags: [], raises: [].}
|
||||||
## computes the difference of two calendar times. Result is in seconds.
|
## computes the difference of two calendar times. Result is in seconds.
|
||||||
|
|
||||||
proc `<`*(a, b: TTime): bool {.
|
proc `<`*(a, b: Time): bool {.
|
||||||
rtl, extern: "ntLtTime", tags: [], raises: [].} =
|
rtl, extern: "ntLtTime", tags: [], raises: [].} =
|
||||||
## returns true iff ``a < b``, that is iff a happened before b.
|
## returns true iff ``a < b``, that is iff a happened before b.
|
||||||
result = a - b < 0
|
result = a - b < 0
|
||||||
|
|
||||||
proc `<=` * (a, b: TTime): bool {.
|
proc `<=` * (a, b: Time): bool {.
|
||||||
rtl, extern: "ntLeTime", tags: [], raises: [].}=
|
rtl, extern: "ntLeTime", tags: [], raises: [].}=
|
||||||
## returns true iff ``a <= b``.
|
## returns true iff ``a <= b``.
|
||||||
result = a - b <= 0
|
result = a - b <= 0
|
||||||
|
|
||||||
proc `==`*(a, b: TTime): bool {.
|
proc `==`*(a, b: Time): bool {.
|
||||||
rtl, extern: "ntEqTime", tags: [], raises: [].} =
|
rtl, extern: "ntEqTime", tags: [], raises: [].} =
|
||||||
## returns true if ``a == b``, that is if both times represent the same value
|
## returns true if ``a == b``, that is if both times represent the same value
|
||||||
result = a - b == 0
|
result = a - b == 0
|
||||||
|
|
@ -203,8 +206,8 @@ proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], gcsafe.}
|
||||||
## version 0.8.10.** Use ``epochTime`` or ``cpuTime`` instead.
|
## version 0.8.10.** Use ``epochTime`` or ``cpuTime`` instead.
|
||||||
|
|
||||||
proc initInterval*(miliseconds, seconds, minutes, hours, days, months,
|
proc initInterval*(miliseconds, seconds, minutes, hours, days, months,
|
||||||
years: int = 0): TTimeInterval =
|
years: int = 0): TimeInterval =
|
||||||
## creates a new ``TTimeInterval``.
|
## creates a new ``TimeInterval``.
|
||||||
result.miliseconds = miliseconds
|
result.miliseconds = miliseconds
|
||||||
result.seconds = seconds
|
result.seconds = seconds
|
||||||
result.minutes = minutes
|
result.minutes = minutes
|
||||||
|
|
@ -234,7 +237,7 @@ proc getDaysInMonth*(month: TMonth, year: int): int =
|
||||||
of mApr, mJun, mSep, mNov: result = 30
|
of mApr, mJun, mSep, mNov: result = 30
|
||||||
else: result = 31
|
else: result = 31
|
||||||
|
|
||||||
proc toSeconds(a: TTimeInfo, interval: TTimeInterval): float =
|
proc toSeconds(a: TimeInfo, interval: TimeInterval): float =
|
||||||
## Calculates how many seconds the interval is worth by adding up
|
## Calculates how many seconds the interval is worth by adding up
|
||||||
## all the fields
|
## all the fields
|
||||||
|
|
||||||
|
|
@ -257,7 +260,7 @@ proc toSeconds(a: TTimeInfo, interval: TTimeInterval): float =
|
||||||
result += float(newinterv.seconds)
|
result += float(newinterv.seconds)
|
||||||
result += newinterv.miliseconds / 1000
|
result += newinterv.miliseconds / 1000
|
||||||
|
|
||||||
proc `+`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo =
|
proc `+`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
|
||||||
## adds ``interval`` time.
|
## adds ``interval`` time.
|
||||||
##
|
##
|
||||||
## **Note:** This has been only briefly tested and it may not be
|
## **Note:** This has been only briefly tested and it may not be
|
||||||
|
|
@ -269,7 +272,7 @@ proc `+`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo =
|
||||||
else:
|
else:
|
||||||
result = getLocalTime(fromSeconds(t + secs))
|
result = getLocalTime(fromSeconds(t + secs))
|
||||||
|
|
||||||
proc `-`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo =
|
proc `-`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
|
||||||
## subtracts ``interval`` time.
|
## subtracts ``interval`` time.
|
||||||
##
|
##
|
||||||
## **Note:** This has been only briefly tested, it is inaccurate especially
|
## **Note:** This has been only briefly tested, it is inaccurate especially
|
||||||
|
|
@ -314,38 +317,36 @@ when not defined(JS):
|
||||||
yearday {.importc: "tm_yday".},
|
yearday {.importc: "tm_yday".},
|
||||||
isdst {.importc: "tm_isdst".}: cint
|
isdst {.importc: "tm_isdst".}: cint
|
||||||
|
|
||||||
PTimeInfo = ptr StructTM
|
TimeInfoPtr = ptr StructTM
|
||||||
PTime = ptr TTime
|
Clock {.importc: "clock_t".} = distinct int
|
||||||
|
|
||||||
TClock {.importc: "clock_t".} = distinct int
|
proc localtime(timer: ptr Time): TimeInfoPtr {.
|
||||||
|
|
||||||
proc localtime(timer: PTime): PTimeInfo {.
|
|
||||||
importc: "localtime", header: "<time.h>", tags: [].}
|
importc: "localtime", header: "<time.h>", tags: [].}
|
||||||
proc gmtime(timer: PTime): PTimeInfo {.
|
proc gmtime(timer: ptr Time): TimeInfoPtr {.
|
||||||
importc: "gmtime", header: "<time.h>", tags: [].}
|
importc: "gmtime", header: "<time.h>", tags: [].}
|
||||||
proc timec(timer: PTime): TTime {.
|
proc timec(timer: ptr Time): Time {.
|
||||||
importc: "time", header: "<time.h>", tags: [].}
|
importc: "time", header: "<time.h>", tags: [].}
|
||||||
proc mktime(t: StructTM): TTime {.
|
proc mktime(t: StructTM): Time {.
|
||||||
importc: "mktime", header: "<time.h>", tags: [].}
|
importc: "mktime", header: "<time.h>", tags: [].}
|
||||||
proc asctime(tblock: StructTM): cstring {.
|
proc asctime(tblock: StructTM): cstring {.
|
||||||
importc: "asctime", header: "<time.h>", tags: [].}
|
importc: "asctime", header: "<time.h>", tags: [].}
|
||||||
proc ctime(time: PTime): cstring {.
|
proc ctime(time: ptr Time): cstring {.
|
||||||
importc: "ctime", header: "<time.h>", tags: [].}
|
importc: "ctime", header: "<time.h>", tags: [].}
|
||||||
# strftime(s: CString, maxsize: int, fmt: CString, t: tm): int {.
|
# strftime(s: CString, maxsize: int, fmt: CString, t: tm): int {.
|
||||||
# importc: "strftime", header: "<time.h>".}
|
# importc: "strftime", header: "<time.h>".}
|
||||||
proc clock(): TClock {.importc: "clock", header: "<time.h>", tags: [TimeEffect].}
|
proc getClock(): Clock {.importc: "clock", header: "<time.h>", tags: [TimeEffect].}
|
||||||
proc difftime(a, b: TTime): float {.importc: "difftime", header: "<time.h>",
|
proc difftime(a, b: Time): float {.importc: "difftime", header: "<time.h>",
|
||||||
tags: [].}
|
tags: [].}
|
||||||
|
|
||||||
var
|
var
|
||||||
clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl.}: int
|
clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl.}: int
|
||||||
|
|
||||||
# our own procs on top of that:
|
# our own procs on top of that:
|
||||||
proc tmToTimeInfo(tm: StructTM, local: bool): TTimeInfo =
|
proc tmToTimeInfo(tm: StructTM, local: bool): TimeInfo =
|
||||||
const
|
const
|
||||||
weekDays: array [0..6, TWeekDay] = [
|
weekDays: array [0..6, WeekDay] = [
|
||||||
dSun, dMon, dTue, dWed, dThu, dFri, dSat]
|
dSun, dMon, dTue, dWed, dThu, dFri, dSat]
|
||||||
TTimeInfo(second: int(tm.second),
|
TimeInfo(second: int(tm.second),
|
||||||
minute: int(tm.minute),
|
minute: int(tm.minute),
|
||||||
hour: int(tm.hour),
|
hour: int(tm.hour),
|
||||||
monthday: int(tm.monthday),
|
monthday: int(tm.monthday),
|
||||||
|
|
@ -364,9 +365,9 @@ when not defined(JS):
|
||||||
timezone: if local: getTimezone() else: 0
|
timezone: if local: getTimezone() else: 0
|
||||||
)
|
)
|
||||||
|
|
||||||
proc timeInfoToTM(t: TTimeInfo): StructTM =
|
proc timeInfoToTM(t: TimeInfo): StructTM =
|
||||||
const
|
const
|
||||||
weekDays: array [TWeekDay, int8] = [1'i8,2'i8,3'i8,4'i8,5'i8,6'i8,0'i8]
|
weekDays: array [WeekDay, int8] = [1'i8,2'i8,3'i8,4'i8,5'i8,6'i8,0'i8]
|
||||||
result.second = t.second
|
result.second = t.second
|
||||||
result.minute = t.minute
|
result.minute = t.minute
|
||||||
result.hour = t.hour
|
result.hour = t.hour
|
||||||
|
|
@ -378,36 +379,36 @@ when not defined(JS):
|
||||||
result.isdst = if t.isDST: 1 else: 0
|
result.isdst = if t.isDST: 1 else: 0
|
||||||
|
|
||||||
when not defined(useNimRtl):
|
when not defined(useNimRtl):
|
||||||
proc `-` (a, b: TTime): int64 =
|
proc `-` (a, b: Time): int64 =
|
||||||
return toBiggestInt(difftime(a, b))
|
return toBiggestInt(difftime(a, b))
|
||||||
|
|
||||||
proc getStartMilsecs(): int =
|
proc getStartMilsecs(): int =
|
||||||
#echo "clocks per sec: ", clocksPerSec, "clock: ", int(clock())
|
#echo "clocks per sec: ", clocksPerSec, "clock: ", int(getClock())
|
||||||
#return clock() div (clocksPerSec div 1000)
|
#return getClock() div (clocksPerSec div 1000)
|
||||||
when defined(macosx):
|
when defined(macosx):
|
||||||
result = toInt(toFloat(int(clock())) / (toFloat(clocksPerSec) / 1000.0))
|
result = toInt(toFloat(int(getClock())) / (toFloat(clocksPerSec) / 1000.0))
|
||||||
else:
|
else:
|
||||||
result = int(clock()) div (clocksPerSec div 1000)
|
result = int(getClock()) div (clocksPerSec div 1000)
|
||||||
when false:
|
when false:
|
||||||
var a: Ttimeval
|
var a: Timeval
|
||||||
posix_gettimeofday(a)
|
posix_gettimeofday(a)
|
||||||
result = a.tv_sec * 1000'i64 + a.tv_usec div 1000'i64
|
result = a.tv_sec * 1000'i64 + a.tv_usec div 1000'i64
|
||||||
#echo "result: ", result
|
#echo "result: ", result
|
||||||
|
|
||||||
proc getTime(): TTime = return timec(nil)
|
proc getTime(): Time = return timec(nil)
|
||||||
proc getLocalTime(t: TTime): TTimeInfo =
|
proc getLocalTime(t: Time): TimeInfo =
|
||||||
var a = t
|
var a = t
|
||||||
result = tmToTimeInfo(localtime(addr(a))[], true)
|
result = tmToTimeInfo(localtime(addr(a))[], true)
|
||||||
# copying is needed anyway to provide reentrancity; thus
|
# copying is needed anyway to provide reentrancity; thus
|
||||||
# the conversion is not expensive
|
# the conversion is not expensive
|
||||||
|
|
||||||
proc getGMTime(t: TTime): TTimeInfo =
|
proc getGMTime(t: Time): TimeInfo =
|
||||||
var a = t
|
var a = t
|
||||||
result = tmToTimeInfo(gmtime(addr(a))[], false)
|
result = tmToTimeInfo(gmtime(addr(a))[], false)
|
||||||
# copying is needed anyway to provide reentrancity; thus
|
# copying is needed anyway to provide reentrancity; thus
|
||||||
# the conversion is not expensive
|
# the conversion is not expensive
|
||||||
|
|
||||||
proc timeInfoToTime(timeInfo: TTimeInfo): TTime =
|
proc timeInfoToTime(timeInfo: TimeInfo): Time =
|
||||||
var cTimeInfo = timeInfo # for C++ we have to make a copy,
|
var cTimeInfo = timeInfo # for C++ we have to make a copy,
|
||||||
# because the header of mktime is broken in my version of libc
|
# because the header of mktime is broken in my version of libc
|
||||||
return mktime(timeInfoToTM(cTimeInfo))
|
return mktime(timeInfoToTM(cTimeInfo))
|
||||||
|
|
@ -419,12 +420,12 @@ when not defined(JS):
|
||||||
add(result, p[i])
|
add(result, p[i])
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
||||||
proc `$`(timeInfo: TTimeInfo): string =
|
proc `$`(timeInfo: TimeInfo): string =
|
||||||
# BUGFIX: asctime returns a newline at the end!
|
# BUGFIX: asctime returns a newline at the end!
|
||||||
var p = asctime(timeInfoToTM(timeInfo))
|
var p = asctime(timeInfoToTM(timeInfo))
|
||||||
result = toStringTillNL(p)
|
result = toStringTillNL(p)
|
||||||
|
|
||||||
proc `$`(time: TTime): string =
|
proc `$`(time: Time): string =
|
||||||
# BUGFIX: ctime returns a newline at the end!
|
# BUGFIX: ctime returns a newline at the end!
|
||||||
var a = time
|
var a = time
|
||||||
return toStringTillNL(ctime(addr(a)))
|
return toStringTillNL(ctime(addr(a)))
|
||||||
|
|
@ -433,13 +434,13 @@ when not defined(JS):
|
||||||
epochDiff = 116444736000000000'i64
|
epochDiff = 116444736000000000'i64
|
||||||
rateDiff = 10000000'i64 # 100 nsecs
|
rateDiff = 10000000'i64 # 100 nsecs
|
||||||
|
|
||||||
proc unixTimeToWinTime*(t: TTime): int64 =
|
proc unixTimeToWinTime*(t: Time): int64 =
|
||||||
## converts a UNIX `TTime` (``time_t``) to a Windows file time
|
## converts a UNIX `Time` (``time_t``) to a Windows file time
|
||||||
result = int64(t) * rateDiff + epochDiff
|
result = int64(t) * rateDiff + epochDiff
|
||||||
|
|
||||||
proc winTimeToUnixTime*(t: int64): TTime =
|
proc winTimeToUnixTime*(t: int64): Time =
|
||||||
## converts a Windows time to a UNIX `TTime` (``time_t``)
|
## converts a Windows time to a UNIX `Time` (``time_t``)
|
||||||
result = TTime((t - epochDiff) div rateDiff)
|
result = Time((t - epochDiff) div rateDiff)
|
||||||
|
|
||||||
proc getTzname(): tuple[nonDST, DST: string] =
|
proc getTzname(): tuple[nonDST, DST: string] =
|
||||||
return ($tzname[0], $tzname[1])
|
return ($tzname[0], $tzname[1])
|
||||||
|
|
@ -447,9 +448,9 @@ when not defined(JS):
|
||||||
proc getTimezone(): int =
|
proc getTimezone(): int =
|
||||||
return timezone
|
return timezone
|
||||||
|
|
||||||
proc fromSeconds(since1970: float): TTime = TTime(since1970)
|
proc fromSeconds(since1970: float): Time = Time(since1970)
|
||||||
|
|
||||||
proc toSeconds(time: TTime): float = float(time)
|
proc toSeconds(time: Time): float = float(time)
|
||||||
|
|
||||||
when not defined(useNimRtl):
|
when not defined(useNimRtl):
|
||||||
proc epochTime(): float =
|
proc epochTime(): float =
|
||||||
|
|
@ -468,15 +469,15 @@ when not defined(JS):
|
||||||
{.error: "unknown OS".}
|
{.error: "unknown OS".}
|
||||||
|
|
||||||
proc cpuTime(): float =
|
proc cpuTime(): float =
|
||||||
result = toFloat(int(clock())) / toFloat(clocksPerSec)
|
result = toFloat(int(getClock())) / toFloat(clocksPerSec)
|
||||||
|
|
||||||
elif defined(JS):
|
elif defined(JS):
|
||||||
proc newDate(): TTime {.importc: "new Date".}
|
proc newDate(): Time {.importc: "new Date".}
|
||||||
proc internGetTime(): TTime {.importc: "new Date", tags: [].}
|
proc internGetTime(): Time {.importc: "new Date", tags: [].}
|
||||||
|
|
||||||
proc newDate(value: float): TTime {.importc: "new Date".}
|
proc newDate(value: float): Time {.importc: "new Date".}
|
||||||
proc newDate(value: string): TTime {.importc: "new Date".}
|
proc newDate(value: string): Time {.importc: "new Date".}
|
||||||
proc getTime(): TTime =
|
proc getTime(): Time =
|
||||||
# Warning: This is something different in JS.
|
# Warning: This is something different in JS.
|
||||||
return newDate()
|
return newDate()
|
||||||
|
|
||||||
|
|
@ -484,7 +485,7 @@ elif defined(JS):
|
||||||
weekDays: array [0..6, TWeekDay] = [
|
weekDays: array [0..6, TWeekDay] = [
|
||||||
dSun, dMon, dTue, dWed, dThu, dFri, dSat]
|
dSun, dMon, dTue, dWed, dThu, dFri, dSat]
|
||||||
|
|
||||||
proc getLocalTime(t: TTime): TTimeInfo =
|
proc getLocalTime(t: Time): TimeInfo =
|
||||||
result.second = t.getSeconds()
|
result.second = t.getSeconds()
|
||||||
result.minute = t.getMinutes()
|
result.minute = t.getMinutes()
|
||||||
result.hour = t.getHours()
|
result.hour = t.getHours()
|
||||||
|
|
@ -494,7 +495,7 @@ elif defined(JS):
|
||||||
result.weekday = weekDays[t.getDay()]
|
result.weekday = weekDays[t.getDay()]
|
||||||
result.yearday = 0
|
result.yearday = 0
|
||||||
|
|
||||||
proc getGMTime(t: TTime): TTimeInfo =
|
proc getGMTime(t: Time): TimeInfo =
|
||||||
result.second = t.getUTCSeconds()
|
result.second = t.getUTCSeconds()
|
||||||
result.minute = t.getUTCMinutes()
|
result.minute = t.getUTCMinutes()
|
||||||
result.hour = t.getUTCHours()
|
result.hour = t.getUTCHours()
|
||||||
|
|
@ -504,7 +505,7 @@ elif defined(JS):
|
||||||
result.weekday = weekDays[t.getUTCDay()]
|
result.weekday = weekDays[t.getUTCDay()]
|
||||||
result.yearday = 0
|
result.yearday = 0
|
||||||
|
|
||||||
proc timeInfoToTime*(timeInfo: TTimeInfo): TTime =
|
proc timeInfoToTime*(timeInfo: TimeInfo): Time =
|
||||||
result = internGetTime()
|
result = internGetTime()
|
||||||
result.setSeconds(timeInfo.second)
|
result.setSeconds(timeInfo.second)
|
||||||
result.setMinutes(timeInfo.minute)
|
result.setMinutes(timeInfo.minute)
|
||||||
|
|
@ -513,10 +514,10 @@ elif defined(JS):
|
||||||
result.setFullYear(timeInfo.year)
|
result.setFullYear(timeInfo.year)
|
||||||
result.setDate(timeInfo.monthday)
|
result.setDate(timeInfo.monthday)
|
||||||
|
|
||||||
proc `$`(timeInfo: TTimeInfo): string = return $(TimeInfoToTIme(timeInfo))
|
proc `$`(timeInfo: TimeInfo): string = return $(TimeInfoToTIme(timeInfo))
|
||||||
proc `$`(time: TTime): string = return $time.toLocaleString()
|
proc `$`(time: Time): string = return $time.toLocaleString()
|
||||||
|
|
||||||
proc `-` (a, b: TTime): int64 =
|
proc `-` (a, b: Time): int64 =
|
||||||
return a.getTime() - b.getTime()
|
return a.getTime() - b.getTime()
|
||||||
|
|
||||||
var
|
var
|
||||||
|
|
@ -526,11 +527,11 @@ elif defined(JS):
|
||||||
## get the miliseconds from the start of the program
|
## get the miliseconds from the start of the program
|
||||||
return int(getTime() - startMilsecs)
|
return int(getTime() - startMilsecs)
|
||||||
|
|
||||||
proc valueOf(time: TTime): float {.importcpp: "getTime", tags:[]}
|
proc valueOf(time: Time): float {.importcpp: "getTime", tags:[]}
|
||||||
|
|
||||||
proc fromSeconds(since1970: float): TTime = result = newDate(since1970)
|
proc fromSeconds(since1970: float): Time = result = newDate(since1970)
|
||||||
|
|
||||||
proc toSeconds(time: TTime): float = result = time.valueOf() / 1000
|
proc toSeconds(time: Time): float = result = time.valueOf() / 1000
|
||||||
|
|
||||||
proc getTimezone(): int = result = newDate().getTimezoneOffset()
|
proc getTimezone(): int = result = newDate().getTimezoneOffset()
|
||||||
|
|
||||||
|
|
@ -546,20 +547,20 @@ proc getClockStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} =
|
||||||
result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) &
|
result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) &
|
||||||
':' & intToStr(ti.second, 2)
|
':' & intToStr(ti.second, 2)
|
||||||
|
|
||||||
proc `$`*(day: TWeekDay): string =
|
proc `$`*(day: WeekDay): string =
|
||||||
## stingify operator for ``TWeekDay``.
|
## stingify operator for ``WeekDay``.
|
||||||
const lookup: array[TWeekDay, string] = ["Monday", "Tuesday", "Wednesday",
|
const lookup: array[WeekDay, string] = ["Monday", "Tuesday", "Wednesday",
|
||||||
"Thursday", "Friday", "Saturday", "Sunday"]
|
"Thursday", "Friday", "Saturday", "Sunday"]
|
||||||
return lookup[day]
|
return lookup[day]
|
||||||
|
|
||||||
proc `$`*(m: TMonth): string =
|
proc `$`*(m: Month): string =
|
||||||
## stingify operator for ``TMonth``.
|
## stingify operator for ``TMonth``.
|
||||||
const lookup: array[TMonth, string] = ["January", "February", "March",
|
const lookup: array[Month, string] = ["January", "February", "March",
|
||||||
"April", "May", "June", "July", "August", "September", "October",
|
"April", "May", "June", "July", "August", "September", "October",
|
||||||
"November", "December"]
|
"November", "December"]
|
||||||
return lookup[m]
|
return lookup[m]
|
||||||
|
|
||||||
proc format_token(info: TTimeInfo, token: string, buf: var string) =
|
proc formatToken(info: TimeInfo, token: string, buf: var string) =
|
||||||
## Helper of the format proc to parse individual tokens.
|
## Helper of the format proc to parse individual tokens.
|
||||||
##
|
##
|
||||||
## Pass the found token in the user input string, and the buffer where the
|
## Pass the found token in the user input string, and the buffer where the
|
||||||
|
|
@ -672,7 +673,7 @@ proc format_token(info: TTimeInfo, token: string, buf: var string) =
|
||||||
raise newException(ValueError, "Invalid format string: " & token)
|
raise newException(ValueError, "Invalid format string: " & token)
|
||||||
|
|
||||||
|
|
||||||
proc format*(info: TTimeInfo, f: string): string =
|
proc format*(info: TimeInfo, f: string): string =
|
||||||
## This function formats `info` as specified by `f`. The following format
|
## This function formats `info` as specified by `f`. The following format
|
||||||
## specifiers are available:
|
## specifiers are available:
|
||||||
##
|
##
|
||||||
|
|
@ -718,7 +719,7 @@ proc format*(info: TTimeInfo, f: string): string =
|
||||||
while true:
|
while true:
|
||||||
case f[i]
|
case f[i]
|
||||||
of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
|
of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
|
||||||
format_token(info, currentF, result)
|
formatToken(info, currentF, result)
|
||||||
|
|
||||||
currentF = ""
|
currentF = ""
|
||||||
if f[i] == '\0': break
|
if f[i] == '\0': break
|
||||||
|
|
@ -735,7 +736,7 @@ proc format*(info: TTimeInfo, f: string): string =
|
||||||
if currentF.len < 1 or currentF[high(currentF)] == f[i]:
|
if currentF.len < 1 or currentF[high(currentF)] == f[i]:
|
||||||
currentF.add(f[i])
|
currentF.add(f[i])
|
||||||
else:
|
else:
|
||||||
format_token(info, currentF, result)
|
formatToken(info, currentF, result)
|
||||||
dec(i) # Move position back to re-process the character separately.
|
dec(i) # Move position back to re-process the character separately.
|
||||||
currentF = ""
|
currentF = ""
|
||||||
|
|
||||||
|
|
@ -764,7 +765,7 @@ when isMainModule:
|
||||||
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
|
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
|
||||||
"27 27 Mon Monday 4 04 16 16 6 06 1 01 Jan January 29 29 P PM 5 75 975 1975 01975 0 00 00:00 UTC"
|
"27 27 Mon Monday 4 04 16 16 6 06 1 01 Jan January 29 29 P PM 5 75 975 1975 01975 0 00 00:00 UTC"
|
||||||
|
|
||||||
when not defined(JS) and sizeof(TTime) == 8:
|
when not defined(JS) and sizeof(Time) == 8:
|
||||||
var t3 = getGMTime(fromSeconds(889067643645)) # Fri 7 Jun 19:20:45 BST 30143
|
var t3 = getGMTime(fromSeconds(889067643645)) # Fri 7 Jun 19:20:45 BST 30143
|
||||||
assert t3.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
|
assert t3.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
|
||||||
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
|
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Nimrod Contributors
|
# (c) Copyright 2012 Nimrod Contributors
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -14,13 +14,15 @@
|
||||||
include "system/inclrtl"
|
include "system/inclrtl"
|
||||||
|
|
||||||
type
|
type
|
||||||
IRune = int # underlying type of TRune
|
RuneImpl = int # underlying type of Rune
|
||||||
TRune* = distinct IRune ## type that can hold any Unicode character
|
Rune* = distinct RuneImpl ## type that can hold any Unicode character
|
||||||
TRune16* = distinct int16 ## 16 bit Unicode character
|
Rune16* = distinct int16 ## 16 bit Unicode character
|
||||||
|
|
||||||
proc `<=%`*(a, b: TRune): bool = return int(a) <=% int(b)
|
{.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: 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)
|
template ones(n: expr): expr = ((1 shl n)-1)
|
||||||
|
|
||||||
|
|
@ -52,17 +54,17 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) =
|
||||||
## `i` is incremented by the number of bytes that have been processed.
|
## `i` is incremented by the number of bytes that have been processed.
|
||||||
bind ones
|
bind ones
|
||||||
if ord(s[i]) <=% 127:
|
if ord(s[i]) <=% 127:
|
||||||
result = TRune(ord(s[i]))
|
result = Rune(ord(s[i]))
|
||||||
when doInc: inc(i)
|
when doInc: inc(i)
|
||||||
elif ord(s[i]) shr 5 == 0b110:
|
elif ord(s[i]) shr 5 == 0b110:
|
||||||
# assert(ord(s[i+1]) shr 6 == 0b10)
|
# assert(ord(s[i+1]) shr 6 == 0b10)
|
||||||
result = TRune((ord(s[i]) and (ones(5))) shl 6 or
|
result = Rune((ord(s[i]) and (ones(5))) shl 6 or
|
||||||
(ord(s[i+1]) and ones(6)))
|
(ord(s[i+1]) and ones(6)))
|
||||||
when doInc: inc(i, 2)
|
when doInc: inc(i, 2)
|
||||||
elif ord(s[i]) shr 4 == 0b1110:
|
elif ord(s[i]) shr 4 == 0b1110:
|
||||||
# assert(ord(s[i+1]) shr 6 == 0b10)
|
# assert(ord(s[i+1]) shr 6 == 0b10)
|
||||||
# assert(ord(s[i+2]) shr 6 == 0b10)
|
# assert(ord(s[i+2]) shr 6 == 0b10)
|
||||||
result = TRune((ord(s[i]) and ones(4)) shl 12 or
|
result = Rune((ord(s[i]) and ones(4)) shl 12 or
|
||||||
(ord(s[i+1]) and ones(6)) shl 6 or
|
(ord(s[i+1]) and ones(6)) shl 6 or
|
||||||
(ord(s[i+2]) and ones(6)))
|
(ord(s[i+2]) and ones(6)))
|
||||||
when doInc: inc(i, 3)
|
when doInc: inc(i, 3)
|
||||||
|
|
@ -70,7 +72,7 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) =
|
||||||
# assert(ord(s[i+1]) shr 6 == 0b10)
|
# assert(ord(s[i+1]) shr 6 == 0b10)
|
||||||
# assert(ord(s[i+2]) shr 6 == 0b10)
|
# assert(ord(s[i+2]) shr 6 == 0b10)
|
||||||
# assert(ord(s[i+3]) shr 6 == 0b10)
|
# assert(ord(s[i+3]) shr 6 == 0b10)
|
||||||
result = TRune((ord(s[i]) and ones(3)) shl 18 or
|
result = Rune((ord(s[i]) and ones(3)) shl 18 or
|
||||||
(ord(s[i+1]) and ones(6)) shl 12 or
|
(ord(s[i+1]) and ones(6)) shl 12 or
|
||||||
(ord(s[i+2]) and ones(6)) shl 6 or
|
(ord(s[i+2]) and ones(6)) shl 6 or
|
||||||
(ord(s[i+3]) and ones(6)))
|
(ord(s[i+3]) and ones(6)))
|
||||||
|
|
@ -80,7 +82,7 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) =
|
||||||
# assert(ord(s[i+2]) shr 6 == 0b10)
|
# assert(ord(s[i+2]) shr 6 == 0b10)
|
||||||
# assert(ord(s[i+3]) shr 6 == 0b10)
|
# assert(ord(s[i+3]) shr 6 == 0b10)
|
||||||
# assert(ord(s[i+4]) shr 6 == 0b10)
|
# assert(ord(s[i+4]) shr 6 == 0b10)
|
||||||
result = TRune((ord(s[i]) and ones(2)) shl 24 or
|
result = Rune((ord(s[i]) and ones(2)) shl 24 or
|
||||||
(ord(s[i+1]) and ones(6)) shl 18 or
|
(ord(s[i+1]) and ones(6)) shl 18 or
|
||||||
(ord(s[i+2]) and ones(6)) shl 12 or
|
(ord(s[i+2]) and ones(6)) shl 12 or
|
||||||
(ord(s[i+3]) and ones(6)) shl 6 or
|
(ord(s[i+3]) and ones(6)) shl 6 or
|
||||||
|
|
@ -92,7 +94,7 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) =
|
||||||
# assert(ord(s[i+3]) shr 6 == 0b10)
|
# assert(ord(s[i+3]) shr 6 == 0b10)
|
||||||
# assert(ord(s[i+4]) shr 6 == 0b10)
|
# assert(ord(s[i+4]) shr 6 == 0b10)
|
||||||
# assert(ord(s[i+5]) shr 6 == 0b10)
|
# assert(ord(s[i+5]) shr 6 == 0b10)
|
||||||
result = TRune((ord(s[i]) and ones(1)) shl 30 or
|
result = Rune((ord(s[i]) and ones(1)) shl 30 or
|
||||||
(ord(s[i+1]) and ones(6)) shl 24 or
|
(ord(s[i+1]) and ones(6)) shl 24 or
|
||||||
(ord(s[i+2]) and ones(6)) shl 18 or
|
(ord(s[i+2]) and ones(6)) shl 18 or
|
||||||
(ord(s[i+3]) and ones(6)) shl 12 or
|
(ord(s[i+3]) and ones(6)) shl 12 or
|
||||||
|
|
@ -100,16 +102,16 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) =
|
||||||
(ord(s[i+5]) and ones(6)))
|
(ord(s[i+5]) and ones(6)))
|
||||||
when doInc: inc(i, 6)
|
when doInc: inc(i, 6)
|
||||||
else:
|
else:
|
||||||
result = TRune(ord(s[i]))
|
result = Rune(ord(s[i]))
|
||||||
when doInc: inc(i)
|
when doInc: inc(i)
|
||||||
|
|
||||||
proc runeAt*(s: string, i: int): TRune =
|
proc runeAt*(s: string, i: int): Rune =
|
||||||
## returns the unicode character in `s` at byte index `i`
|
## returns the unicode character in `s` at byte index `i`
|
||||||
fastRuneAt(s, i, result, false)
|
fastRuneAt(s, i, result, false)
|
||||||
|
|
||||||
proc toUTF8*(c: TRune): string {.rtl, extern: "nuc$1".} =
|
proc toUTF8*(c: Rune): string {.rtl, extern: "nuc$1".} =
|
||||||
## converts a rune into its UTF8 representation
|
## converts a rune into its UTF8 representation
|
||||||
var i = IRune(c)
|
var i = RuneImpl(c)
|
||||||
if i <=% 127:
|
if i <=% 127:
|
||||||
result = newString(1)
|
result = newString(1)
|
||||||
result[0] = chr(i)
|
result[0] = chr(i)
|
||||||
|
|
@ -132,11 +134,11 @@ proc toUTF8*(c: TRune): string {.rtl, extern: "nuc$1".} =
|
||||||
result = newString(1)
|
result = newString(1)
|
||||||
result[0] = chr(i)
|
result[0] = chr(i)
|
||||||
|
|
||||||
proc `$`*(rune: TRune): string =
|
proc `$`*(rune: Rune): string =
|
||||||
## converts a rune to a string
|
## converts a rune to a string
|
||||||
rune.toUTF8
|
rune.toUTF8
|
||||||
|
|
||||||
proc `$`*(runes: seq[TRune]): string =
|
proc `$`*(runes: seq[Rune]): string =
|
||||||
## converts a sequence of runes to a string
|
## converts a sequence of runes to a string
|
||||||
result = ""
|
result = ""
|
||||||
for rune in runes: result.add(rune.toUTF8)
|
for rune in runes: result.add(rune.toUTF8)
|
||||||
|
|
@ -1100,7 +1102,7 @@ const
|
||||||
0x01f1, 501, #
|
0x01f1, 501, #
|
||||||
0x01f3, 499] #
|
0x01f3, 499] #
|
||||||
|
|
||||||
proc binarySearch(c: IRune, tab: openArray[IRune], len, stride: int): int =
|
proc binarySearch(c: RuneImpl, tab: openArray[RuneImpl], len, stride: int): int =
|
||||||
var n = len
|
var n = len
|
||||||
var t = 0
|
var t = 0
|
||||||
while n > 1:
|
while n > 1:
|
||||||
|
|
@ -1115,41 +1117,41 @@ proc binarySearch(c: IRune, tab: openArray[IRune], len, stride: int): int =
|
||||||
return t
|
return t
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
proc toLower*(c: TRune): TRune {.rtl, extern: "nuc$1", procvar.} =
|
proc toLower*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
|
||||||
## Converts `c` into lower case. This works for any Unicode character.
|
## Converts `c` into lower case. This works for any Unicode character.
|
||||||
## If possible, prefer `toLower` over `toUpper`.
|
## If possible, prefer `toLower` over `toUpper`.
|
||||||
var c = IRune(c)
|
var c = RuneImpl(c)
|
||||||
var p = binarySearch(c, tolowerRanges, len(tolowerRanges) div 3, 3)
|
var p = binarySearch(c, tolowerRanges, len(tolowerRanges) div 3, 3)
|
||||||
if p >= 0 and c >= tolowerRanges[p] and c <= tolowerRanges[p+1]:
|
if p >= 0 and c >= tolowerRanges[p] and c <= tolowerRanges[p+1]:
|
||||||
return TRune(c + tolowerRanges[p+2] - 500)
|
return Rune(c + tolowerRanges[p+2] - 500)
|
||||||
p = binarySearch(c, tolowerSinglets, len(tolowerSinglets) div 2, 2)
|
p = binarySearch(c, tolowerSinglets, len(tolowerSinglets) div 2, 2)
|
||||||
if p >= 0 and c == tolowerSinglets[p]:
|
if p >= 0 and c == tolowerSinglets[p]:
|
||||||
return TRune(c + tolowerSinglets[p+1] - 500)
|
return Rune(c + tolowerSinglets[p+1] - 500)
|
||||||
return TRune(c)
|
return Rune(c)
|
||||||
|
|
||||||
proc toUpper*(c: TRune): TRune {.rtl, extern: "nuc$1", procvar.} =
|
proc toUpper*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
|
||||||
## Converts `c` into upper case. This works for any Unicode character.
|
## Converts `c` into upper case. This works for any Unicode character.
|
||||||
## If possible, prefer `toLower` over `toUpper`.
|
## If possible, prefer `toLower` over `toUpper`.
|
||||||
var c = IRune(c)
|
var c = RuneImpl(c)
|
||||||
var p = binarySearch(c, toupperRanges, len(toupperRanges) div 3, 3)
|
var p = binarySearch(c, toupperRanges, len(toupperRanges) div 3, 3)
|
||||||
if p >= 0 and c >= toupperRanges[p] and c <= toupperRanges[p+1]:
|
if p >= 0 and c >= toupperRanges[p] and c <= toupperRanges[p+1]:
|
||||||
return TRune(c + toupperRanges[p+2] - 500)
|
return Rune(c + toupperRanges[p+2] - 500)
|
||||||
p = binarySearch(c, toupperSinglets, len(toupperSinglets) div 2, 2)
|
p = binarySearch(c, toupperSinglets, len(toupperSinglets) div 2, 2)
|
||||||
if p >= 0 and c == toupperSinglets[p]:
|
if p >= 0 and c == toupperSinglets[p]:
|
||||||
return TRune(c + toupperSinglets[p+1] - 500)
|
return Rune(c + toupperSinglets[p+1] - 500)
|
||||||
return TRune(c)
|
return Rune(c)
|
||||||
|
|
||||||
proc toTitle*(c: TRune): TRune {.rtl, extern: "nuc$1", procvar.} =
|
proc toTitle*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
|
||||||
var c = IRune(c)
|
var c = RuneImpl(c)
|
||||||
var p = binarySearch(c, toTitleSinglets, len(toTitleSinglets) div 2, 2)
|
var p = binarySearch(c, toTitleSinglets, len(toTitleSinglets) div 2, 2)
|
||||||
if p >= 0 and c == toTitleSinglets[p]:
|
if p >= 0 and c == toTitleSinglets[p]:
|
||||||
return TRune(c + toTitleSinglets[p+1] - 500)
|
return Rune(c + toTitleSinglets[p+1] - 500)
|
||||||
return TRune(c)
|
return Rune(c)
|
||||||
|
|
||||||
proc isLower*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
|
proc isLower*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||||
## returns true iff `c` is a lower case Unicode character
|
## returns true iff `c` is a lower case Unicode character
|
||||||
## If possible, prefer `isLower` over `isUpper`.
|
## If possible, prefer `isLower` over `isUpper`.
|
||||||
var c = IRune(c)
|
var c = RuneImpl(c)
|
||||||
# Note: toUpperRanges is correct here!
|
# Note: toUpperRanges is correct here!
|
||||||
var p = binarySearch(c, toupperRanges, len(toupperRanges) div 3, 3)
|
var p = binarySearch(c, toupperRanges, len(toupperRanges) div 3, 3)
|
||||||
if p >= 0 and c >= toupperRanges[p] and c <= toupperRanges[p+1]:
|
if p >= 0 and c >= toupperRanges[p] and c <= toupperRanges[p+1]:
|
||||||
|
|
@ -1158,10 +1160,10 @@ proc isLower*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||||
if p >= 0 and c == toupperSinglets[p]:
|
if p >= 0 and c == toupperSinglets[p]:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc isUpper*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
|
proc isUpper*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||||
## returns true iff `c` is a upper case Unicode character
|
## returns true iff `c` is a upper case Unicode character
|
||||||
## If possible, prefer `isLower` over `isUpper`.
|
## If possible, prefer `isLower` over `isUpper`.
|
||||||
var c = IRune(c)
|
var c = RuneImpl(c)
|
||||||
# Note: toLowerRanges is correct here!
|
# Note: toLowerRanges is correct here!
|
||||||
var p = binarySearch(c, tolowerRanges, len(tolowerRanges) div 3, 3)
|
var p = binarySearch(c, tolowerRanges, len(tolowerRanges) div 3, 3)
|
||||||
if p >= 0 and c >= tolowerRanges[p] and c <= tolowerRanges[p+1]:
|
if p >= 0 and c >= tolowerRanges[p] and c <= tolowerRanges[p+1]:
|
||||||
|
|
@ -1170,11 +1172,11 @@ proc isUpper*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||||
if p >= 0 and c == tolowerSinglets[p]:
|
if p >= 0 and c == tolowerSinglets[p]:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc isAlpha*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
|
proc isAlpha*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||||
## returns true iff `c` is an *alpha* Unicode character (i.e. a letter)
|
## returns true iff `c` is an *alpha* Unicode character (i.e. a letter)
|
||||||
if isUpper(c) or isLower(c):
|
if isUpper(c) or isLower(c):
|
||||||
return true
|
return true
|
||||||
var c = IRune(c)
|
var c = RuneImpl(c)
|
||||||
var p = binarySearch(c, alphaRanges, len(alphaRanges) div 2, 2)
|
var p = binarySearch(c, alphaRanges, len(alphaRanges) div 2, 2)
|
||||||
if p >= 0 and c >= alphaRanges[p] and c <= alphaRanges[p+1]:
|
if p >= 0 and c >= alphaRanges[p] and c <= alphaRanges[p+1]:
|
||||||
return true
|
return true
|
||||||
|
|
@ -1182,21 +1184,21 @@ proc isAlpha*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||||
if p >= 0 and c == alphaSinglets[p]:
|
if p >= 0 and c == alphaSinglets[p]:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc isTitle*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
|
proc isTitle*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||||
return isUpper(c) and isLower(c)
|
return isUpper(c) and isLower(c)
|
||||||
|
|
||||||
proc isWhiteSpace*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
|
proc isWhiteSpace*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||||
## returns true iff `c` is a Unicode whitespace character
|
## returns true iff `c` is a Unicode whitespace character
|
||||||
var c = IRune(c)
|
var c = RuneImpl(c)
|
||||||
var p = binarySearch(c, spaceRanges, len(spaceRanges) div 2, 2)
|
var p = binarySearch(c, spaceRanges, len(spaceRanges) div 2, 2)
|
||||||
if p >= 0 and c >= spaceRanges[p] and c <= spaceRanges[p+1]:
|
if p >= 0 and c >= spaceRanges[p] and c <= spaceRanges[p+1]:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
iterator runes*(s: string): TRune =
|
iterator runes*(s: string): Rune =
|
||||||
## iterates over any unicode character of the string `s`.
|
## iterates over any unicode character of the string `s`.
|
||||||
var
|
var
|
||||||
i = 0
|
i = 0
|
||||||
result: TRune
|
result: Rune
|
||||||
while i < len(s):
|
while i < len(s):
|
||||||
fastRuneAt(s, i, result, true)
|
fastRuneAt(s, i, result, true)
|
||||||
yield result
|
yield result
|
||||||
|
|
@ -1209,12 +1211,12 @@ proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1", procvar.} =
|
||||||
## | > 0 iff a > b
|
## | > 0 iff a > b
|
||||||
var i = 0
|
var i = 0
|
||||||
var j = 0
|
var j = 0
|
||||||
var ar, br: TRune
|
var ar, br: Rune
|
||||||
while i < a.len and j < b.len:
|
while i < a.len and j < b.len:
|
||||||
# slow path:
|
# slow path:
|
||||||
fastRuneAt(a, i, ar)
|
fastRuneAt(a, i, ar)
|
||||||
fastRuneAt(b, j, br)
|
fastRuneAt(b, j, br)
|
||||||
result = IRune(toLower(ar)) - IRune(toLower(br))
|
result = RuneImpl(toLower(ar)) - RuneImpl(toLower(br))
|
||||||
if result != 0: return
|
if result != 0: return
|
||||||
result = a.len - b.len
|
result = a.len - b.len
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -55,7 +55,7 @@ proc unidecode*(s: string): string =
|
||||||
##
|
##
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
## ..code-block:: nimrod
|
## ..code-block:: nim
|
||||||
##
|
##
|
||||||
## unidecode("\x53\x17\x4E\xB0")
|
## unidecode("\x53\x17\x4E\xB0")
|
||||||
##
|
##
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Nimrod Contributors
|
# (c) Copyright 2012 Nimrod Contributors
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -26,16 +26,19 @@ when not defined(ECMAScript):
|
||||||
import terminal
|
import terminal
|
||||||
|
|
||||||
type
|
type
|
||||||
TTestStatus* = enum OK, FAILED
|
TestStatus* = enum OK, FAILED
|
||||||
TOutputLevel* = enum PRINT_ALL, PRINT_FAILURES, PRINT_NONE
|
OutputLevel* = enum PRINT_ALL, PRINT_FAILURES, PRINT_NONE
|
||||||
|
|
||||||
|
{.deprecated: [TTestStatus: TestStatus, TOutputLevel: OutputLevel]}
|
||||||
|
|
||||||
var
|
var
|
||||||
# XXX: These better be thread-local
|
abortOnError* {.threadvar.}: bool
|
||||||
AbortOnError*: bool
|
outputLevel* {.threadvar.}: OutputLevel
|
||||||
OutputLevel*: TOutputLevel
|
colorOutput* {.threadvar.}: bool
|
||||||
ColorOutput*: bool
|
|
||||||
|
|
||||||
checkpoints: seq[string] = @[]
|
checkpoints {.threadvar.}: seq[string]
|
||||||
|
|
||||||
|
checkpoints = @[]
|
||||||
|
|
||||||
template TestSetupIMPL*: stmt {.immediate, dirty.} = discard
|
template TestSetupIMPL*: stmt {.immediate, dirty.} = discard
|
||||||
template TestTeardownIMPL*: stmt {.immediate, dirty.} = discard
|
template TestTeardownIMPL*: stmt {.immediate, dirty.} = discard
|
||||||
|
|
@ -53,7 +56,7 @@ template suite*(name: expr, body: stmt): stmt {.immediate, dirty.} =
|
||||||
|
|
||||||
body
|
body
|
||||||
|
|
||||||
proc testDone(name: string, s: TTestStatus) =
|
proc testDone(name: string, s: TestStatus) =
|
||||||
if s == FAILED:
|
if s == FAILED:
|
||||||
program_result += 1
|
program_result += 1
|
||||||
|
|
||||||
|
|
@ -192,15 +195,15 @@ when declared(stdout):
|
||||||
## Reading settings
|
## Reading settings
|
||||||
var envOutLvl = os.getEnv("NIMTEST_OUTPUT_LVL").string
|
var envOutLvl = os.getEnv("NIMTEST_OUTPUT_LVL").string
|
||||||
|
|
||||||
AbortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")
|
abortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")
|
||||||
ColorOutput = not existsEnv("NIMTEST_NO_COLOR")
|
colorOutput = not existsEnv("NIMTEST_NO_COLOR")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
var envOutLvl = "" # TODO
|
var envOutLvl = "" # TODO
|
||||||
ColorOutput = false
|
colorOutput = false
|
||||||
|
|
||||||
if envOutLvl.len > 0:
|
if envOutLvl.len > 0:
|
||||||
for opt in countup(low(TOutputLevel), high(TOutputLevel)):
|
for opt in countup(low(OutputLevel), high(OutputLevel)):
|
||||||
if $opt == envOutLvl:
|
if $opt == envOutLvl:
|
||||||
OutputLevel = opt
|
outputLevel = opt
|
||||||
break
|
break
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2014 Dominik Picheta
|
# (c) Copyright 2014 Dominik Picheta
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -11,12 +11,14 @@
|
||||||
|
|
||||||
import strutils, parseutils
|
import strutils, parseutils
|
||||||
type
|
type
|
||||||
TUrl* = distinct string
|
Url* = distinct string
|
||||||
|
|
||||||
TUri* = object
|
Uri* = object
|
||||||
scheme*, username*, password*: string
|
scheme*, username*, password*: string
|
||||||
hostname*, port*, path*, query*, anchor*: string
|
hostname*, port*, path*, query*, anchor*: string
|
||||||
|
|
||||||
|
{.deprecated: [TUrl: Url, TUri: Uri].}
|
||||||
|
|
||||||
proc `$`*(url: TUrl): string {.deprecated.} =
|
proc `$`*(url: TUrl): string {.deprecated.} =
|
||||||
## **Deprecated since 0.9.6**: Use ``TUri`` instead.
|
## **Deprecated since 0.9.6**: Use ``TUri`` instead.
|
||||||
return string(url)
|
return string(url)
|
||||||
|
|
@ -172,7 +174,7 @@ proc combine*(base: TUri, reference: TUri): TUri =
|
||||||
##
|
##
|
||||||
## Examples:
|
## Examples:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block::
|
||||||
## let foo = combine(parseUri("http://example.com/foo/bar"), parseUri("/baz"))
|
## let foo = combine(parseUri("http://example.com/foo/bar"), parseUri("/baz"))
|
||||||
## assert foo.path == "/baz"
|
## assert foo.path == "/baz"
|
||||||
##
|
##
|
||||||
|
|
@ -229,7 +231,7 @@ proc `/`*(x: TUri, path: string): TUri =
|
||||||
##
|
##
|
||||||
## Examples:
|
## Examples:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block::
|
||||||
## let foo = parseUri("http://example.com/foo/bar") / parseUri("/baz")
|
## let foo = parseUri("http://example.com/foo/bar") / parseUri("/baz")
|
||||||
## assert foo.path == "/foo/bar/baz"
|
## assert foo.path == "/foo/bar/baz"
|
||||||
##
|
##
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2010 Dominik Picheta
|
# (c) Copyright 2010 Dominik Picheta
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -9,7 +9,8 @@
|
||||||
|
|
||||||
|
|
||||||
import strutils
|
import strutils
|
||||||
## This module implements XML DOM Level 2 Core specification(http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html)
|
## This module implements XML DOM Level 2 Core
|
||||||
|
## specification (http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html)
|
||||||
|
|
||||||
|
|
||||||
#http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html
|
#http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2010 Dominik Picheta
|
# (c) Copyright 2010 Dominik Picheta
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -156,7 +156,7 @@ proc loadXMLFile*(path: string): PDocument =
|
||||||
|
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
var xml = loadXMLFile(r"C:\Users\Dominik\Desktop\Code\Nimrod\xmldom\test.xml")
|
var xml = loadXMLFile("nim/xmldom/test.xml")
|
||||||
#echo(xml.getElementsByTagName("m:test2")[0].namespaceURI)
|
#echo(xml.getElementsByTagName("m:test2")[0].namespaceURI)
|
||||||
#echo(xml.getElementsByTagName("bla:test")[0].namespaceURI)
|
#echo(xml.getElementsByTagName("bla:test")[0].namespaceURI)
|
||||||
#echo(xml.getElementsByTagName("test")[0].namespaceURI)
|
#echo(xml.getElementsByTagName("test")[0].namespaceURI)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2010 Andreas Rumpf
|
# (c) Copyright 2010 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -12,9 +12,11 @@
|
||||||
import streams, parsexml, strtabs, xmltree
|
import streams, parsexml, strtabs, xmltree
|
||||||
|
|
||||||
type
|
type
|
||||||
EInvalidXml* = object of EInvalidValue ## exception that is raised
|
XmlError* = object of ValueError ## exception that is raised
|
||||||
## for invalid XML
|
## for invalid XML
|
||||||
errors*: seq[string] ## all detected parsing errors
|
errors*: seq[string] ## all detected parsing errors
|
||||||
|
|
||||||
|
{.deprecated: [EInvalidXml: XmlError].}
|
||||||
|
|
||||||
proc raiseInvalidXml(errors: seq[string]) =
|
proc raiseInvalidXml(errors: seq[string]) =
|
||||||
var e: ref EInvalidXml
|
var e: ref EInvalidXml
|
||||||
|
|
@ -23,12 +25,12 @@ proc raiseInvalidXml(errors: seq[string]) =
|
||||||
e.errors = errors
|
e.errors = errors
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
proc addNode(father, son: PXmlNode) =
|
proc addNode(father, son: XmlNode) =
|
||||||
if son != nil: add(father, son)
|
if son != nil: add(father, son)
|
||||||
|
|
||||||
proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode
|
proc parse(x: var XmlParser, errors: var seq[string]): XmlNode
|
||||||
|
|
||||||
proc untilElementEnd(x: var TXmlParser, result: PXmlNode,
|
proc untilElementEnd(x: var XmlParser, result: XmlNode,
|
||||||
errors: var seq[string]) =
|
errors: var seq[string]) =
|
||||||
while true:
|
while true:
|
||||||
case x.kind
|
case x.kind
|
||||||
|
|
@ -45,7 +47,7 @@ proc untilElementEnd(x: var TXmlParser, result: PXmlNode,
|
||||||
else:
|
else:
|
||||||
result.addNode(parse(x, errors))
|
result.addNode(parse(x, errors))
|
||||||
|
|
||||||
proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode =
|
proc parse(x: var XmlParser, errors: var seq[string]): XmlNode =
|
||||||
case x.kind
|
case x.kind
|
||||||
of xmlComment:
|
of xmlComment:
|
||||||
result = newComment(x.charData)
|
result = newComment(x.charData)
|
||||||
|
|
@ -98,11 +100,11 @@ proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode =
|
||||||
next(x)
|
next(x)
|
||||||
of xmlEof: discard
|
of xmlEof: discard
|
||||||
|
|
||||||
proc parseXml*(s: PStream, filename: string,
|
proc parseXml*(s: Stream, filename: string,
|
||||||
errors: var seq[string]): PXmlNode =
|
errors: var seq[string]): XmlNode =
|
||||||
## parses the XML from stream `s` and returns a ``PXmlNode``. Every
|
## parses the XML from stream `s` and returns a ``PXmlNode``. Every
|
||||||
## occured parsing error is added to the `errors` sequence.
|
## occured parsing error is added to the `errors` sequence.
|
||||||
var x: TXmlParser
|
var x: XmlParser
|
||||||
open(x, s, filename, {reportComments})
|
open(x, s, filename, {reportComments})
|
||||||
while true:
|
while true:
|
||||||
x.next()
|
x.next()
|
||||||
|
|
@ -118,14 +120,14 @@ proc parseXml*(s: PStream, filename: string,
|
||||||
break
|
break
|
||||||
close(x)
|
close(x)
|
||||||
|
|
||||||
proc parseXml*(s: PStream): PXmlNode =
|
proc parseXml*(s: Stream): XmlNode =
|
||||||
## parses the XTML from stream `s` and returns a ``PXmlNode``. All parsing
|
## parses the XTML from stream `s` and returns a ``PXmlNode``. All parsing
|
||||||
## errors are turned into an ``EInvalidXML`` exception.
|
## errors are turned into an ``EInvalidXML`` exception.
|
||||||
var errors: seq[string] = @[]
|
var errors: seq[string] = @[]
|
||||||
result = parseXml(s, "unknown_html_doc", errors)
|
result = parseXml(s, "unknown_html_doc", errors)
|
||||||
if errors.len > 0: raiseInvalidXMl(errors)
|
if errors.len > 0: raiseInvalidXMl(errors)
|
||||||
|
|
||||||
proc loadXml*(path: string, errors: var seq[string]): PXmlNode =
|
proc loadXml*(path: string, errors: var seq[string]): XmlNode =
|
||||||
## Loads and parses XML from file specified by ``path``, and returns
|
## Loads and parses XML from file specified by ``path``, and returns
|
||||||
## a ``PXmlNode``. Every occured parsing error is added to the `errors`
|
## a ``PXmlNode``. Every occured parsing error is added to the `errors`
|
||||||
## sequence.
|
## sequence.
|
||||||
|
|
@ -133,7 +135,7 @@ proc loadXml*(path: string, errors: var seq[string]): PXmlNode =
|
||||||
if s == nil: raise newException(EIO, "Unable to read file: " & path)
|
if s == nil: raise newException(EIO, "Unable to read file: " & path)
|
||||||
result = parseXml(s, path, errors)
|
result = parseXml(s, path, errors)
|
||||||
|
|
||||||
proc loadXml*(path: string): PXmlNode =
|
proc loadXml*(path: string): XmlNode =
|
||||||
## Loads and parses XML from file specified by ``path``, and returns
|
## Loads and parses XML from file specified by ``path``, and returns
|
||||||
## a ``PXmlNode``. All parsing errors are turned into an ``EInvalidXML``
|
## a ``PXmlNode``. All parsing errors are turned into an ``EInvalidXML``
|
||||||
## exception.
|
## exception.
|
||||||
|
|
@ -148,7 +150,7 @@ when isMainModule:
|
||||||
var x = loadXml(paramStr(1), errors)
|
var x = loadXml(paramStr(1), errors)
|
||||||
for e in items(errors): echo e
|
for e in items(errors): echo e
|
||||||
|
|
||||||
var f: TFile
|
var f: File
|
||||||
if open(f, "xmltest.txt", fmWrite):
|
if open(f, "xmltest.txt", fmWrite):
|
||||||
f.write($x)
|
f.write($x)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
|
|
@ -12,33 +12,36 @@
|
||||||
import macros, strtabs
|
import macros, strtabs
|
||||||
|
|
||||||
type
|
type
|
||||||
PXmlNode* = ref TXmlNode ## an XML tree consists of ``PXmlNode``'s.
|
XmlNode* = ref TXmlNode ## an XML tree consists of ``PXmlNode``'s.
|
||||||
|
|
||||||
TXmlNodeKind* = enum ## different kinds of ``PXmlNode``'s
|
XmlNodeKind* = enum ## different kinds of ``PXmlNode``'s
|
||||||
xnText, ## a text element
|
xnText, ## a text element
|
||||||
xnElement, ## an element with 0 or more children
|
xnElement, ## an element with 0 or more children
|
||||||
xnCData, ## a CDATA node
|
xnCData, ## a CDATA node
|
||||||
xnEntity, ## an entity (like ``&thing;``)
|
xnEntity, ## an entity (like ``&thing;``)
|
||||||
xnComment ## an XML comment
|
xnComment ## an XML comment
|
||||||
|
|
||||||
PXmlAttributes* = PStringTable ## an alias for a string to string mapping
|
XmlAttributes* = StringTableRef ## an alias for a string to string mapping
|
||||||
|
|
||||||
TXmlNode {.pure, final, acyclic.} = object
|
XmlNodeObj {.acyclic.} = object
|
||||||
case k: TXmlNodeKind # private, use the kind() proc to read this field.
|
case k: XmlNodeKind # private, use the kind() proc to read this field.
|
||||||
of xnText, xnComment, xnCData, xnEntity:
|
of xnText, xnComment, xnCData, xnEntity:
|
||||||
fText: string
|
fText: string
|
||||||
of xnElement:
|
of xnElement:
|
||||||
fTag: string
|
fTag: string
|
||||||
s: seq[PXmlNode]
|
s: seq[XmlNode]
|
||||||
fAttr: PXmlAttributes
|
fAttr: XmlAttributes
|
||||||
fClientData: int ## for other clients
|
fClientData: int ## for other clients
|
||||||
|
|
||||||
proc newXmlNode(kind: TXmlNodeKind): PXmlNode =
|
{.deprecated: [PXmlNode: XmlNode, TXmlNodeKind: XmlNodeKind, PXmlAttributes:
|
||||||
## creates a new ``PXmlNode``.
|
XmlAttributes, TXmlNode: XmlNodeObj].}
|
||||||
|
|
||||||
|
proc newXmlNode(kind: XmlNodeKind): XmlNode =
|
||||||
|
## creates a new ``XmlNode``.
|
||||||
new(result)
|
new(result)
|
||||||
result.k = kind
|
result.k = kind
|
||||||
|
|
||||||
proc newElement*(tag: string): PXmlNode =
|
proc newElement*(tag: string): XmlNode =
|
||||||
## creates a new ``PXmlNode`` of kind ``xnText`` with the given `tag`.
|
## creates a new ``PXmlNode`` of kind ``xnText`` with the given `tag`.
|
||||||
result = newXmlNode(xnElement)
|
result = newXmlNode(xnElement)
|
||||||
result.fTag = tag
|
result.fTag = tag
|
||||||
|
|
@ -304,10 +307,10 @@ proc findAll*(n: PXmlNode, tag: string, result: var seq[PXmlNode]) =
|
||||||
## Found nodes satisfying the condition will be appended to the `result`
|
## Found nodes satisfying the condition will be appended to the `result`
|
||||||
## sequence, which can't be nil or the proc will crash. Usage example:
|
## sequence, which can't be nil or the proc will crash. Usage example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block::
|
||||||
## var
|
## var
|
||||||
## html: PXmlNode
|
## html: XmlNode
|
||||||
## tags: seq[PXmlNode] = @[]
|
## tags: seq[XmlNode] = @[]
|
||||||
##
|
##
|
||||||
## html = buildHtml()
|
## html = buildHtml()
|
||||||
## findAll(html, "img", tags)
|
## findAll(html, "img", tags)
|
||||||
|
|
@ -326,8 +329,8 @@ proc findAll*(n: PXmlNode, tag: string, result: var seq[PXmlNode]) =
|
||||||
proc findAll*(n: PXmlNode, tag: string): seq[PXmlNode] =
|
proc findAll*(n: PXmlNode, tag: string): seq[PXmlNode] =
|
||||||
## Shortcut version to assign in let blocks. Example:
|
## Shortcut version to assign in let blocks. Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block::
|
||||||
## var html: PXmlNode
|
## var html: XmlNode
|
||||||
##
|
##
|
||||||
## html = buildHtml(html)
|
## html = buildHtml(html)
|
||||||
## for imgTag in html.findAll("img"):
|
## for imgTag in html.findAll("img"):
|
||||||
|
|
|
||||||
|
|
@ -2437,6 +2437,8 @@ when not defined(JS): #and not defined(NimrodVM):
|
||||||
## returns the OS file handle of the file ``f``. This is only useful for
|
## returns the OS file handle of the file ``f``. This is only useful for
|
||||||
## platform specific programming.
|
## platform specific programming.
|
||||||
|
|
||||||
|
{.deprecated: [fileHandle: getFileHandle].}
|
||||||
|
|
||||||
proc cstringArrayToSeq*(a: cstringArray, len: int): seq[string] =
|
proc cstringArrayToSeq*(a: cstringArray, len: int): seq[string] =
|
||||||
## converts a ``cstringArray`` to a ``seq[string]``. `a` is supposed to be
|
## converts a ``cstringArray`` to a ``seq[string]``. `a` is supposed to be
|
||||||
## of length ``len``.
|
## of length ``len``.
|
||||||
|
|
|
||||||
1
todo.txt
1
todo.txt
|
|
@ -49,7 +49,6 @@ Bugs
|
||||||
- bug: type conversions concerning proc types are weird
|
- bug: type conversions concerning proc types are weird
|
||||||
- compilation of niminst takes way too long. looks like a regression
|
- compilation of niminst takes way too long. looks like a regression
|
||||||
- docgen: sometimes effects are listed twice
|
- docgen: sometimes effects are listed twice
|
||||||
- 'result' is not properly cleaned for NRVO --> use uninit checking instead
|
|
||||||
- blocks can "export" an identifier but the CCG generates {} for them ...
|
- blocks can "export" an identifier but the CCG generates {} for them ...
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue