mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
big rename
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# The Nimrod Compiler
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# The Nimrod Compiler
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Configuration file for the Nimrod Compiler.
|
||||
# (c) 2013 Andreas Rumpf
|
||||
# Configuration file for the Nim Compiler.
|
||||
# (c) 2014 Andreas Rumpf
|
||||
|
||||
# Feel free to edit the default values as you need.
|
||||
|
||||
@@ -13,6 +13,8 @@ cc = gcc
|
||||
arm.linux.gcc.exe = "arm-linux-gcc"
|
||||
arm.linux.gcc.linkerexe = "arm-linux-gcc"
|
||||
|
||||
cs:partial
|
||||
|
||||
path="$lib/core"
|
||||
path="$lib/pure"
|
||||
path="$lib/pure/collections"
|
||||
|
||||
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
|
||||
--------
|
||||
|
||||
Check if `s` matches Nimrod's "while" keyword:
|
||||
Check if `s` matches Nim's "while" keyword:
|
||||
|
||||
.. code-block:: nimrod
|
||||
s =~ peg" y'while'"
|
||||
@@ -212,7 +212,7 @@ example ``*`` should not be greedy, so ``\[.*?\]`` should be used instead.
|
||||
|
||||
PEG construction
|
||||
----------------
|
||||
There are two ways to construct a PEG in Nimrod code:
|
||||
There are two ways to construct a PEG in Nim code:
|
||||
(1) Parsing a string into an AST which consists of `TPeg` nodes with the
|
||||
`peg` proc.
|
||||
(2) Constructing the AST directly with proc calls. This method does not
|
||||
|
||||
258
doc/tut1.txt
258
doc/tut1.txt
@@ -1,9 +1,9 @@
|
||||
========================
|
||||
Nimrod Tutorial (Part I)
|
||||
========================
|
||||
=====================
|
||||
Nim Tutorial (Part I)
|
||||
=====================
|
||||
|
||||
:Author: Andreas Rumpf
|
||||
:Version: |nimrodversion|
|
||||
:Version: |nimversion|
|
||||
|
||||
.. contents::
|
||||
|
||||
@@ -16,7 +16,7 @@ Introduction
|
||||
</p></blockquote>
|
||||
|
||||
|
||||
This document is a tutorial for the programming language *Nimrod*.
|
||||
This document is a tutorial for the programming language *Nim*.
|
||||
This tutorial assumes that you are familiar with basic programming concepts
|
||||
like variables, types or statements but is kept very basic. The `manual
|
||||
<manual.html>`_ contains many more examples of the advanced language features.
|
||||
@@ -28,7 +28,7 @@ The first program
|
||||
|
||||
We start the tour with a modified "hello world" program:
|
||||
|
||||
.. code-block:: Nimrod
|
||||
.. code-block:: Nim
|
||||
# This is a comment
|
||||
echo("What's your name? ")
|
||||
var name: string = readLine(stdin)
|
||||
@@ -37,30 +37,30 @@ We start the tour with a modified "hello world" program:
|
||||
|
||||
Save this code to the file "greetings.nim". Now compile and run it::
|
||||
|
||||
nimrod compile --run greetings.nim
|
||||
nim compile --run greetings.nim
|
||||
|
||||
With the ``--run`` `switch <nimrodc.html#command-line-switches>`_ Nimrod
|
||||
With the ``--run`` `switch <nimc.html#command-line-switches>`_ Nim
|
||||
executes the file automatically after compilation. You can give your program
|
||||
command line arguments by appending them after the filename::
|
||||
|
||||
nimrod compile --run greetings.nim arg1 arg2
|
||||
nim compile --run greetings.nim arg1 arg2
|
||||
|
||||
Commonly used commands and switches have abbreviations, so you can also use::
|
||||
|
||||
nimrod c -r greetings.nim
|
||||
nim c -r greetings.nim
|
||||
|
||||
To compile a release version use::
|
||||
|
||||
nimrod c -d:release greetings.nim
|
||||
nim c -d:release greetings.nim
|
||||
|
||||
By default the Nimrod compiler generates a large amount of runtime checks
|
||||
By default the Nim compiler generates a large amount of runtime checks
|
||||
aiming for your debugging pleasure. With ``-d:release`` these checks are
|
||||
`turned off and optimizations are turned on
|
||||
<nimrodc.html#compile-time-symbols>`_.
|
||||
<nimc.html#compile-time-symbols>`_.
|
||||
|
||||
Though it should be pretty obvious what the program does, I will explain the
|
||||
syntax: statements which are not indented are executed when the program
|
||||
starts. Indentation is Nimrod's way of grouping statements. Indentation is
|
||||
starts. Indentation is Nim's way of grouping statements. Indentation is
|
||||
done with spaces only, tabulators are not allowed.
|
||||
|
||||
String literals are enclosed in double quotes. The ``var`` statement declares
|
||||
@@ -70,11 +70,11 @@ compiler knows that `readLine <system.html#readLine,TFile>`_ returns a string,
|
||||
you can leave out the type in the declaration (this is called `local type
|
||||
inference`:idx:). So this will work too:
|
||||
|
||||
.. code-block:: Nimrod
|
||||
.. code-block:: Nim
|
||||
var name = readLine(stdin)
|
||||
|
||||
Note that this is basically the only form of type inference that exists in
|
||||
Nimrod: it is a good compromise between brevity and readability.
|
||||
Nim: it is a good compromise between brevity and readability.
|
||||
|
||||
The "hello world" program contains several identifiers that are already known
|
||||
to the compiler: ``echo``, `readLine <system.html#readLine,TFile>`_, etc.
|
||||
@@ -85,8 +85,8 @@ imported by any other module.
|
||||
Lexical elements
|
||||
================
|
||||
|
||||
Let us look at Nimrod's lexical elements in more detail: like other
|
||||
programming languages Nimrod consists of (string) literals, identifiers,
|
||||
Let us look at Nim's lexical elements in more detail: like other
|
||||
programming languages Nim consists of (string) literals, identifiers,
|
||||
keywords, comments, operators, and other punctuation marks.
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ String literals are enclosed in double quotes; character literals in single
|
||||
quotes. Special characters are escaped with ``\``: ``\n`` means newline, ``\t``
|
||||
means tabulator, etc. There are also *raw* string literals:
|
||||
|
||||
.. code-block:: Nimrod
|
||||
.. code-block:: Nim
|
||||
r"C:\program files\nim"
|
||||
|
||||
In raw literals the backslash is not an escape character.
|
||||
@@ -115,7 +115,7 @@ Comments start anywhere outside a string or character literal with the
|
||||
hash character ``#``. Documentation comments start with ``##``. Multiline
|
||||
comments need to be aligned at the same column:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
|
||||
i = 0 # This is a single comment over multiple lines belonging to the
|
||||
# assignment statement.
|
||||
@@ -128,7 +128,7 @@ comments need to be aligned at the same column:
|
||||
The alignment requirement does not hold if the preceding comment piece ends in
|
||||
a backslash:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
type
|
||||
TMyObject {.final, pure, acyclic.} = object # comment continues: \
|
||||
# we have lots of space here to comment 'TMyObject'.
|
||||
@@ -151,15 +151,15 @@ the syntax, watch their indentation:
|
||||
**Note**: To comment out a large piece of code, it is often better to use a
|
||||
``when false:`` statement.
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
when false:
|
||||
brokenCode()
|
||||
|
||||
Another option is to use the `discard statement`_ together with *long string
|
||||
literals* to create block comments:
|
||||
|
||||
.. code-block:: nimrod
|
||||
discard """ You can have any Nimrod code text commented
|
||||
.. code-block:: nim
|
||||
discard """ You can have any Nim code text commented
|
||||
out inside this with no indentation restrictions.
|
||||
yes("May I ask a pointless question?") """
|
||||
|
||||
@@ -204,7 +204,7 @@ to a storage location:
|
||||
|
||||
``=`` is the *assignment operator*. The assignment operator cannot be
|
||||
overloaded, overwritten or forbidden, but this might change in a future version
|
||||
of Nimrod. You can declare multiple variables with a single assignment
|
||||
of Nim. You can declare multiple variables with a single assignment
|
||||
statement and all the variables will have the same value:
|
||||
|
||||
.. code-block::
|
||||
@@ -229,7 +229,7 @@ Constants are symbols which are bound to a value. The constant's value
|
||||
cannot change. The compiler must be able to evaluate the expression in a
|
||||
constant declaration at compile time:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
const x = "abc" # the constant x contains the string "abc"
|
||||
|
||||
Indentation can be used after the ``const`` keyword to list a whole section of
|
||||
@@ -277,7 +277,7 @@ If statement
|
||||
|
||||
The if statement is one way to branch the control flow:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
let name = readLine(stdin)
|
||||
if name == "":
|
||||
echo("Poor soul, you lost your name?")
|
||||
@@ -298,7 +298,7 @@ Case statement
|
||||
Another way to branch is provided by the case statement. A case statement is
|
||||
a multi-branch:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
let name = readLine(stdin)
|
||||
case name
|
||||
of "":
|
||||
@@ -317,7 +317,7 @@ The case statement can deal with integers, other ordinal types and strings.
|
||||
(What an ordinal type is will be explained soon.)
|
||||
For integers or other ordinal types value ranges are also possible:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
# this statement will be explained later:
|
||||
from strutils import parseInt
|
||||
|
||||
@@ -333,7 +333,7 @@ every value that ``n`` may contain, but the code only handles the values
|
||||
(though it is possible thanks to the range notation), we fix this by telling
|
||||
the compiler that for every other value nothing should be done:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
...
|
||||
case n
|
||||
of 0..2, 4..7: echo("The number is in the set: {0, 1, 2, 4, 5, 6, 7}")
|
||||
@@ -355,7 +355,7 @@ While statement
|
||||
|
||||
The while statement is a simple looping construct:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
|
||||
echo("What's your name? ")
|
||||
var name = readLine(stdin)
|
||||
@@ -375,7 +375,7 @@ The ``for`` statement is a construct to loop over any element an *iterator*
|
||||
provides. The example uses the built-in `countup <system.html#countup>`_
|
||||
iterator:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
echo("Counting to ten: ")
|
||||
for i in countup(1, 10):
|
||||
echo($i)
|
||||
@@ -387,7 +387,7 @@ other types into a string. The variable ``i`` is implicitly declared by the
|
||||
<system.html#countup>`_ returns. ``i`` runs through the values 1, 2, .., 10.
|
||||
Each value is ``echo``-ed. This code does the same:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
echo("Counting to 10: ")
|
||||
var i = 1
|
||||
while i <= 10:
|
||||
@@ -397,16 +397,16 @@ Each value is ``echo``-ed. This code does the same:
|
||||
|
||||
Counting down can be achieved as easily (but is less often needed):
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
echo("Counting down from 10 to 1: ")
|
||||
for i in countdown(10, 1):
|
||||
echo($i)
|
||||
# --> Outputs 10 9 8 7 6 5 4 3 2 1 on different lines
|
||||
|
||||
Since counting up occurs so often in programs, Nimrod also has a `..
|
||||
Since counting up occurs so often in programs, Nim also has a `..
|
||||
<system.html#...i,S,T>`_ iterator that does the same:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
for i in 1..10:
|
||||
...
|
||||
|
||||
@@ -417,7 +417,7 @@ Control flow statements have a feature not covered yet: they open a
|
||||
new scope. This means that in the following example, ``x`` is not accessible
|
||||
outside the loop:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
while false:
|
||||
var x = "hi"
|
||||
echo(x) # does not work
|
||||
@@ -426,7 +426,7 @@ A while (for) statement introduces an implicit block. Identifiers
|
||||
are only visible within the block they have been declared. The ``block``
|
||||
statement can be used to open a new block explicitly:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
block myblock:
|
||||
var x = "hi"
|
||||
echo(x) # does not work either
|
||||
@@ -440,7 +440,7 @@ A block can be left prematurely with a ``break`` statement. The break statement
|
||||
can leave a ``while``, ``for``, or a ``block`` statement. It leaves the
|
||||
innermost construct, unless a label of a block is given:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
block myblock:
|
||||
echo("entering block")
|
||||
while true:
|
||||
@@ -461,7 +461,7 @@ Continue statement
|
||||
Like in many other programming languages, a ``continue`` statement starts
|
||||
the next iteration immediately:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
while true:
|
||||
let x = readLine(stdin)
|
||||
if x == "": continue
|
||||
@@ -473,7 +473,7 @@ When statement
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
|
||||
when system.hostOS == "windows":
|
||||
echo("running on Windows!")
|
||||
@@ -504,17 +504,17 @@ possible.
|
||||
Statements and indentation
|
||||
==========================
|
||||
|
||||
Now that we covered the basic control flow statements, let's return to Nimrod
|
||||
Now that we covered the basic control flow statements, let's return to Nim
|
||||
indentation rules.
|
||||
|
||||
In Nimrod there is a distinction between *simple statements* and *complex
|
||||
In Nim there is a distinction between *simple statements* and *complex
|
||||
statements*. *Simple statements* cannot contain other statements:
|
||||
Assignment, procedure calls or the ``return`` statement belong to the simple
|
||||
statements. *Complex statements* like ``if``, ``when``, ``for``, ``while`` can
|
||||
contain other statements. To avoid ambiguities, complex statements always have
|
||||
to be indented, but single simple statements do not:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
# no indentation needed for single assignment statement:
|
||||
if x: x = false
|
||||
|
||||
@@ -535,7 +535,7 @@ to be indented, but single simple statements do not:
|
||||
condition in an if statement is an example for an expression. Expressions can
|
||||
contain indentation at certain places for better readability:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
|
||||
if thisIsaLongCondition() and
|
||||
thisIsAnotherLongCondition(1,
|
||||
@@ -548,7 +548,7 @@ an open parenthesis and after commas.
|
||||
With parenthesis and semicolons ``(;)`` you can use statements where only
|
||||
an expression is allowed:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
# computes fac(4) at compile time:
|
||||
const fac4 = (var x = 1; for i in 1..4: x *= i; x)
|
||||
|
||||
@@ -558,10 +558,10 @@ Procedures
|
||||
|
||||
To define new commands like `echo <system.html#echo>`_ and `readLine
|
||||
<system.html#readLine,TFile>`_ in the examples, the concept of a `procedure`
|
||||
is needed. (Some languages call them *methods* or *functions*.) In Nimrod new
|
||||
is needed. (Some languages call them *methods* or *functions*.) In Nim new
|
||||
procedures are defined with the ``proc`` keyword:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
proc yes(question: string): bool =
|
||||
echo(question, " (y/n)")
|
||||
while true:
|
||||
@@ -597,7 +597,7 @@ shorthand for ``return result``. The ``result`` value is always returned
|
||||
automatically at the end a procedure if there is no ``return`` statement at
|
||||
the exit.
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
proc sumTillNegative(x: varargs[int]): int =
|
||||
for i in x:
|
||||
if i < 0:
|
||||
@@ -624,7 +624,7 @@ most efficient way. If a mutable variable is needed inside the procedure, it has
|
||||
to be declared with ``var`` in the procedure body. Shadowing the parameter name
|
||||
is possible, and actually an idiom:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
proc printSeq(s: seq, nprinted: int = -1) =
|
||||
var nprinted = if nprinted == -1: s.len else: min(nprinted, s.len)
|
||||
for i in 0 .. <nprinted:
|
||||
@@ -633,7 +633,7 @@ is possible, and actually an idiom:
|
||||
If the procedure needs to modify the argument for the
|
||||
caller, a ``var`` parameter can be used:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
proc divmod(a, b: int; res, remainder: var int) =
|
||||
res = a div b # integer division
|
||||
remainder = a mod b # integer modulo operation
|
||||
@@ -653,17 +653,17 @@ a tuple as a return value instead of using var parameters.
|
||||
Discard statement
|
||||
-----------------
|
||||
To call a procedure that returns a value just for its side effects and ignoring
|
||||
its return value, a ``discard`` statement **has** to be used. Nimrod does not
|
||||
its return value, a ``discard`` statement **has** to be used. Nim does not
|
||||
allow to silently throw away a return value:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
discard yes("May I ask a pointless question?")
|
||||
|
||||
|
||||
The return value can be ignored implicitly if the called proc/iterator has
|
||||
been declared with the ``discardable`` pragma:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
proc p(x, y: int): int {.discardable.} =
|
||||
return x + y
|
||||
|
||||
@@ -681,7 +681,7 @@ parameters appear. This is especially true for procedures that construct a
|
||||
complex data type. Therefore the arguments to a procedure can be named, so
|
||||
that it is clear which argument belongs to which parameter:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
proc createWindow(x, y, width, height: int; title: string;
|
||||
show: bool): Window =
|
||||
...
|
||||
@@ -693,7 +693,7 @@ Now that we use named arguments to call ``createWindow`` the argument order
|
||||
does not matter anymore. Mixing named arguments with ordered arguments is
|
||||
also possible, but not very readable:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
var w = createWindow(0, 0, title = "My Application",
|
||||
height = 600, width = 800, true)
|
||||
|
||||
@@ -706,7 +706,7 @@ To make the ``createWindow`` proc easier to use it should provide `default
|
||||
values`, these are values that are used as arguments if the caller does not
|
||||
specify them:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
proc createWindow(x = 0, y = 0, width = 500, height = 700,
|
||||
title = "unknown",
|
||||
show = true): Window =
|
||||
@@ -723,9 +723,9 @@ no need to write ``title: string = "unknown"``, for example.
|
||||
|
||||
Overloaded procedures
|
||||
---------------------
|
||||
Nimrod provides the ability to overload procedures similar to C++:
|
||||
Nim provides the ability to overload procedures similar to C++:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
proc toString(x: int): string = ...
|
||||
proc toString(x: bool): string =
|
||||
if x: result = "true"
|
||||
@@ -735,7 +735,7 @@ Nimrod provides the ability to overload procedures similar to C++:
|
||||
echo(toString(true)) # calls the toString(x: bool) proc
|
||||
|
||||
(Note that ``toString`` is usually the `$ <system.html#$>`_ operator in
|
||||
Nimrod.) The compiler chooses the most appropriate proc for the ``toString``
|
||||
Nim.) The compiler chooses the most appropriate proc for the ``toString``
|
||||
calls. How this overloading resolution algorithm works exactly is not
|
||||
discussed here (it will be specified in the manual soon). However, it does
|
||||
not lead to nasty surprises and is based on a quite simple unification
|
||||
@@ -744,13 +744,13 @@ algorithm. Ambiguous calls are reported as errors.
|
||||
|
||||
Operators
|
||||
---------
|
||||
The Nimrod library makes heavy use of overloading - one reason for this is that
|
||||
The Nim library makes heavy use of overloading - one reason for this is that
|
||||
each operator like ``+`` is a just an overloaded proc. The parser lets you
|
||||
use operators in `infix notation` (``a + b``) or `prefix notation` (``+ a``).
|
||||
An infix operator always receives two arguments, a prefix operator always one.
|
||||
Postfix operators are not possible, because this would be ambiguous: does
|
||||
``a @ @ b`` mean ``(a) @ (@b)`` or ``(a@) @ (b)``? It always means
|
||||
``(a) @ (@b)``, because there are no postfix operators in Nimrod.
|
||||
``(a) @ (@b)``, because there are no postfix operators in Nim.
|
||||
|
||||
Apart from a few built-in keyword operators such as ``and``, ``or``, ``not``,
|
||||
operators always consist of these characters:
|
||||
@@ -764,7 +764,7 @@ can be found in the manual.
|
||||
|
||||
To define a new operator enclose the operator in backticks "``":
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
proc `$` (x: myDataType): string = ...
|
||||
# now the $ operator also works with myDataType, overloading resolution
|
||||
# ensures that $ works for built-in types just like before
|
||||
@@ -772,7 +772,7 @@ To define a new operator enclose the operator in backticks "``":
|
||||
The "``" notation can also be used to call an operator just like any other
|
||||
procedure:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
if `==`( `+`(3, 4), 7): echo("True")
|
||||
|
||||
|
||||
@@ -783,7 +783,7 @@ Every variable, procedure, etc. needs to be declared before it can be used.
|
||||
(The reason for this is compilation efficiency.)
|
||||
However, this cannot be done for mutually recursive procedures:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
# forward declaration:
|
||||
proc even(n: int): bool
|
||||
|
||||
@@ -810,7 +810,7 @@ Iterators
|
||||
|
||||
Let's return to the boring counting example:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
echo("Counting to ten: ")
|
||||
for i in countup(1, 10):
|
||||
echo($i)
|
||||
@@ -818,7 +818,7 @@ Let's return to the boring counting example:
|
||||
Can a `countup <system.html#countup>`_ proc be written that supports this
|
||||
loop? Lets try:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
proc countup(a, b: int): int =
|
||||
var res = a
|
||||
while res <= b:
|
||||
@@ -831,7 +831,7 @@ finished. This *return and continue* is called a `yield` statement. Now
|
||||
the only thing left to do is to replace the ``proc`` keyword by ``iterator``
|
||||
and there it is - our first iterator:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
iterator countup(a, b: int): int =
|
||||
var res = a
|
||||
while res <= b:
|
||||
@@ -868,7 +868,7 @@ that are available for them in detail.
|
||||
Booleans
|
||||
--------
|
||||
|
||||
The boolean type is named ``bool`` in Nimrod and consists of the two
|
||||
The boolean type is named ``bool`` in Nim and consists of the two
|
||||
pre-defined values ``true`` and ``false``. Conditions in while,
|
||||
if, elif, when statements need to be of type bool.
|
||||
|
||||
@@ -876,7 +876,7 @@ The operators ``not, and, or, xor, <, <=, >, >=, !=, ==`` are defined
|
||||
for the bool type. The ``and`` and ``or`` operators perform short-cut
|
||||
evaluation. Example:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
|
||||
while p != nil and p.name != "xyz":
|
||||
# p.name is not evaluated if p == nil
|
||||
@@ -885,7 +885,7 @@ evaluation. Example:
|
||||
|
||||
Characters
|
||||
----------
|
||||
The `character type` is named ``char`` in Nimrod. Its size is one byte.
|
||||
The `character type` is named ``char`` in Nim. Its size is one byte.
|
||||
Thus it cannot represent an UTF-8 character, but a part of it.
|
||||
The reason for this is efficiency: for the overwhelming majority of use-cases,
|
||||
the resulting programs will still handle UTF-8 properly as UTF-8 was specially
|
||||
@@ -900,13 +900,13 @@ Converting from an integer to a ``char`` is done with the ``chr`` proc.
|
||||
|
||||
Strings
|
||||
-------
|
||||
String variables in Nimrod are **mutable**, so appending to a string
|
||||
is quite efficient. Strings in Nimrod are both zero-terminated and have a
|
||||
String variables in Nim are **mutable**, so appending to a string
|
||||
is quite efficient. Strings in Nim are both zero-terminated and have a
|
||||
length field. One can retrieve a string's length with the builtin ``len``
|
||||
procedure; the length never counts the terminating zero. Accessing the
|
||||
terminating zero is no error and often leads to simpler code:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
if s[i] == 'a' and s[i+1] == 'b':
|
||||
# no need to check whether ``i < len(s)``!
|
||||
...
|
||||
@@ -929,14 +929,14 @@ object on the heap, so there is a trade-off to be made here.
|
||||
|
||||
Integers
|
||||
--------
|
||||
Nimrod has these integer types built-in:
|
||||
Nim has these integer types built-in:
|
||||
``int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64``.
|
||||
|
||||
The default integer type is ``int``. Integer literals can have a *type suffix*
|
||||
to mark them to be of another integer type:
|
||||
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
let
|
||||
x = 0 # x is of type ``int``
|
||||
y = 0'i8 # y is of type ``int8``
|
||||
@@ -964,7 +964,7 @@ cannot be detected at compile time).
|
||||
|
||||
Floats
|
||||
------
|
||||
Nimrod has these floating point types built-in: ``float float32 float64``.
|
||||
Nim has these floating point types built-in: ``float float32 float64``.
|
||||
|
||||
The default float type is ``float``. In the current implementation,
|
||||
``float`` is always 64 bit wide.
|
||||
@@ -972,7 +972,7 @@ The default float type is ``float``. In the current implementation,
|
||||
Float literals can have a *type suffix* to mark them to be of another float
|
||||
type:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
var
|
||||
x = 0.0 # x is of type ``float``
|
||||
y = 0.0'f32 # y is of type ``float32``
|
||||
@@ -1001,11 +1001,11 @@ having to write its ``$`` operator. You can use then the `repr
|
||||
graphs with cycles. The following example shows that even for basic types
|
||||
there is a difference between the ``$`` and ``repr`` outputs:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
var
|
||||
myBool = true
|
||||
myCharacter = 'n'
|
||||
myString = "nimrod"
|
||||
myString = "nim"
|
||||
myInteger = 42
|
||||
myFloat = 3.14
|
||||
echo($myBool, ":", repr(myBool))
|
||||
@@ -1013,7 +1013,7 @@ there is a difference between the ``$`` and ``repr`` outputs:
|
||||
echo($myCharacter, ":", repr(myCharacter))
|
||||
# --> n:'n'
|
||||
echo($myString, ":", repr(myString))
|
||||
# --> nimrod:0x10fa8c050"nimrod"
|
||||
# --> nim:0x10fa8c050"nim"
|
||||
echo($myInteger, ":", repr(myInteger))
|
||||
# --> 42:42
|
||||
echo($myFloat, ":", repr(myFloat))
|
||||
@@ -1023,9 +1023,9 @@ there is a difference between the ``$`` and ``repr`` outputs:
|
||||
Advanced types
|
||||
==============
|
||||
|
||||
In Nimrod new types can be defined within a ``type`` statement:
|
||||
In Nim new types can be defined within a ``type`` statement:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
type
|
||||
biggestInt = int64 # biggest integer type that is available
|
||||
biggestFloat = float64 # biggest float type that is available
|
||||
@@ -1041,7 +1041,7 @@ limited set. This set consists of ordered symbols. Each symbol is mapped
|
||||
to an integer value internally. The first symbol is represented
|
||||
at runtime by 0, the second by 1 and so on. Example:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
|
||||
type
|
||||
TDirection = enum
|
||||
@@ -1050,7 +1050,7 @@ at runtime by 0, the second by 1 and so on. Example:
|
||||
var x = south # `x` is of type `TDirection`; its value is `south`
|
||||
echo($x) # writes "south" to `stdout`
|
||||
|
||||
(To prefix a new type with the letter ``T`` is a convention in Nimrod.)
|
||||
(To prefix a new type with the letter ``T`` is a convention in Nim.)
|
||||
All comparison operators can be used with enumeration types.
|
||||
|
||||
An enumeration's symbol can be qualified to avoid ambiguities:
|
||||
@@ -1066,7 +1066,7 @@ explicitly given is assigned the value of the previous symbol + 1.
|
||||
|
||||
An explicit ordered enum can have *holes*:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
type
|
||||
TMyEnum = enum
|
||||
a = 2, b = 4, c = 89
|
||||
@@ -1104,7 +1104,7 @@ Subranges
|
||||
A subrange type is a range of values from an integer or enumeration type
|
||||
(the base type). Example:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
type
|
||||
TSubrange = range[0..5]
|
||||
|
||||
@@ -1119,7 +1119,7 @@ type as ``range[0..high(int)]`` (`high <system.html#high>`_ returns the
|
||||
maximal value). Other programming languages mandate the usage of unsigned
|
||||
integers for natural numbers. This is often **wrong**: you don't want unsigned
|
||||
arithmetic (which wraps around) just because the numbers cannot be negative.
|
||||
Nimrod's ``Natural`` type helps to avoid this common programming error.
|
||||
Nim's ``Natural`` type helps to avoid this common programming error.
|
||||
|
||||
|
||||
Sets
|
||||
@@ -1134,7 +1134,7 @@ the array has the same type. The array's index type can be any ordinal type.
|
||||
|
||||
Arrays can be constructed via ``[]``:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
|
||||
type
|
||||
TIntArray = array[0..5, int] # an array that is indexed with 0..5
|
||||
@@ -1149,14 +1149,14 @@ Array access is always bounds checked (at compile-time or at runtime). These
|
||||
checks can be disabled via pragmas or invoking the compiler with the
|
||||
``--bound_checks:off`` command line switch.
|
||||
|
||||
Arrays are value types, like any other Nimrod type. The assignment operator
|
||||
Arrays are value types, like any other Nim type. The assignment operator
|
||||
copies the whole array contents.
|
||||
|
||||
The built-in `len <system.html#len,TOpenArray>`_ proc returns the array's
|
||||
length. `low(a) <system.html#low>`_ returns the lowest valid index for the
|
||||
array `a` and `high(a) <system.html#high>`_ the highest valid index.
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
type
|
||||
TDirection = enum
|
||||
north, east, south, west
|
||||
@@ -1175,13 +1175,13 @@ array `a` and `high(a) <system.html#high>`_ the highest valid index.
|
||||
|
||||
The syntax for nested arrays (multidimensional) in other languages is a matter
|
||||
of appending more brackets because usually each dimension is restricted to the
|
||||
same index type as the others. In nimrod you can have different dimensions with
|
||||
same index type as the others. In Nim you can have different dimensions with
|
||||
different index types, so the nesting syntax is slightly different. Building on
|
||||
the previous example where a level is defined as an array of enums indexed by
|
||||
yet another enum, we can add the following lines to add a light tower type
|
||||
subdivided in height levels accessed through their integer index:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
type
|
||||
TLightTower = array[1..10, TLevelSetting]
|
||||
var
|
||||
@@ -1200,14 +1200,14 @@ length. Another way of defining the ``TLightTower`` to show better its
|
||||
nested nature would be to omit the previous definition of the ``TLevelSetting``
|
||||
type and instead write it embedded directly as the type of the first dimension:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
type
|
||||
TLightTower = array[1..10, array[north..west, TBlinkLights]]
|
||||
|
||||
It is quite frequent to have arrays start at zero, so there's a shortcut syntax
|
||||
to specify a range from zero to the specified index minus one:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
type
|
||||
TIntArray = array[0..5, int] # an array that is indexed with 0..5
|
||||
TQuickArray = array[6, int] # an array that is indexed with 0..5
|
||||
@@ -1239,7 +1239,7 @@ A sequence may be passed to an openarray parameter.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
|
||||
var
|
||||
x: seq[int] # a sequence of integers
|
||||
@@ -1261,7 +1261,7 @@ value. Here the ``for`` statement is looping over the results from the
|
||||
`pairs() <system.html#pairs.i,seq[T]>`_ iterator from the `system
|
||||
<system.html>`_ module. Examples:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
for i in @[3, 4, 5]:
|
||||
echo($i)
|
||||
# --> 3
|
||||
@@ -1299,7 +1299,7 @@ also a means to implement passing a variable number of
|
||||
arguments to a procedure. The compiler converts the list of arguments
|
||||
to an array automatically:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
proc myWriteln(f: TFile, a: varargs[string]) =
|
||||
for s in items(a):
|
||||
write(f, s)
|
||||
@@ -1313,7 +1313,7 @@ This transformation is only done if the varargs parameter is the
|
||||
last parameter in the procedure header. It is also possible to perform
|
||||
type conversions in this context:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
proc myWriteln(f: TFile, a: varargs[string, `$`]) =
|
||||
for s in items(a):
|
||||
write(f, s)
|
||||
@@ -1336,10 +1336,10 @@ context. A slice is just an object of type TSlice which contains two bounds,
|
||||
`a` and `b`. By itself a slice is not very useful, but other collection types
|
||||
define operators which accept TSlice objects to define ranges.
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
|
||||
var
|
||||
a = "Nimrod is a progamming language"
|
||||
a = "Nim is a progamming language"
|
||||
b = "Slices are useless."
|
||||
|
||||
echo a[10..15] # --> 'a prog'
|
||||
@@ -1366,7 +1366,7 @@ The assignment operator for tuples copies each component. The notation
|
||||
``t[i]`` to access the ``i``'th field. Here ``i`` needs to be a constant
|
||||
integer.
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
|
||||
type
|
||||
TPerson = tuple[name: string, age: int] # type representing a person:
|
||||
@@ -1411,19 +1411,19 @@ use parenthesis around the values you want to assign the unpacking to,
|
||||
otherwise you will be assigning the same value to all the individual
|
||||
variables! Example:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
|
||||
import os
|
||||
|
||||
let
|
||||
path = "usr/local/nimrodc.html"
|
||||
path = "usr/local/nimc.html"
|
||||
(dir, name, ext) = splitFile(path)
|
||||
baddir, badname, badext = splitFile(path)
|
||||
echo dir # outputs `usr/local`
|
||||
echo name # outputs `nimrodc`
|
||||
echo name # outputs `nimc`
|
||||
echo ext # outputs `.html`
|
||||
# All the following output the same line:
|
||||
# `(dir: usr/local, name: nimrodc, ext: .html)`
|
||||
# `(dir: usr/local, name: nimc, ext: .html)`
|
||||
echo baddir
|
||||
echo badname
|
||||
echo badext
|
||||
@@ -1431,12 +1431,12 @@ variables! Example:
|
||||
Tuple unpacking **only** works in ``var`` or ``let`` blocks. The following code
|
||||
won't compile:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
|
||||
import os
|
||||
|
||||
var
|
||||
path = "usr/local/nimrodc.html"
|
||||
path = "usr/local/nimc.html"
|
||||
dir, name, ext = ""
|
||||
|
||||
(dir, name, ext) = splitFile(path)
|
||||
@@ -1449,7 +1449,7 @@ References (similar to pointers in other programming languages) are a
|
||||
way to introduce many-to-one relationships. This means different references can
|
||||
point to and modify the same location in memory.
|
||||
|
||||
Nimrod distinguishes between `traced`:idx: and `untraced`:idx: references.
|
||||
Nim distinguishes between `traced`:idx: and `untraced`:idx: references.
|
||||
Untraced references are also called *pointers*. Traced references point to
|
||||
objects of a garbage collected heap, untraced references point to
|
||||
manually allocated objects or to objects somewhere else in memory. Thus
|
||||
@@ -1464,7 +1464,7 @@ meaning to retrieve the item the reference points to. The ``.`` (access a
|
||||
tuple/object field operator) and ``[]`` (array/string/sequence index operator)
|
||||
operators perform implicit dereferencing operations for reference types:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
|
||||
type
|
||||
PNode = ref TNode
|
||||
@@ -1489,12 +1489,12 @@ Procedural type
|
||||
---------------
|
||||
A procedural type is a (somewhat abstract) pointer to a procedure.
|
||||
``nil`` is an allowed value for a variable of a procedural type.
|
||||
Nimrod uses procedural types to achieve `functional`:idx: programming
|
||||
Nim uses procedural types to achieve `functional`:idx: programming
|
||||
techniques.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
proc echoItem(x: int) = echo(x)
|
||||
|
||||
proc forEach(action: proc (x: int)) =
|
||||
@@ -1513,13 +1513,13 @@ listed in the `manual <manual.html>`_.
|
||||
|
||||
Modules
|
||||
=======
|
||||
Nimrod supports splitting a program into pieces with a module concept.
|
||||
Nim supports splitting a program into pieces with a module concept.
|
||||
Each module is in its own file. Modules enable `information hiding`:idx: and
|
||||
`separate compilation`:idx:. A module may gain access to symbols of another
|
||||
module by the `import`:idx: statement. Only top-level symbols that are marked
|
||||
with an asterisk (``*``) are exported:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
# Module A
|
||||
var
|
||||
x*, y: int
|
||||
@@ -1554,7 +1554,7 @@ The algorithm for compiling modules is:
|
||||
|
||||
This is best illustrated by an example:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
# Module A
|
||||
type
|
||||
T1* = int # Module A exports the type ``T1``
|
||||
@@ -1565,7 +1565,7 @@ This is best illustrated by an example:
|
||||
|
||||
main()
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
# Module B
|
||||
import A # A is not parsed here! Only the already known symbols
|
||||
# of A are imported.
|
||||
@@ -1581,15 +1581,15 @@ the symbol is ambiguous, it even *has* to be qualified. A symbol is ambiguous
|
||||
if it is defined in two (or more) different modules and both modules are
|
||||
imported by a third one:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
# Module A
|
||||
var x*: string
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
# Module B
|
||||
var x*: int
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
# Module C
|
||||
import A, B
|
||||
write(stdout, x) # error: x is ambiguous
|
||||
@@ -1602,15 +1602,15 @@ imported by a third one:
|
||||
But this rule does not apply to procedures or iterators. Here the overloading
|
||||
rules apply:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
# Module A
|
||||
proc x*(a: int): string = result = $a
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
# Module B
|
||||
proc x*(a: string): string = result = $a
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
# Module C
|
||||
import A, B
|
||||
write(stdout, x(3)) # no error: A.x is called
|
||||
@@ -1627,7 +1627,7 @@ The normal ``import`` statement will bring in all exported symbols.
|
||||
These can be limited by naming symbols which should be excluded with
|
||||
the ``except`` qualifier.
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
import mymodule except y
|
||||
|
||||
|
||||
@@ -1638,19 +1638,19 @@ We have already seen the simple ``import`` statement that just imports all
|
||||
exported symbols. An alternative that only imports listed symbols is the
|
||||
``from import`` statement:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
from mymodule import x, y, z
|
||||
|
||||
The ``from`` statement can also force namespace qualification on
|
||||
symbols, thereby making symbols available, but needing to be qualified
|
||||
to be used.
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
from mymodule import x, y, z
|
||||
|
||||
x() # use x without any qualification
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
from mymodule import nil
|
||||
|
||||
mymodule.x() # must qualify x with the module name as prefix
|
||||
@@ -1660,7 +1660,7 @@ to be used.
|
||||
Since module names are generally long to be descriptive, you can also
|
||||
define a shorter alias to use when qualifying symbols.
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
from mymodule as m import nil
|
||||
|
||||
m.x() # m is aliasing mymodule
|
||||
@@ -1672,7 +1672,7 @@ The ``include`` statement does something fundamentally different than
|
||||
importing a module: it merely includes the contents of a file. The ``include``
|
||||
statement is useful to split up a large module into several files:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
include fileA, fileB, fileC
|
||||
|
||||
**Note**: The documentation generator currently does not follow ``include``
|
||||
@@ -1683,7 +1683,7 @@ generated documentation.
|
||||
Part 2
|
||||
======
|
||||
|
||||
So, now that we are done with the basics, let's see what Nimrod offers apart
|
||||
So, now that we are done with the basics, let's see what Nim offers apart
|
||||
from a nice syntax for procedural programming: `Part II <tut2.html>`_
|
||||
|
||||
|
||||
|
||||
139
doc/tut2.txt
139
doc/tut2.txt
@@ -1,9 +1,9 @@
|
||||
=========================
|
||||
Nimrod Tutorial (Part II)
|
||||
=========================
|
||||
======================
|
||||
Nim Tutorial (Part II)
|
||||
======================
|
||||
|
||||
:Author: Andreas Rumpf
|
||||
:Version: |nimrodversion|
|
||||
:Version: |nimversion|
|
||||
|
||||
.. contents::
|
||||
|
||||
@@ -15,7 +15,7 @@ Introduction
|
||||
only have originated in California." --Edsger Dijkstra
|
||||
|
||||
|
||||
This document is a tutorial for the advanced constructs of the *Nimrod*
|
||||
This document is a tutorial for the advanced constructs of the *Nim*
|
||||
programming language. **Note that this document is somewhat obsolete as the**
|
||||
`manual <manual.html>`_ **contains many more examples of the advanced language
|
||||
features.**
|
||||
@@ -24,18 +24,18 @@ features.**
|
||||
Pragmas
|
||||
=======
|
||||
|
||||
Pragmas are Nimrod's method to give the compiler additional information/
|
||||
Pragmas are Nim's method to give the compiler additional information/
|
||||
commands without introducing a massive number of new keywords. Pragmas are
|
||||
enclosed in the special ``{.`` and ``.}`` curly dot brackets. This tutorial
|
||||
does not cover pragmas. See the `manual <manual.html#pragmas>`_ or `user guide
|
||||
<nimrodc.html#additional-features>`_ for a description of the available
|
||||
<nimc.html#additional-features>`_ for a description of the available
|
||||
pragmas.
|
||||
|
||||
|
||||
Object Oriented Programming
|
||||
===========================
|
||||
|
||||
While Nimrod's support for object oriented programming (OOP) is minimalistic,
|
||||
While Nim's support for object oriented programming (OOP) is minimalistic,
|
||||
powerful OOP technics can be used. OOP is seen as *one* way to design a
|
||||
program, not *the only* way. Often a procedural approach leads to simpler
|
||||
and more efficient code. In particular, prefering composition over inheritance
|
||||
@@ -55,7 +55,7 @@ a *constructor*).
|
||||
Objects have access to their type at runtime. There is an
|
||||
``of`` operator that can be used to check the object's type:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
type
|
||||
TPerson = object of TObject
|
||||
name*: string # the * means that `name` is accessible from other modules
|
||||
@@ -86,20 +86,20 @@ in the GTK wrapper for instance.)
|
||||
|
||||
**Note**: Composition (*has-a* relation) is often preferable to inheritance
|
||||
(*is-a* relation) for simple code reuse. Since objects are value types in
|
||||
Nimrod, composition is as efficient as inheritance.
|
||||
Nim, composition is as efficient as inheritance.
|
||||
|
||||
|
||||
Mutually recursive types
|
||||
------------------------
|
||||
|
||||
Objects, tuples and references can model quite complex data structures which
|
||||
depend on each other; they are *mutually recursive*. In Nimrod
|
||||
depend on each other; they are *mutually recursive*. In Nim
|
||||
these types can only be declared within a single type section. (Anything else
|
||||
would require arbitrary symbol lookahead which slows down compilation.)
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
type
|
||||
PNode = ref TNode # a traced reference to a TNode
|
||||
TNode = object
|
||||
@@ -114,7 +114,7 @@ Example:
|
||||
|
||||
Type conversions
|
||||
----------------
|
||||
Nimrod distinguishes between `type casts`:idx: and `type conversions`:idx:.
|
||||
Nim distinguishes between `type casts`:idx: and `type conversions`:idx:.
|
||||
Casts are done with the ``cast`` operator and force the compiler to
|
||||
interpret a bit pattern to be of another type.
|
||||
|
||||
@@ -126,7 +126,7 @@ raised.
|
||||
The syntax for type conversions is ``destination_type(expression_to_convert)``
|
||||
(like an ordinary call):
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
proc getID(x: TPerson): int =
|
||||
TStudent(x).id
|
||||
|
||||
@@ -141,9 +141,9 @@ variant types are needed.
|
||||
|
||||
An example:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
|
||||
# This is an example how an abstract syntax tree could be modeled in Nimrod
|
||||
# This is an example how an abstract syntax tree could be modeled in Nim
|
||||
type
|
||||
TNodeKind = enum # the different node types
|
||||
nkInt, # a leaf with an integer value
|
||||
@@ -183,8 +183,8 @@ bound to a class. This has disadvantages:
|
||||
* Often it is unclear where the method should belong to: is
|
||||
``join`` a string method or an array method?
|
||||
|
||||
Nimrod avoids these problems by not assigning methods to a class. All methods
|
||||
in Nimrod are multi-methods. As we will see later, multi-methods are
|
||||
Nim avoids these problems by not assigning methods to a class. All methods
|
||||
in Nim are multi-methods. As we will see later, multi-methods are
|
||||
distinguished from procs only for dynamic binding purposes.
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ If there are no remaining arguments, the parentheses can be omitted:
|
||||
This method call syntax is not restricted to objects, it can be used
|
||||
for any type:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
|
||||
echo("abc".len) # is the same as echo(len("abc"))
|
||||
echo("abc".toUpper())
|
||||
@@ -211,7 +211,7 @@ postfix notation.)
|
||||
|
||||
So "pure object oriented" code is easy to write:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
import strutils
|
||||
|
||||
stdout.writeln("Give a list of numbers (separated by spaces): ")
|
||||
@@ -221,12 +221,12 @@ So "pure object oriented" code is easy to write:
|
||||
|
||||
Properties
|
||||
----------
|
||||
As the above example shows, Nimrod has no need for *get-properties*:
|
||||
As the above example shows, Nim has no need for *get-properties*:
|
||||
Ordinary get-procedures that are called with the *method call syntax* achieve
|
||||
the same. But setting a value is different; for this a special setter syntax
|
||||
is needed:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
|
||||
type
|
||||
TSocket* = object of TObject
|
||||
@@ -252,7 +252,7 @@ is needed:
|
||||
The ``[]`` array access operator can be overloaded to provide
|
||||
`array properties`:idx:\ :
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
type
|
||||
TVector* = object
|
||||
x, y, z: float
|
||||
@@ -283,7 +283,7 @@ Dynamic dispatch
|
||||
Procedures always use static dispatch. For dynamic dispatch replace the
|
||||
``proc`` keyword by ``method``:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
type
|
||||
PExpr = ref object of TObject ## abstract base class for an expression
|
||||
PLiteral = ref object of PExpr
|
||||
@@ -311,7 +311,7 @@ requires dynamic binding.
|
||||
In a multi-method all parameters that have an object type are used for the
|
||||
dispatching:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
|
||||
type
|
||||
TThing = object of TObject
|
||||
@@ -336,7 +336,7 @@ As the example demonstrates, invocation of a multi-method cannot be ambiguous:
|
||||
Collide 2 is preferred over collide 1 because the resolution works from left to
|
||||
right. Thus ``TUnit, TThing`` is preferred over ``TThing, TUnit``.
|
||||
|
||||
**Perfomance note**: Nimrod does not produce a virtual method table, but
|
||||
**Perfomance note**: Nim does not produce a virtual method table, but
|
||||
generates dispatch trees. This avoids the expensive indirect branch for method
|
||||
calls and enables inlining. However, other optimizations like compile time
|
||||
evaluation or dead code elimination do not work with methods.
|
||||
@@ -345,7 +345,7 @@ evaluation or dead code elimination do not work with methods.
|
||||
Exceptions
|
||||
==========
|
||||
|
||||
In Nimrod exceptions are objects. By convention, exception types are
|
||||
In Nim exceptions are objects. By convention, exception types are
|
||||
prefixed with an 'E', not 'T'. The `system <system.html>`_ module defines an
|
||||
exception hierarchy that you might want to stick to. Exceptions derive from
|
||||
E_Base, which provides the common interface.
|
||||
@@ -364,7 +364,7 @@ Raise statement
|
||||
---------------
|
||||
Raising an exception is done with the ``raise`` statement:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
var
|
||||
e: ref EOS
|
||||
new(e)
|
||||
@@ -375,7 +375,7 @@ If the ``raise`` keyword is not followed by an expression, the last exception
|
||||
is *re-raised*. For the purpose of avoiding repeating this common code pattern,
|
||||
the template ``newException`` in the ``system`` module can be used:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
raise newException(EOS, "the request to the OS failed")
|
||||
|
||||
|
||||
@@ -384,7 +384,7 @@ Try statement
|
||||
|
||||
The ``try`` statement handles exceptions:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
# read the first two lines of a text file that should contain numbers
|
||||
# and tries to add them
|
||||
var
|
||||
@@ -428,7 +428,7 @@ If you need to *access* the actual exception object or message inside an
|
||||
<system.html#getCurrentExceptionMsg>`_ procs from the `system <system.html>`_
|
||||
module. Example:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
try:
|
||||
doSomethingHere()
|
||||
except:
|
||||
@@ -460,7 +460,7 @@ instance, if you specify that a proc raises ``EIO``, and at some point it (or
|
||||
one of the procs it calls) starts raising a new exception the compiler will
|
||||
prevent that proc from compiling. Usage example:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
proc complexProc() {.raises: [EIO, EArithmetic].} =
|
||||
...
|
||||
|
||||
@@ -476,21 +476,21 @@ help you locate the offending code which has changed.
|
||||
If you want to add the ``{.raises.}`` pragma to existing code, the compiler can
|
||||
also help you. You can add the ``{.effects.}`` pragma statement to your proc and
|
||||
the compiler will output all inferred effects up to that point (exception
|
||||
tracking is part of Nimrod's effect system). Another more roundabout way to
|
||||
find out the list of exceptions raised by a proc is to use the Nimrod ``doc2``
|
||||
tracking is part of Nim's effect system). Another more roundabout way to
|
||||
find out the list of exceptions raised by a proc is to use the Nim ``doc2``
|
||||
command which generates documentation for a whole module and decorates all
|
||||
procs with the list of raised exceptions. You can read more about Nimrod's
|
||||
procs with the list of raised exceptions. You can read more about Nim's
|
||||
`effect system and related pragmas in the manual <manual.html#effect-system>`_.
|
||||
|
||||
|
||||
Generics
|
||||
========
|
||||
|
||||
Generics are Nimrod's means to parametrize procs, iterators or types
|
||||
Generics are Nim's means to parametrize procs, iterators or types
|
||||
with `type parameters`:idx:. They are most useful for efficient type safe
|
||||
containers:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
type
|
||||
TBinaryTree[T] = object # TBinaryTree is a generic type with
|
||||
# with generic param ``T``
|
||||
@@ -557,7 +557,7 @@ is not hidden and is used in the ``preorder`` iterator.
|
||||
Templates
|
||||
=========
|
||||
|
||||
Templates are a simple substitution mechanism that operates on Nimrod's
|
||||
Templates are a simple substitution mechanism that operates on Nim's
|
||||
abstract syntax trees. Templates are processed in the semantic pass of the
|
||||
compiler. They integrate well with the rest of the language and share none
|
||||
of C's preprocessor macros flaws.
|
||||
@@ -566,7 +566,7 @@ To *invoke* a template, call it like a procedure.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
template `!=` (a, b: expr): expr =
|
||||
# this definition exists in the System module
|
||||
not (a == b)
|
||||
@@ -585,7 +585,7 @@ for IEEE floating point numbers - NaN breaks basic boolean logic.)
|
||||
Templates are especially useful for lazy evaluation purposes. Consider a
|
||||
simple proc for logging:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
const
|
||||
debug = true
|
||||
|
||||
@@ -602,7 +602,7 @@ evaluation for procedures is *eager*).
|
||||
|
||||
Turning the ``log`` proc into a template solves this problem:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
const
|
||||
debug = true
|
||||
|
||||
@@ -618,32 +618,11 @@ The parameters' types can be ordinary types or the meta types ``expr``
|
||||
(stands for *type description*). If the template has no explicit return type,
|
||||
``stmt`` is used for consistency with procs and methods.
|
||||
|
||||
The template body does not open a new scope. To open a new scope use a ``block``
|
||||
statement:
|
||||
|
||||
.. code-block:: nimrod
|
||||
template declareInScope(x: expr, t: typeDesc): stmt {.immediate.} =
|
||||
var x: t
|
||||
|
||||
template declareInNewScope(x: expr, t: typeDesc): stmt {.immediate.} =
|
||||
# open a new scope:
|
||||
block:
|
||||
var x: t
|
||||
|
||||
declareInScope(a, int)
|
||||
a = 42 # works, `a` is known here
|
||||
|
||||
declareInNewScope(b, int)
|
||||
b = 42 # does not work, `b` is unknown
|
||||
|
||||
(The `manual explains <manual.html#ordinary-vs-immediate-templates>`_ why the
|
||||
``immediate`` pragma is needed for these templates.)
|
||||
|
||||
If there is a ``stmt`` parameter it should be the last in the template
|
||||
declaration. The reason is that statements can be passed to a template
|
||||
via a special ``:`` syntax:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
|
||||
template withFile(f: expr, filename: string, mode: TFileMode,
|
||||
body: stmt): stmt {.immediate.} =
|
||||
@@ -672,18 +651,18 @@ Macros
|
||||
======
|
||||
|
||||
Macros enable advanced compile-time code transformations, but they cannot
|
||||
change Nimrod's syntax. However, this is no real restriction because Nimrod's
|
||||
syntax is flexible enough anyway. Macros have to be implemented in pure Nimrod
|
||||
change Nim's syntax. However, this is no real restriction because Nim's
|
||||
syntax is flexible enough anyway. Macros have to be implemented in pure Nim
|
||||
code if `foreign function interface (FFI)
|
||||
<manual.html#foreign-function-interface>`_ is not enabled in the compiler, but
|
||||
other than that restriction (which at some point in the future will go away)
|
||||
you can write any kind of Nimrod code and the compiler will run it at compile
|
||||
you can write any kind of Nim code and the compiler will run it at compile
|
||||
time.
|
||||
|
||||
There are two ways to write a macro, either *generating* Nimrod source code and
|
||||
There are two ways to write a macro, either *generating* Nim source code and
|
||||
letting the compiler parse it, or creating manually an abstract syntax tree
|
||||
(AST) which you feed to the compiler. In order to build the AST one needs to
|
||||
know how the Nimrod concrete syntax is converted to an abstract syntax tree
|
||||
know how the Nim concrete syntax is converted to an abstract syntax tree
|
||||
(AST). The AST is documented in the `macros <macros.html>`_ module.
|
||||
|
||||
Once your macro is finished, there are two ways to invoke it:
|
||||
@@ -698,13 +677,13 @@ Expression Macros
|
||||
The following example implements a powerful ``debug`` command that accepts a
|
||||
variable number of arguments:
|
||||
|
||||
.. code-block:: nimrod
|
||||
# to work with Nimrod syntax trees, we need an API that is defined in the
|
||||
.. code-block:: nim
|
||||
# to work with Nim syntax trees, we need an API that is defined in the
|
||||
# ``macros`` module:
|
||||
import macros
|
||||
|
||||
macro debug(n: varargs[expr]): stmt =
|
||||
# `n` is a Nimrod AST that contains a list of expressions;
|
||||
# `n` is a Nim AST that contains a list of expressions;
|
||||
# this macro returns a list of statements:
|
||||
result = newNimNode(nnkStmtList, n)
|
||||
# iterate over any argument that is passed to this macro:
|
||||
@@ -727,7 +706,7 @@ variable number of arguments:
|
||||
|
||||
The macro call expands to:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
write(stdout, "a[0]")
|
||||
write(stdout, ": ")
|
||||
writeln(stdout, a[0])
|
||||
@@ -751,7 +730,7 @@ invoked by an expression following a colon.
|
||||
The following example outlines a macro that generates a lexical analyzer from
|
||||
regular expressions:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
|
||||
macro case_token(n: stmt): stmt =
|
||||
# creates a lexical analyzer from regular expressions
|
||||
@@ -784,7 +763,7 @@ To give a footstart to writing macros we will show now how to turn your typical
|
||||
dynamic code into something that compiles statically. For the exercise we will
|
||||
use the following snippet of code as the starting point:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
|
||||
import strutils, tables
|
||||
|
||||
@@ -848,7 +827,7 @@ time string with the *generated source code*, which we then pass to the
|
||||
``parseStmt`` proc from the `macros module <macros.html>`_. Here is the
|
||||
modified source code implementing the macro:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
import macros, strutils
|
||||
|
||||
macro readCfgAndBuildSource(cfgFilename: string): stmt =
|
||||
@@ -893,13 +872,13 @@ this limitation by using the ``slurp`` proc from the `system module
|
||||
``gorge`` which executes an external program and captures its output).
|
||||
|
||||
The interesting thing is that our macro does not return a runtime ``TTable``
|
||||
object. Instead, it builds up Nimrod source code into the ``source`` variable.
|
||||
object. Instead, it builds up Nim source code into the ``source`` variable.
|
||||
For each line of the configuration file a ``const`` variable will be generated.
|
||||
To avoid conflicts we prefix these variables with ``cfg``. In essence, what the
|
||||
compiler is doing is replacing the line calling the macro with the following
|
||||
snippet of code:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
const cfgversion= "1.1"
|
||||
const cfglicenseOwner= "Hyori Lee"
|
||||
const cfglicenseKey= "M1Tl3PjBWO2CC48m"
|
||||
@@ -919,14 +898,14 @@ Generating AST by hand
|
||||
++++++++++++++++++++++
|
||||
|
||||
To generate an AST we would need to intimately know the structures used by the
|
||||
Nimrod compiler exposed in the `macros module <macros.html>`_, which at first
|
||||
Nim compiler exposed in the `macros module <macros.html>`_, which at first
|
||||
look seems a daunting task. But we can use as helper shortcut the ``dumpTree``
|
||||
macro, which is used as a statement macro instead of an expression macro.
|
||||
Since we know that we want to generate a bunch of ``const`` symbols we can
|
||||
create the following source file and compile it to see what the compiler
|
||||
*expects* from us:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
import macros
|
||||
|
||||
dumpTree:
|
||||
@@ -969,7 +948,7 @@ identifier, optionally a type (can be an *empty* node) and the value. Armed
|
||||
with this knowledge, let's look at the finished version of the AST building
|
||||
macro:
|
||||
|
||||
.. code-block:: nimrod
|
||||
.. code-block:: nim
|
||||
import macros, strutils
|
||||
|
||||
macro readCfgAndBuildAST(cfgFilename: string): stmt =
|
||||
|
||||
@@ -15,7 +15,7 @@ var links = 0 # count the number of links
|
||||
var filename = addFileExt(ParamStr(1), "html")
|
||||
var s = newFileStream(filename, fmRead)
|
||||
if s == nil: quit("cannot open the file " & filename)
|
||||
var x: TXmlParser
|
||||
var x: XmlParser
|
||||
open(x, s, filename)
|
||||
next(x) # get first event
|
||||
block mainLoop:
|
||||
|
||||
@@ -10,7 +10,7 @@ if paramCount() < 1:
|
||||
var filename = addFileExt(paramStr(1), "html")
|
||||
var s = newFileStream(filename, fmRead)
|
||||
if s == nil: quit("cannot open the file " & filename)
|
||||
var x: TXmlParser
|
||||
var x: XmlParser
|
||||
open(x, s, filename)
|
||||
while true:
|
||||
x.next()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -21,6 +21,11 @@
|
||||
## for i in 0 .. < 300:
|
||||
## a.spawn(i, proc (x: int) {.thread.} = echo x)
|
||||
## a.join()
|
||||
##
|
||||
## **Note**: This whole module is deprecated. Use `threadpool` and ``spawn``
|
||||
## instead.
|
||||
|
||||
{.deprecated.}
|
||||
|
||||
from os import sleep
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -10,9 +10,12 @@
|
||||
## This module implements some common generic algorithms.
|
||||
|
||||
type
|
||||
TSortOrder* = enum ## sort order
|
||||
SortOrder* = enum ## sort order
|
||||
Descending, Ascending
|
||||
|
||||
{.deprecated: [TSortOrder: SortOrder].}
|
||||
|
||||
|
||||
proc `*`*(x: int, order: TSortOrder): int {.inline.} =
|
||||
## flips `x` if ``order == Descending``;
|
||||
## if ``order == Ascending`` then `x` is returned.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2014 Dominik Picheta
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -13,7 +13,7 @@ import os, oids, tables, strutils, macros, times
|
||||
|
||||
import rawsockets, net
|
||||
|
||||
export TPort, TSocketFlags
|
||||
export Port, SocketFlags
|
||||
|
||||
#{.injectStmt: newGcInvariant().}
|
||||
|
||||
@@ -36,21 +36,24 @@ export TPort, TSocketFlags
|
||||
# -- Futures
|
||||
|
||||
type
|
||||
PFutureBase* = ref object of PObject
|
||||
FutureBase* = ref object of RootObj
|
||||
cb: proc () {.closure,gcsafe.}
|
||||
finished: bool
|
||||
error*: ref EBase
|
||||
error*: ref Exception
|
||||
errorStackTrace*: string
|
||||
when not defined(release):
|
||||
stackTrace: string ## For debugging purposes only.
|
||||
id: int
|
||||
fromProc: string
|
||||
|
||||
PFuture*[T] = ref object of PFutureBase
|
||||
Future*[T] = ref object of FutureBase
|
||||
value: T
|
||||
|
||||
{.deprecated: [PFutureBase: FutureBase, PFuture: Future].}
|
||||
|
||||
|
||||
var currentID* = 0
|
||||
proc newFuture*[T](fromProc: string = "unspecified"): PFuture[T] =
|
||||
proc newFuture*[T](fromProc: string = "unspecified"): Future[T] =
|
||||
## Creates a new future.
|
||||
##
|
||||
## Specifying ``fromProc``, which is a string specifying the name of the proc
|
||||
@@ -63,7 +66,7 @@ proc newFuture*[T](fromProc: string = "unspecified"): PFuture[T] =
|
||||
result.fromProc = fromProc
|
||||
currentID.inc()
|
||||
|
||||
proc checkFinished[T](future: PFuture[T]) =
|
||||
proc checkFinished[T](future: Future[T]) =
|
||||
when not defined(release):
|
||||
if future.finished:
|
||||
echo("<-----> ", future.id, " ", future.fromProc)
|
||||
@@ -76,7 +79,7 @@ proc checkFinished[T](future: PFuture[T]) =
|
||||
echo getStackTrace()
|
||||
assert false
|
||||
|
||||
proc complete*[T](future: PFuture[T], val: T) =
|
||||
proc complete*[T](future: Future[T], val: T) =
|
||||
## Completes ``future`` with value ``val``.
|
||||
#assert(not future.finished, "Future already finished, cannot finish twice.")
|
||||
checkFinished(future)
|
||||
@@ -86,7 +89,7 @@ proc complete*[T](future: PFuture[T], val: T) =
|
||||
if future.cb != nil:
|
||||
future.cb()
|
||||
|
||||
proc complete*(future: PFuture[void]) =
|
||||
proc complete*(future: Future[void]) =
|
||||
## Completes a void ``future``.
|
||||
#assert(not future.finished, "Future already finished, cannot finish twice.")
|
||||
checkFinished(future)
|
||||
@@ -95,7 +98,7 @@ proc complete*(future: PFuture[void]) =
|
||||
if future.cb != nil:
|
||||
future.cb()
|
||||
|
||||
proc fail*[T](future: PFuture[T], error: ref EBase) =
|
||||
proc fail*[T](future: Future[T], error: ref Exception) =
|
||||
## Completes ``future`` with ``error``.
|
||||
#assert(not future.finished, "Future already finished, cannot finish twice.")
|
||||
checkFinished(future)
|
||||
@@ -111,8 +114,9 @@ proc fail*[T](future: PFuture[T], error: ref EBase) =
|
||||
# TODO: This may turn out to be a bad idea.
|
||||
# Turns out this is a bad idea.
|
||||
#raise error
|
||||
discard
|
||||
|
||||
proc `callback=`*(future: PFutureBase, cb: proc () {.closure,gcsafe.}) =
|
||||
proc `callback=`*(future: FutureBase, cb: proc () {.closure,gcsafe.}) =
|
||||
## Sets the callback proc to be called when the future completes.
|
||||
##
|
||||
## If future has already completed then ``cb`` will be called immediately.
|
||||
@@ -123,23 +127,23 @@ proc `callback=`*(future: PFutureBase, cb: proc () {.closure,gcsafe.}) =
|
||||
if future.finished:
|
||||
future.cb()
|
||||
|
||||
proc `callback=`*[T](future: PFuture[T],
|
||||
cb: proc (future: PFuture[T]) {.closure,gcsafe.}) =
|
||||
proc `callback=`*[T](future: Future[T],
|
||||
cb: proc (future: Future[T]) {.closure,gcsafe.}) =
|
||||
## Sets the callback proc to be called when the future completes.
|
||||
##
|
||||
## If future has already completed then ``cb`` will be called immediately.
|
||||
future.callback = proc () = cb(future)
|
||||
|
||||
proc echoOriginalStackTrace[T](future: PFuture[T]) =
|
||||
proc echoOriginalStackTrace[T](future: Future[T]) =
|
||||
# TODO: Come up with something better.
|
||||
when not defined(release):
|
||||
echo("Original stack trace in ", future.fromProc, ":")
|
||||
if not future.errorStackTrace.isNil() and future.errorStackTrace != "":
|
||||
if not future.errorStackTrace.isNil and future.errorStackTrace != "":
|
||||
echo(future.errorStackTrace)
|
||||
else:
|
||||
echo("Empty or nil stack trace.")
|
||||
|
||||
proc read*[T](future: PFuture[T]): T =
|
||||
proc read*[T](future: Future[T]): T =
|
||||
## Retrieves the value of ``future``. Future must be finished otherwise
|
||||
## this function will fail with a ``EInvalidValue`` exception.
|
||||
##
|
||||
@@ -154,22 +158,22 @@ proc read*[T](future: PFuture[T]): T =
|
||||
# TODO: Make a custom exception type for this?
|
||||
raise newException(EInvalidValue, "Future still in progress.")
|
||||
|
||||
proc readError*[T](future: PFuture[T]): ref EBase =
|
||||
proc readError*[T](future: Future[T]): ref EBase =
|
||||
if future.error != nil: return future.error
|
||||
else:
|
||||
raise newException(EInvalidValue, "No error in future.")
|
||||
|
||||
proc finished*[T](future: PFuture[T]): bool =
|
||||
proc finished*[T](future: Future[T]): bool =
|
||||
## Determines whether ``future`` has completed.
|
||||
##
|
||||
## ``True`` may indicate an error or a value. Use ``failed`` to distinguish.
|
||||
future.finished
|
||||
|
||||
proc failed*(future: PFutureBase): bool =
|
||||
proc failed*(future: FutureBase): bool =
|
||||
## Determines whether ``future`` completed with an error.
|
||||
future.error != nil
|
||||
|
||||
proc asyncCheck*[T](future: PFuture[T]) =
|
||||
proc asyncCheck*[T](future: Future[T]) =
|
||||
## Sets a callback on ``future`` which raises an exception if the future
|
||||
## finished with an error.
|
||||
##
|
||||
@@ -180,7 +184,7 @@ proc asyncCheck*[T](future: PFuture[T]) =
|
||||
echoOriginalStackTrace(future)
|
||||
raise future.error
|
||||
|
||||
proc `and`*[T, Y](fut1: PFuture[T], fut2: PFuture[Y]): PFuture[void] =
|
||||
proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
|
||||
## Returns a future which will complete once both ``fut1`` and ``fut2``
|
||||
## complete.
|
||||
var retFuture = newFuture[void]()
|
||||
@@ -192,7 +196,7 @@ proc `and`*[T, Y](fut1: PFuture[T], fut2: PFuture[Y]): PFuture[void] =
|
||||
if fut1.finished: retFuture.complete()
|
||||
return retFuture
|
||||
|
||||
proc `or`*[T, Y](fut1: PFuture[T], fut2: PFuture[Y]): PFuture[void] =
|
||||
proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
|
||||
## Returns a future which will complete once either ``fut1`` or ``fut2``
|
||||
## complete.
|
||||
var retFuture = newFuture[void]()
|
||||
@@ -203,7 +207,7 @@ proc `or`*[T, Y](fut1: PFuture[T], fut2: PFuture[Y]): PFuture[void] =
|
||||
|
||||
type
|
||||
PDispatcherBase = ref object of PObject
|
||||
timers: seq[tuple[finishAt: float, fut: PFuture[void]]]
|
||||
timers: seq[tuple[finishAt: float, fut: Future[void]]]
|
||||
|
||||
proc processTimers(p: PDispatcherBase) =
|
||||
var oldTimers = p.timers
|
||||
@@ -375,10 +379,10 @@ when defined(windows) or defined(nimdoc):
|
||||
RemoteSockaddr, RemoteSockaddrLength)
|
||||
|
||||
proc connect*(socket: TAsyncFD, address: string, port: TPort,
|
||||
af = AF_INET): PFuture[void] =
|
||||
af = AF_INET): Future[void] =
|
||||
## Connects ``socket`` to server at ``address:port``.
|
||||
##
|
||||
## Returns a ``PFuture`` which will complete when the connection succeeds
|
||||
## Returns a ``Future`` which will complete when the connection succeeds
|
||||
## or an error occurs.
|
||||
verifyPresence(socket)
|
||||
var retFuture = newFuture[void]("connect")
|
||||
@@ -437,7 +441,7 @@ when defined(windows) or defined(nimdoc):
|
||||
return retFuture
|
||||
|
||||
proc recv*(socket: TAsyncFD, size: int,
|
||||
flags = {TSocketFlags.SafeDisconn}): PFuture[string] =
|
||||
flags = {TSocketFlags.SafeDisconn}): Future[string] =
|
||||
## Reads **up to** ``size`` bytes from ``socket``. Returned future will
|
||||
## complete once all the data requested is read, a part of the data has been
|
||||
## read, or the socket has disconnected in which case the future will
|
||||
@@ -525,7 +529,7 @@ when defined(windows) or defined(nimdoc):
|
||||
return retFuture
|
||||
|
||||
proc send*(socket: TAsyncFD, data: string,
|
||||
flags = {TSocketFlags.SafeDisconn}): PFuture[void] =
|
||||
flags = {TSocketFlags.SafeDisconn}): Future[void] =
|
||||
## Sends ``data`` to ``socket``. The returned future will complete once all
|
||||
## data has been sent.
|
||||
verifyPresence(socket)
|
||||
@@ -568,7 +572,7 @@ when defined(windows) or defined(nimdoc):
|
||||
return retFuture
|
||||
|
||||
proc acceptAddr*(socket: TAsyncFD, flags = {TSocketFlags.SafeDisconn}):
|
||||
PFuture[tuple[address: string, client: TAsyncFD]] =
|
||||
Future[tuple[address: string, client: TAsyncFD]] =
|
||||
## Accepts a new connection. Returns a future containing the client socket
|
||||
## corresponding to that connection and the remote address of the client.
|
||||
## The future will complete when the connection is successfully accepted.
|
||||
@@ -789,7 +793,7 @@ else:
|
||||
processTimers(p)
|
||||
|
||||
proc connect*(socket: TAsyncFD, address: string, port: TPort,
|
||||
af = AF_INET): PFuture[void] =
|
||||
af = AF_INET): Future[void] =
|
||||
var retFuture = newFuture[void]("connect")
|
||||
|
||||
proc cb(sock: TAsyncFD): bool =
|
||||
@@ -824,7 +828,7 @@ else:
|
||||
return retFuture
|
||||
|
||||
proc recv*(socket: TAsyncFD, size: int,
|
||||
flags = {TSocketFlags.SafeDisconn}): PFuture[string] =
|
||||
flags = {TSocketFlags.SafeDisconn}): Future[string] =
|
||||
var retFuture = newFuture[string]("recv")
|
||||
|
||||
var readBuffer = newString(size)
|
||||
@@ -855,7 +859,7 @@ else:
|
||||
return retFuture
|
||||
|
||||
proc send*(socket: TAsyncFD, data: string,
|
||||
flags = {TSocketFlags.SafeDisconn}): PFuture[void] =
|
||||
flags = {TSocketFlags.SafeDisconn}): Future[void] =
|
||||
var retFuture = newFuture[void]("send")
|
||||
|
||||
var written = 0
|
||||
@@ -887,7 +891,7 @@ else:
|
||||
return retFuture
|
||||
|
||||
proc acceptAddr*(socket: TAsyncFD, flags = {TSocketFlags.SafeDisconn}):
|
||||
PFuture[tuple[address: string, client: TAsyncFD]] =
|
||||
Future[tuple[address: string, client: TAsyncFD]] =
|
||||
var retFuture = newFuture[tuple[address: string,
|
||||
client: TAsyncFD]]("acceptAddr")
|
||||
proc cb(sock: TAsyncFD): bool =
|
||||
@@ -912,7 +916,7 @@ else:
|
||||
addRead(socket, cb)
|
||||
return retFuture
|
||||
|
||||
proc sleepAsync*(ms: int): PFuture[void] =
|
||||
proc sleepAsync*(ms: int): Future[void] =
|
||||
## Suspends the execution of the current async procedure for the next
|
||||
## ``ms`` miliseconds.
|
||||
var retFuture = newFuture[void]("sleepAsync")
|
||||
@@ -921,14 +925,14 @@ proc sleepAsync*(ms: int): PFuture[void] =
|
||||
return retFuture
|
||||
|
||||
proc accept*(socket: TAsyncFD,
|
||||
flags = {TSocketFlags.SafeDisconn}): PFuture[TAsyncFD] =
|
||||
flags = {TSocketFlags.SafeDisconn}): Future[TAsyncFD] =
|
||||
## Accepts a new connection. Returns a future containing the client socket
|
||||
## corresponding to that connection.
|
||||
## The future will complete when the connection is successfully accepted.
|
||||
var retFut = newFuture[TAsyncFD]("accept")
|
||||
var fut = acceptAddr(socket, flags)
|
||||
fut.callback =
|
||||
proc (future: PFuture[tuple[address: string, client: TAsyncFD]]) =
|
||||
proc (future: Future[tuple[address: string, client: TAsyncFD]]) =
|
||||
assert future.finished
|
||||
if future.failed:
|
||||
retFut.fail(future.error)
|
||||
@@ -1111,12 +1115,12 @@ macro async*(prc: stmt): stmt {.immediate.} =
|
||||
hint("Processing " & prc[0].getName & " as an async proc.")
|
||||
|
||||
let returnType = prc[3][0]
|
||||
# Verify that the return type is a PFuture[T]
|
||||
# Verify that the return type is a Future[T]
|
||||
if returnType.kind == nnkIdent:
|
||||
error("Expected return type of 'PFuture' got '" & $returnType & "'")
|
||||
error("Expected return type of 'Future' got '" & $returnType & "'")
|
||||
elif returnType.kind == nnkBracketExpr:
|
||||
if $returnType[0] != "PFuture":
|
||||
error("Expected return type of 'PFuture' got '" & $returnType[0] & "'")
|
||||
if $returnType[0] != "Future":
|
||||
error("Expected return type of 'Future' got '" & $returnType[0] & "'")
|
||||
|
||||
let subtypeIsVoid = returnType.kind == nnkEmpty or
|
||||
(returnType.kind == nnkBracketExpr and
|
||||
@@ -1137,7 +1141,7 @@ macro async*(prc: stmt): stmt {.immediate.} =
|
||||
subRetType),
|
||||
newLit(prc[0].getName)))) # Get type from return type of this proc
|
||||
|
||||
# -> iterator nameIter(): PFutureBase {.closure.} =
|
||||
# -> iterator nameIter(): FutureBase {.closure.} =
|
||||
# -> var result: T
|
||||
# -> <proc_body>
|
||||
# -> complete(retFuture, result)
|
||||
@@ -1153,7 +1157,7 @@ macro async*(prc: stmt): stmt {.immediate.} =
|
||||
# -> complete(retFuture)
|
||||
procBody.add(newCall(newIdentNode("complete"), retFutureSym))
|
||||
|
||||
var closureIterator = newProc(iteratorNameSym, [newIdentNode("PFutureBase")],
|
||||
var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase")],
|
||||
procBody, nnkIteratorDef)
|
||||
closureIterator[4] = newNimNode(nnkPragma, prc[6]).add(newIdentNode("closure"))
|
||||
outerProcBody.add(closureIterator)
|
||||
@@ -1176,8 +1180,8 @@ macro async*(prc: stmt): stmt {.immediate.} =
|
||||
if subtypeIsVoid:
|
||||
# Add discardable pragma.
|
||||
if returnType.kind == nnkEmpty:
|
||||
# Add PFuture[void]
|
||||
result[3][0] = parseExpr("PFuture[void]")
|
||||
# Add Future[void]
|
||||
result[3][0] = parseExpr("Future[void]")
|
||||
|
||||
result[6] = outerProcBody
|
||||
|
||||
@@ -1185,7 +1189,7 @@ macro async*(prc: stmt): stmt {.immediate.} =
|
||||
#if prc[0].getName == "processClient":
|
||||
# echo(toStrLit(result))
|
||||
|
||||
proc recvLine*(socket: TAsyncFD): PFuture[string] {.async.} =
|
||||
proc recvLine*(socket: TAsyncFD): Future[string] {.async.} =
|
||||
## Reads a line of data from ``socket``. Returned future will complete once
|
||||
## a full line is read or an error occurs.
|
||||
##
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2014 Dominik Picheta
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
import strtabs, asyncnet, asyncdispatch, parseutils, uri, strutils
|
||||
type
|
||||
TRequest* = object
|
||||
Request* = object
|
||||
client*: PAsyncSocket # TODO: Separate this into a Response object?
|
||||
reqMethod*: string
|
||||
headers*: PStringTable
|
||||
@@ -22,10 +22,10 @@ type
|
||||
hostname*: string ## The hostname of the client that made the request.
|
||||
body*: string
|
||||
|
||||
PAsyncHttpServer* = ref object
|
||||
AsyncHttpServer* = ref object
|
||||
socket: PAsyncSocket
|
||||
|
||||
THttpCode* = enum
|
||||
HttpCode* = enum
|
||||
Http200 = "200 OK",
|
||||
Http303 = "303 Moved",
|
||||
Http400 = "400 Bad Request",
|
||||
@@ -33,10 +33,13 @@ type
|
||||
Http500 = "500 Internal Server Error",
|
||||
Http502 = "502 Bad Gateway"
|
||||
|
||||
THttpVersion* = enum
|
||||
HttpVersion* = enum
|
||||
HttpVer11,
|
||||
HttpVer10
|
||||
|
||||
{.deprecated: [TRequest: Request, PAsyncHttpServer: AsyncHttpServer,
|
||||
THttpCode: HttpCode, THttpVersion: HttpVersion].}
|
||||
|
||||
proc `==`*(protocol: tuple[orig: string, major, minor: int],
|
||||
ver: THttpVersion): bool =
|
||||
let major =
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf, Dominik Picheta
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
@@ -10,8 +10,8 @@ include "system/inclrtl"
|
||||
|
||||
import sockets, os
|
||||
|
||||
## This module implements an asynchronous event loop together with asynchronous sockets
|
||||
## which use this event loop.
|
||||
## This module implements an asynchronous event loop together with asynchronous
|
||||
## sockets which use this event loop.
|
||||
## It is akin to Python's asyncore module. Many modules that use sockets
|
||||
## have an implementation for this module, those modules should all have a
|
||||
## ``register`` function which you should use to add the desired objects to a
|
||||
@@ -31,10 +31,10 @@ import sockets, os
|
||||
##
|
||||
## Most (if not all) modules that use asyncio provide a userArg which is passed
|
||||
## on with the events. The type that you set userArg to must be inheriting from
|
||||
## TObject!
|
||||
## ``RootObj``!
|
||||
##
|
||||
## **Note:** If you want to provide async ability to your module please do not
|
||||
## use the ``TDelegate`` object, instead use ``PAsyncSocket``. It is possible
|
||||
## use the ``Delegate`` object, instead use ``AsyncSocket``. It is possible
|
||||
## that in the future this type's fields will not be exported therefore breaking
|
||||
## your code.
|
||||
##
|
||||
@@ -44,10 +44,10 @@ import sockets, os
|
||||
## Asynchronous sockets
|
||||
## ====================
|
||||
##
|
||||
## For most purposes you do not need to worry about the ``TDelegate`` type. The
|
||||
## ``PAsyncSocket`` is what you are after. It's a reference to the ``TAsyncSocket``
|
||||
## object. This object defines events which you should overwrite by your own
|
||||
## procedures.
|
||||
## For most purposes you do not need to worry about the ``Delegate`` type. The
|
||||
## ``AsyncSocket`` is what you are after. It's a reference to
|
||||
## the ``AsyncSocketObj`` object. This object defines events which you should
|
||||
## overwrite by your own procedures.
|
||||
##
|
||||
## For server sockets the only event you need to worry about is the ``handleAccept``
|
||||
## event, in your handleAccept proc you should call ``accept`` on the server
|
||||
@@ -57,13 +57,13 @@ import sockets, os
|
||||
##
|
||||
## An example ``handleAccept`` follows:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## var disp: PDispatcher = newDispatcher()
|
||||
## var disp = newDispatcher()
|
||||
## ...
|
||||
## proc handleAccept(s: PAsyncSocket) =
|
||||
## proc handleAccept(s: AsyncSocket) =
|
||||
## echo("Accepted client.")
|
||||
## var client: PAsyncSocket
|
||||
## var client: AsyncSocket
|
||||
## new(client)
|
||||
## s.accept(client)
|
||||
## client.handleRead = ...
|
||||
@@ -76,29 +76,29 @@ import sockets, os
|
||||
## the socket has established a connection to a server socket; from that point
|
||||
## it can be safely written to.
|
||||
##
|
||||
## Getting a blocking client from a PAsyncSocket
|
||||
## Getting a blocking client from an AsyncSocket
|
||||
## =============================================
|
||||
##
|
||||
## If you need a asynchronous server socket but you wish to process the clients
|
||||
## synchronously then you can use the ``getSocket`` converter to get a TSocket
|
||||
## object from the PAsyncSocket object, this can then be combined with ``accept``
|
||||
## like so:
|
||||
## synchronously then you can use the ``getSocket`` converter to get
|
||||
## a ``Socket`` from the ``AsyncSocket`` object, this can then be combined
|
||||
## with ``accept`` like so:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## proc handleAccept(s: PAsyncSocket) =
|
||||
## var client: TSocket
|
||||
## proc handleAccept(s: AsyncSocket) =
|
||||
## var client: Socket
|
||||
## getSocket(s).accept(client)
|
||||
|
||||
when defined(windows):
|
||||
from winlean import TTimeVal, TSocketHandle, TFdSet, FD_ZERO, FD_SET, FD_ISSET, select
|
||||
from winlean import TimeVal, SocketHandle, FdSet, FD_ZERO, FD_SET, FD_ISSET, select
|
||||
else:
|
||||
from posix import TTimeVal, TSocketHandle, TFdSet, FD_ZERO, FD_SET, FD_ISSET, select
|
||||
from posix import TimeVal, SocketHandle, FdSet, FD_ZERO, FD_SET, FD_ISSET, select
|
||||
|
||||
type
|
||||
TDelegate* = object
|
||||
fd*: TSocketHandle
|
||||
deleVal*: PObject
|
||||
DelegateObj* = object
|
||||
fd*: SocketHandle
|
||||
deleVal*: RootRef
|
||||
|
||||
handleRead*: proc (h: PObject) {.nimcall, gcsafe.}
|
||||
handleWrite*: proc (h: PObject) {.nimcall, gcsafe.}
|
||||
@@ -107,18 +107,18 @@ type
|
||||
|
||||
open*: bool
|
||||
task*: proc (h: PObject) {.nimcall, gcsafe.}
|
||||
mode*: TFileMode
|
||||
mode*: FileMode
|
||||
|
||||
PDelegate* = ref TDelegate
|
||||
Delegate* = ref DelegateObj
|
||||
|
||||
PDispatcher* = ref TDispatcher
|
||||
TDispatcher = object
|
||||
Dispatcher* = ref DispatcherObj
|
||||
DispatcherObj = object
|
||||
delegates: seq[PDelegate]
|
||||
|
||||
PAsyncSocket* = ref TAsyncSocket
|
||||
TAsyncSocket* = object of TObject
|
||||
socket: TSocket
|
||||
info: TInfo
|
||||
AsyncSocket* = ref AsyncSocketObj
|
||||
AsyncSocketObj* = object of RootObj
|
||||
socket: Socket
|
||||
info: SocketStatus
|
||||
|
||||
handleRead*: proc (s: PAsyncSocket) {.closure, gcsafe.}
|
||||
handleWrite: proc (s: PAsyncSocket) {.closure, gcsafe.}
|
||||
@@ -134,10 +134,17 @@ type
|
||||
proto: TProtocol
|
||||
deleg: PDelegate
|
||||
|
||||
TInfo* = enum
|
||||
SocketStatus* = enum
|
||||
SockIdle, SockConnecting, SockConnected, SockListening, SockClosed,
|
||||
SockUDPBound
|
||||
|
||||
{.deprecated: [TDelegate: DelegateObj, PDelegate: Delegate,
|
||||
TProcessOption: ProcessOption,
|
||||
TInfo: SocketStatus, PAsyncSocket: AsyncSocket, TAsyncSocket: AsyncSocketObj,
|
||||
TDispatcher: DispatcherObj, PDispatcher: Dispatcher,
|
||||
].}
|
||||
|
||||
|
||||
proc newDelegate*(): PDelegate =
|
||||
## Creates a new delegate.
|
||||
new(result)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2014 Dominik Picheta
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -19,9 +19,9 @@
|
||||
##
|
||||
## import asyncnet, asyncdispatch
|
||||
##
|
||||
## var clients: seq[PAsyncSocket] = @[]
|
||||
## var clients: seq[AsyncSocket] = @[]
|
||||
##
|
||||
## proc processClient(client: PAsyncSocket) {.async.} =
|
||||
## proc processClient(client: AsyncSocket) {.async.} =
|
||||
## while true:
|
||||
## let line = await client.recvLine()
|
||||
## for c in clients:
|
||||
@@ -29,7 +29,7 @@
|
||||
##
|
||||
## proc serve() {.async.} =
|
||||
## var server = newAsyncSocket()
|
||||
## server.bindAddr(TPort(12345))
|
||||
## server.bindAddr(Port(12345))
|
||||
## server.listen()
|
||||
##
|
||||
## while true:
|
||||
@@ -54,8 +54,10 @@ when defined(ssl):
|
||||
type
|
||||
# TODO: I would prefer to just do:
|
||||
# PAsyncSocket* {.borrow: `.`.} = distinct PSocket. But that doesn't work.
|
||||
TAsyncSocket {.borrow: `.`.} = distinct TSocketImpl
|
||||
PAsyncSocket* = ref TAsyncSocket
|
||||
AsyncSocketDesc {.borrow: `.`.} = distinct TSocketImpl
|
||||
AsyncSocket* = ref AsyncSocketDesc
|
||||
|
||||
{.deprecated: [PAsyncSocket: AsyncSocket].}
|
||||
|
||||
# TODO: Save AF, domain etc info and reuse it in procs which need it like connect.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2010 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2013 Robert Persson
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2013 Robert Persson
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -44,5 +44,5 @@ proc openDefaultBrowser*(url: string) =
|
||||
# we use ``startProcess`` here because we don't want to block!
|
||||
discard startProcess(command=b, args=[url], options={poUseShell})
|
||||
return
|
||||
except EOS:
|
||||
except OSError:
|
||||
discard
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
## This module implements helper procs for CGI applications. Example:
|
||||
##
|
||||
## .. code-block:: Nimrod
|
||||
## .. code-block:: Nim
|
||||
##
|
||||
## import strtabs, cgi
|
||||
##
|
||||
@@ -93,12 +93,14 @@ proc XMLencode*(s: string): string =
|
||||
for i in 0..len(s)-1: addXmlChar(result, s[i])
|
||||
|
||||
type
|
||||
ECgi* = object of IOError ## the exception that is raised, if a CGI error occurs
|
||||
TRequestMethod* = enum ## the used request method
|
||||
CgiError* = object of IOError ## exception that is raised if a CGI error occurs
|
||||
RequestMethod* = enum ## the used request method
|
||||
methodNone, ## no REQUEST_METHOD environment variable
|
||||
methodPost, ## query uses the POST method
|
||||
methodGet ## query uses the GET method
|
||||
|
||||
{.deprecated: [TRequestMethod: RequestMethod, ECgi: CgiError].}
|
||||
|
||||
proc cgiError*(msg: string) {.noreturn.} =
|
||||
## raises an ECgi exception with message `msg`.
|
||||
var e: ref ECgi
|
||||
@@ -377,7 +379,7 @@ proc setCookie*(name, value: string) =
|
||||
write(stdout, "Set-Cookie: ", name, "=", value, "\n")
|
||||
|
||||
var
|
||||
gcookies {.threadvar.}: PStringTable
|
||||
gcookies {.threadvar.}: StringTableRef
|
||||
|
||||
proc getCookie*(name: string): TaintedString =
|
||||
## Gets a cookie. If no cookie of `name` exists, "" is returned.
|
||||
|
||||
@@ -1,8 +1,40 @@
|
||||
#nimrod c -t:-march=i686 --cpu:amd64 --threads:on -d:release lockfreehash.nim
|
||||
|
||||
import baseutils, unsigned, math, hashes
|
||||
import unsigned, math, hashes
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
## Memory Utility Functions
|
||||
|
||||
proc newHeap*[T](): ptr T =
|
||||
result = cast[ptr T](alloc0(sizeof(T)))
|
||||
|
||||
proc copyNew*[T](x: var T): ptr T =
|
||||
var
|
||||
size = sizeof(T)
|
||||
mem = alloc(size)
|
||||
copyMem(mem, x.addr, size)
|
||||
return cast[ptr T](mem)
|
||||
|
||||
proc copyTo*[T](val: var T, dest: int) =
|
||||
copyMem(pointer(dest), val.addr, sizeof(T))
|
||||
|
||||
proc allocType*[T](): pointer = alloc(sizeof(T))
|
||||
|
||||
proc newShared*[T](): ptr T =
|
||||
result = cast[ptr T](allocShared0(sizeof(T)))
|
||||
|
||||
proc copyShared*[T](x: var T): ptr T =
|
||||
var
|
||||
size = sizeof(T)
|
||||
mem = allocShared(size)
|
||||
copyMem(mem, x.addr, size)
|
||||
return cast[ptr T](mem)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
## Pointer arithmetic
|
||||
|
||||
proc `+`*(p: pointer, i: int): pointer {.inline.} =
|
||||
cast[pointer](cast[int](p) + i)
|
||||
|
||||
const
|
||||
minTableSize = 8
|
||||
@@ -194,7 +226,7 @@ proc copySlot[K,V](idx: int, oldTbl: var PConcTable[K,V], newTbl: var PConcTable
|
||||
#Prevent new values from appearing in the old table by priming
|
||||
oldVal = atomic_load_n(oldTbl[idx].value.addr, ATOMIC_RELAXED)
|
||||
while not isPrime(oldVal):
|
||||
var box = if oldVal == NULL or isTomb(oldVal) : oldVal.setTomb.setPrime
|
||||
var box = if oldVal == 0 or isTomb(oldVal) : oldVal.setTomb.setPrime
|
||||
else: oldVal.setPrime
|
||||
if atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr,
|
||||
box, false, ATOMIC_RELAXED, ATOMIC_RELAXED):
|
||||
@@ -209,8 +241,8 @@ proc copySlot[K,V](idx: int, oldTbl: var PConcTable[K,V], newTbl: var PConcTable
|
||||
return false
|
||||
if isTomb(oldVal):
|
||||
echo("oldVal is Tomb!!!, should not happen")
|
||||
if pop(oldVal) != NULL:
|
||||
result = setVal(newTbl, pop(oldKey), pop(oldVal), NULL, true) == NULL
|
||||
if pop(oldVal) != 0:
|
||||
result = setVal(newTbl, pop(oldKey), pop(oldVal), 0, true) == 0
|
||||
if result:
|
||||
#echo("Copied a Slot! idx= " & $idx & " key= " & $oldKey & " val= " & $oldVal)
|
||||
else:
|
||||
@@ -323,7 +355,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||
idx = idx and (table.len - 1)
|
||||
#echo("try set idx = " & $idx & "for" & $key)
|
||||
var
|
||||
probedKey = NULL
|
||||
probedKey = 0
|
||||
openKey = atomic_compare_exchange_n(table[idx].key.addr, probedKey.addr,
|
||||
key, false, ATOMIC_RELAXED, ATOMIC_RELAXED)
|
||||
if openKey:
|
||||
@@ -339,7 +371,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||
if keyEQ[K](probedKey, key):
|
||||
#echo("we found the matching slot")
|
||||
break # We found a matching slot
|
||||
if (not(expVal != NULL and match)) and (probes >= reProbeLimit or key.isTomb):
|
||||
if (not(expVal != 0 and match)) and (probes >= reProbeLimit or key.isTomb):
|
||||
if key.isTomb: echo("Key is Tombstone")
|
||||
#if probes >= reProbeLimit: echo("Too much probing " & $probes)
|
||||
#echo("try to resize")
|
||||
@@ -361,7 +393,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||
return oldVal
|
||||
nextTable = atomic_load_n(table.next.addr, ATOMIC_SEQ_CST)
|
||||
if nextTable == nil and
|
||||
((oldVal == NULL and
|
||||
((oldVal == 0 and
|
||||
(probes >= reProbeLimit or table.used / table.len > 0.8)) or
|
||||
(isPrime(oldVal))):
|
||||
if table.used / table.len > 0.8: echo("resize because usage ratio = " &
|
||||
@@ -380,12 +412,12 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||
if atomic_compare_exchange_n(table[idx].value.addr, oldVal.addr,
|
||||
val, false, ATOMIC_RELEASE, ATOMIC_RELAXED):
|
||||
#echo("val set at table " & $cast[int](table))
|
||||
if expVal != NULL:
|
||||
if (oldVal == NULL or isTomb(oldVal)) and not isTomb(val):
|
||||
if expVal != 0:
|
||||
if (oldVal == 0 or isTomb(oldVal)) and not isTomb(val):
|
||||
discard atomic_add_fetch(table.active.addr, 1, ATOMIC_RELAXED)
|
||||
elif not (oldVal == NULL or isTomb(oldVal)) and isTomb(val):
|
||||
elif not (oldVal == 0 or isTomb(oldVal)) and isTomb(val):
|
||||
discard atomic_add_fetch(table.active.addr, -1, ATOMIC_RELAXED)
|
||||
if oldVal == NULL and expVal != NULL:
|
||||
if oldVal == 0 and expVal != 0:
|
||||
return setTomb(oldVal)
|
||||
else: return oldVal
|
||||
if isPrime(oldVal):
|
||||
@@ -415,7 +447,7 @@ proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
|
||||
if not isPrime(val):
|
||||
if isTomb(val):
|
||||
#echo("val was tomb but not prime")
|
||||
return NULL
|
||||
return 0
|
||||
else:
|
||||
#echo("-GotIt- idx = ", idx, " key = ", key, " val ", val )
|
||||
return val
|
||||
@@ -427,7 +459,7 @@ proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
|
||||
if probes >= reProbeLimit*4 or key.isTomb:
|
||||
if newTable == nil:
|
||||
#echo("too many probes and no new table ", key, " ", idx )
|
||||
return NULL
|
||||
return 0
|
||||
else:
|
||||
newTable = helpCopy(table)
|
||||
return getVal(newTable, key)
|
||||
@@ -437,10 +469,10 @@ proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#proc set*(table: var PConcTable[TRaw,TRaw], key: TRaw, val: TRaw) =
|
||||
# discard setVal(table, pack(key), pack(key), NULL, false)
|
||||
# discard setVal(table, pack(key), pack(key), 0, false)
|
||||
|
||||
#proc set*[V](table: var PConcTable[TRaw,V], key: TRaw, val: ptr V) =
|
||||
# discard setVal(table, pack(key), cast[int](val), NULL, false)
|
||||
# discard setVal(table, pack(key), cast[int](val), 0, false)
|
||||
|
||||
proc set*[K,V](table: var PConcTable[K,V], key: var K, val: var V) =
|
||||
when not (K is TRaw):
|
||||
@@ -451,10 +483,10 @@ proc set*[K,V](table: var PConcTable[K,V], key: var K, val: var V) =
|
||||
var newVal = cast[int](copyShared(val))
|
||||
else:
|
||||
var newVal = pack(val)
|
||||
var oldPtr = pop(setVal(table, newKey, newVal, NULL, false))
|
||||
var oldPtr = pop(setVal(table, newKey, newVal, 0, false))
|
||||
#echo("oldPtr = ", cast[int](oldPtr), " newPtr = ", cast[int](newPtr))
|
||||
when not (V is TRaw):
|
||||
if newVal != oldPtr and oldPtr != NULL:
|
||||
if newVal != oldPtr and oldPtr != 0:
|
||||
deallocShared(cast[ptr V](oldPtr))
|
||||
|
||||
|
||||
@@ -573,10 +605,3 @@ when isMainModule:
|
||||
# echo(i, " = ", hashInt(i) and 8191)
|
||||
|
||||
deleteConcTable(table)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -12,30 +12,32 @@
|
||||
## by Adam Langley.
|
||||
|
||||
type
|
||||
TNode[T] = object {.pure, final, acyclic.}
|
||||
NodeObj[T] = object {.pure, final, acyclic.}
|
||||
byte: int ## byte index of the difference
|
||||
otherbits: char
|
||||
case isLeaf: bool
|
||||
of false: child: array[0..1, ref TNode[T]]
|
||||
of false: child: array[0..1, ref NodeObj[T]]
|
||||
of true:
|
||||
key: string
|
||||
when T isnot void:
|
||||
val: T
|
||||
|
||||
PNode[T] = ref TNode[T]
|
||||
TCritBitTree*[T] = object {.
|
||||
Node[T] = ref NodeObj[T]
|
||||
CritBitTree*[T] = object {.
|
||||
pure, final.} ## The crit bit tree can either be used
|
||||
## as a mapping from strings to
|
||||
## some type ``T`` or as a set of
|
||||
## strings if ``T`` is void.
|
||||
root: PNode[T]
|
||||
root: Node[T]
|
||||
count: int
|
||||
|
||||
proc len*[T](c: TCritBitTree[T]): int =
|
||||
|
||||
{.deprecated: [TCritBitTree: CritBitTree].}
|
||||
|
||||
proc len*[T](c: CritBitTree[T]): int =
|
||||
## returns the number of elements in `c` in O(1).
|
||||
result = c.count
|
||||
|
||||
proc rawGet[T](c: TCritBitTree[T], key: string): PNode[T] =
|
||||
proc rawGet[T](c: CritBitTree[T], key: string): Node[T] =
|
||||
var it = c.root
|
||||
while it != nil:
|
||||
if not it.isLeaf:
|
||||
@@ -45,15 +47,15 @@ proc rawGet[T](c: TCritBitTree[T], key: string): PNode[T] =
|
||||
else:
|
||||
return if it.key == key: it else: nil
|
||||
|
||||
proc contains*[T](c: TCritBitTree[T], key: string): bool {.inline.} =
|
||||
proc contains*[T](c: CritBitTree[T], key: string): bool {.inline.} =
|
||||
## returns true iff `c` contains the given `key`.
|
||||
result = rawGet(c, key) != nil
|
||||
|
||||
proc hasKey*[T](c: TCritBitTree[T], key: string): bool {.inline.} =
|
||||
proc hasKey*[T](c: CritBitTree[T], key: string): bool {.inline.} =
|
||||
## alias for `contains`.
|
||||
result = rawGet(c, key) != nil
|
||||
|
||||
proc rawInsert[T](c: var TCritBitTree[T], key: string): PNode[T] =
|
||||
proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] =
|
||||
if c.root == nil:
|
||||
new c.root
|
||||
c.root.isleaf = true
|
||||
@@ -84,7 +86,7 @@ proc rawInsert[T](c: var TCritBitTree[T], key: string): PNode[T] =
|
||||
let ch = it.key[newByte]
|
||||
let dir = (1 + (ord(ch) or newOtherBits)) shr 8
|
||||
|
||||
var inner: PNode[T]
|
||||
var inner: Node[T]
|
||||
new inner
|
||||
new result
|
||||
result.isLeaf = true
|
||||
@@ -106,7 +108,7 @@ proc rawInsert[T](c: var TCritBitTree[T], key: string): PNode[T] =
|
||||
wherep[] = inner
|
||||
inc c.count
|
||||
|
||||
proc containsOrIncl*[T](c: var TCritBitTree[T], key: string, val: T): bool =
|
||||
proc containsOrIncl*[T](c: var CritBitTree[T], key: string, val: T): bool =
|
||||
## returns true iff `c` contains the given `key`. If the key does not exist
|
||||
## ``c[key] = val`` is performed.
|
||||
let oldCount = c.count
|
||||
@@ -115,23 +117,23 @@ proc containsOrIncl*[T](c: var TCritBitTree[T], key: string, val: T): bool =
|
||||
when T isnot void:
|
||||
if not result: n.val = val
|
||||
|
||||
proc containsOrIncl*(c: var TCritBitTree[void], key: string): bool =
|
||||
proc containsOrIncl*(c: var CritBitTree[void], key: string): bool =
|
||||
## returns true iff `c` contains the given `key`. If the key does not exist
|
||||
## it is inserted into `c`.
|
||||
let oldCount = c.count
|
||||
var n = rawInsert(c, key)
|
||||
result = c.count == oldCount
|
||||
|
||||
proc incl*(c: var TCritBitTree[void], key: string) =
|
||||
proc incl*(c: var CritBitTree[void], key: string) =
|
||||
## includes `key` in `c`.
|
||||
discard rawInsert(c, key)
|
||||
|
||||
proc `[]=`*[T](c: var TCritBitTree[T], key: string, val: T) =
|
||||
proc `[]=`*[T](c: var CritBitTree[T], key: string, val: T) =
|
||||
## puts a (key, value)-pair into `t`.
|
||||
var n = rawInsert(c, key)
|
||||
n.val = val
|
||||
|
||||
proc `[]`*[T](c: TCritBitTree[T], key: string): T {.inline.} =
|
||||
proc `[]`*[T](c: CritBitTree[T], key: string): T {.inline.} =
|
||||
## retrieves the value at ``c[key]``. If `key` is not in `t`,
|
||||
## default empty value for the type `B` is returned
|
||||
## and no exception is raised. One can check with ``hasKey`` whether the key
|
||||
@@ -139,22 +141,22 @@ proc `[]`*[T](c: TCritBitTree[T], key: string): T {.inline.} =
|
||||
let n = rawGet(c, key)
|
||||
if n != nil: result = n.val
|
||||
|
||||
proc mget*[T](c: var TCritBitTree[T], key: string): var T {.inline.} =
|
||||
proc mget*[T](c: var CritBitTree[T], key: string): var T {.inline.} =
|
||||
## retrieves the value at ``c[key]``. The value can be modified.
|
||||
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
|
||||
## If `key` is not in `t`, the ``KeyError`` exception is raised.
|
||||
let n = rawGet(c, key)
|
||||
if n != nil: result = n.val
|
||||
else: raise newException(EInvalidKey, "key not found: " & $key)
|
||||
else: raise newException(KeyError, "key not found: " & $key)
|
||||
|
||||
proc excl*[T](c: var TCritBitTree[T], key: string) =
|
||||
proc excl*[T](c: var CritBitTree[T], key: string) =
|
||||
## removes `key` (and its associated value) from the set `c`.
|
||||
## If the `key` does not exist, nothing happens.
|
||||
var p = c.root
|
||||
var wherep = addr(c.root)
|
||||
var whereq: ptr PNode = nil
|
||||
var whereq: ptr Node[T] = nil
|
||||
if p == nil: return
|
||||
var dir = 0
|
||||
var q: PNode
|
||||
var q: Node[T]
|
||||
while not p.isLeaf:
|
||||
whereq = wherep
|
||||
q = p
|
||||
@@ -170,7 +172,7 @@ proc excl*[T](c: var TCritBitTree[T], key: string) =
|
||||
whereq[] = q.child[1 - dir]
|
||||
dec c.count
|
||||
|
||||
iterator leaves[T](n: PNode[T]): PNode[T] =
|
||||
iterator leaves[T](n: Node[T]): Node[T] =
|
||||
if n != nil:
|
||||
# XXX actually we could compute the necessary stack size in advance:
|
||||
# it's rougly log2(c.count).
|
||||
@@ -183,33 +185,33 @@ iterator leaves[T](n: PNode[T]): PNode[T] =
|
||||
assert(it != nil)
|
||||
yield it
|
||||
|
||||
iterator keys*[T](c: TCritBitTree[T]): string =
|
||||
iterator keys*[T](c: CritBitTree[T]): string =
|
||||
## yields all keys in lexicographical order.
|
||||
for x in leaves(c.root): yield x.key
|
||||
|
||||
iterator values*[T](c: TCritBitTree[T]): T =
|
||||
iterator values*[T](c: CritBitTree[T]): T =
|
||||
## yields all values of `c` in the lexicographical order of the
|
||||
## corresponding keys.
|
||||
for x in leaves(c.root): yield x.val
|
||||
|
||||
iterator mvalues*[T](c: var TCritBitTree[T]): var T =
|
||||
iterator mvalues*[T](c: var CritBitTree[T]): var T =
|
||||
## yields all values of `c` in the lexicographical order of the
|
||||
## corresponding keys. The values can be modified.
|
||||
for x in leaves(c.root): yield x.val
|
||||
|
||||
iterator items*[T](c: TCritBitTree[T]): string =
|
||||
iterator items*[T](c: CritBitTree[T]): string =
|
||||
## yields all keys in lexicographical order.
|
||||
for x in leaves(c.root): yield x.key
|
||||
|
||||
iterator pairs*[T](c: TCritBitTree[T]): tuple[key: string, val: T] =
|
||||
iterator pairs*[T](c: CritBitTree[T]): tuple[key: string, val: T] =
|
||||
## yields all (key, value)-pairs of `c`.
|
||||
for x in leaves(c.root): yield (x.key, x.val)
|
||||
|
||||
iterator mpairs*[T](c: var TCritBitTree[T]): tuple[key: string, val: var T] =
|
||||
iterator mpairs*[T](c: var CritBitTree[T]): tuple[key: string, val: var T] =
|
||||
## yields all (key, value)-pairs of `c`. The yielded values can be modified.
|
||||
for x in leaves(c.root): yield (x.key, x.val)
|
||||
|
||||
proc allprefixedAux[T](c: TCritBitTree[T], key: string): PNode[T] =
|
||||
proc allprefixedAux[T](c: CritBitTree[T], key: string): Node[T] =
|
||||
var p = c.root
|
||||
var top = p
|
||||
if p != nil:
|
||||
@@ -223,42 +225,42 @@ proc allprefixedAux[T](c: TCritBitTree[T], key: string): PNode[T] =
|
||||
if p.key[i] != key[i]: return
|
||||
result = top
|
||||
|
||||
iterator itemsWithPrefix*[T](c: TCritBitTree[T], prefix: string): string =
|
||||
iterator itemsWithPrefix*[T](c: CritBitTree[T], prefix: string): string =
|
||||
## yields all keys starting with `prefix`.
|
||||
let top = allprefixedAux(c, prefix)
|
||||
for x in leaves(top): yield x.key
|
||||
|
||||
iterator keysWithPrefix*[T](c: TCritBitTree[T], prefix: string): string =
|
||||
iterator keysWithPrefix*[T](c: CritBitTree[T], prefix: string): string =
|
||||
## yields all keys starting with `prefix`.
|
||||
let top = allprefixedAux(c, prefix)
|
||||
for x in leaves(top): yield x.key
|
||||
|
||||
iterator valuesWithPrefix*[T](c: TCritBitTree[T], prefix: string): T =
|
||||
iterator valuesWithPrefix*[T](c: CritBitTree[T], prefix: string): T =
|
||||
## yields all values of `c` starting with `prefix` of the
|
||||
## corresponding keys.
|
||||
let top = allprefixedAux(c, prefix)
|
||||
for x in leaves(top): yield x.val
|
||||
|
||||
iterator mvaluesWithPrefix*[T](c: var TCritBitTree[T], prefix: string): var T =
|
||||
iterator mvaluesWithPrefix*[T](c: var CritBitTree[T], prefix: string): var T =
|
||||
## yields all values of `c` starting with `prefix` of the
|
||||
## corresponding keys. The values can be modified.
|
||||
let top = allprefixedAux(c, prefix)
|
||||
for x in leaves(top): yield x.val
|
||||
|
||||
iterator pairsWithPrefix*[T](c: TCritBitTree[T],
|
||||
iterator pairsWithPrefix*[T](c: CritBitTree[T],
|
||||
prefix: string): tuple[key: string, val: T] =
|
||||
## yields all (key, value)-pairs of `c` starting with `prefix`.
|
||||
let top = allprefixedAux(c, prefix)
|
||||
for x in leaves(top): yield (x.key, x.val)
|
||||
|
||||
iterator mpairsWithPrefix*[T](c: var TCritBitTree[T],
|
||||
iterator mpairsWithPrefix*[T](c: var CritBitTree[T],
|
||||
prefix: string): tuple[key: string, val: var T] =
|
||||
## yields all (key, value)-pairs of `c` starting with `prefix`.
|
||||
## The yielded values can be modified.
|
||||
let top = allprefixedAux(c, prefix)
|
||||
for x in leaves(top): yield (x.key, x.val)
|
||||
|
||||
proc `$`*[T](c: TCritBitTree[T]): string =
|
||||
proc `$`*[T](c: CritBitTree[T]): string =
|
||||
## turns `c` into a string representation. Example outputs:
|
||||
## ``{keyA: value, keyB: value}``, ``{:}``
|
||||
## If `T` is void the outputs look like:
|
||||
@@ -285,7 +287,7 @@ proc `$`*[T](c: TCritBitTree[T]): string =
|
||||
result.add("}")
|
||||
|
||||
when isMainModule:
|
||||
var r: TCritBitTree[void]
|
||||
var r: CritBitTree[void]
|
||||
r.incl "abc"
|
||||
r.incl "xyz"
|
||||
r.incl "def"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
## The ``intsets`` module implements an efficient int set implemented as a
|
||||
## sparse bit set.
|
||||
## **Note**: Since Nimrod currently does not allow the assignment operator to
|
||||
## **Note**: Since Nim currently does not allow the assignment operator to
|
||||
## be overloaded, ``=`` for int sets performs some rather meaningless shallow
|
||||
## copy; use ``assign`` to get a deep copy.
|
||||
|
||||
@@ -17,7 +17,7 @@ import
|
||||
os, hashes, math
|
||||
|
||||
type
|
||||
TBitScalar = int
|
||||
BitScalar = int
|
||||
|
||||
const
|
||||
InitIntSetSize = 8 # must be a power of two!
|
||||
@@ -25,8 +25,8 @@ const
|
||||
BitsPerTrunk = 1 shl TrunkShift # needs to be a power of 2 and
|
||||
# divisible by 64
|
||||
TrunkMask = BitsPerTrunk - 1
|
||||
IntsPerTrunk = BitsPerTrunk div (sizeof(TBitScalar) * 8)
|
||||
IntShift = 5 + ord(sizeof(TBitScalar) == 8) # 5 or 6, depending on int width
|
||||
IntsPerTrunk = BitsPerTrunk div (sizeof(BitScalar) * 8)
|
||||
IntShift = 5 + ord(sizeof(BitScalar) == 8) # 5 or 6, depending on int width
|
||||
IntMask = 1 shl IntShift - 1
|
||||
|
||||
type
|
||||
@@ -34,15 +34,16 @@ type
|
||||
TTrunk {.final.} = object
|
||||
next: PTrunk # all nodes are connected with this pointer
|
||||
key: int # start address at bit 0
|
||||
bits: array[0..IntsPerTrunk - 1, TBitScalar] # a bit vector
|
||||
bits: array[0..IntsPerTrunk - 1, BitScalar] # a bit vector
|
||||
|
||||
TTrunkSeq = seq[PTrunk]
|
||||
TIntSet* {.final.} = object ## an efficient set of 'int' implemented as a
|
||||
## sparse bit set
|
||||
IntSet* = object ## an efficient set of 'int' implemented as a sparse bit set
|
||||
counter, max: int
|
||||
head: PTrunk
|
||||
data: TTrunkSeq
|
||||
|
||||
{.deprecated: [TIntSet: IntSet].}
|
||||
|
||||
proc mustRehash(length, counter: int): bool {.inline.} =
|
||||
assert(length > counter)
|
||||
result = (length * 2 < counter * 3) or (length - counter < 4)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -15,52 +15,58 @@ when not defined(nimhygiene):
|
||||
{.pragma: dirty.}
|
||||
|
||||
type
|
||||
TDoublyLinkedNode* {.pure,
|
||||
final.}[T] = object ## a node a doubly linked list consists of
|
||||
next*, prev*: ref TDoublyLinkedNode[T]
|
||||
DoublyLinkedNodeObj*[T] = object ## a node a doubly linked list consists of
|
||||
next*, prev*: ref DoublyLinkedNodeObj[T]
|
||||
value*: T
|
||||
PDoublyLinkedNode*[T] = ref TDoublyLinkedNode[T]
|
||||
DoublyLinkedNode*[T] = ref DoublyLinkedNodeObj[T]
|
||||
|
||||
TSinglyLinkedNode* {.pure,
|
||||
final.}[T] = object ## a node a singly linked list consists of
|
||||
next*: ref TSinglyLinkedNode[T]
|
||||
SinglyLinkedNodeObj*[T] = object ## a node a singly linked list consists of
|
||||
next*: ref SinglyLinkedNodeObj[T]
|
||||
value*: T
|
||||
PSinglyLinkedNode*[T] = ref TSinglyLinkedNode[T]
|
||||
SinglyLinkedNode*[T] = ref SinglyLinkedNodeObj[T]
|
||||
|
||||
TSinglyLinkedList* {.pure, final.}[T] = object ## a singly linked list
|
||||
head*, tail*: PSinglyLinkedNode[T]
|
||||
SinglyLinkedList*[T] = object ## a singly linked list
|
||||
head*, tail*: SinglyLinkedNode[T]
|
||||
|
||||
TDoublyLinkedList* {.pure, final.}[T] = object ## a doubly linked list
|
||||
head*, tail*: PDoublyLinkedNode[T]
|
||||
DoublyLinkedList*[T] = object ## a doubly linked list
|
||||
head*, tail*: DoublyLinkedNode[T]
|
||||
|
||||
TSinglyLinkedRing* {.pure, final.}[T] = object ## a singly linked ring
|
||||
head*: PSinglyLinkedNode[T]
|
||||
SinglyLinkedRing*[T] = object ## a singly linked ring
|
||||
head*: SinglyLinkedNode[T]
|
||||
|
||||
TDoublyLinkedRing* {.pure, final.}[T] = object ## a doubly linked ring
|
||||
head*: PDoublyLinkedNode[T]
|
||||
DoublyLinkedRing*[T] = object ## a doubly linked ring
|
||||
head*: DoublyLinkedNode[T]
|
||||
|
||||
proc initSinglyLinkedList*[T](): TSinglyLinkedList[T] =
|
||||
{.deprecated: [TDoublyLinkedNode: DoublyLinkedNodeObj,
|
||||
PDoublyLinkedNode: DoublyLinkedNode,
|
||||
TSinglyLinkedNode: SinglyLinkedNodeObj,
|
||||
PSinglyLinkedNode: SinglyLinkedNode,
|
||||
TDoublyLinkedList: DoublyLinkedList,
|
||||
TSinglyLinkedRing: SinglyLinkedRing,
|
||||
TDoublyLinkedRing: DoublyLinkedRing].}
|
||||
|
||||
proc initSinglyLinkedList*[T](): SinglyLinkedList[T] =
|
||||
## creates a new singly linked list that is empty.
|
||||
discard
|
||||
|
||||
proc initDoublyLinkedList*[T](): TDoublyLinkedList[T] =
|
||||
proc initDoublyLinkedList*[T](): DoublyLinkedList[T] =
|
||||
## creates a new doubly linked list that is empty.
|
||||
discard
|
||||
|
||||
proc initSinglyLinkedRing*[T](): TSinglyLinkedRing[T] =
|
||||
proc initSinglyLinkedRing*[T](): SinglyLinkedRing[T] =
|
||||
## creates a new singly linked ring that is empty.
|
||||
discard
|
||||
|
||||
proc initDoublyLinkedRing*[T](): TDoublyLinkedRing[T] =
|
||||
proc initDoublyLinkedRing*[T](): DoublyLinkedRing[T] =
|
||||
## creates a new doubly linked ring that is empty.
|
||||
discard
|
||||
|
||||
proc newDoublyLinkedNode*[T](value: T): PDoublyLinkedNode[T] =
|
||||
proc newDoublyLinkedNode*[T](value: T): DoublyLinkedNode[T] =
|
||||
## creates a new doubly linked node with the given `value`.
|
||||
new(result)
|
||||
result.value = value
|
||||
|
||||
proc newSinglyLinkedNode*[T](value: T): PSinglyLinkedNode[T] =
|
||||
proc newSinglyLinkedNode*[T](value: T): SinglyLinkedNode[T] =
|
||||
## creates a new singly linked node with the given `value`.
|
||||
new(result)
|
||||
result.value = value
|
||||
@@ -99,38 +105,38 @@ template findImpl() {.dirty.} =
|
||||
for x in nodes(L):
|
||||
if x.value == value: return x
|
||||
|
||||
iterator items*[T](L: TDoublyLinkedList[T]): T =
|
||||
iterator items*[T](L: DoublyLinkedList[T]): T =
|
||||
## yields every value of `L`.
|
||||
itemsListImpl()
|
||||
|
||||
iterator items*[T](L: TSinglyLinkedList[T]): T =
|
||||
iterator items*[T](L: SinglyLinkedList[T]): T =
|
||||
## yields every value of `L`.
|
||||
itemsListImpl()
|
||||
|
||||
iterator items*[T](L: TSinglyLinkedRing[T]): T =
|
||||
iterator items*[T](L: SinglyLinkedRing[T]): T =
|
||||
## yields every value of `L`.
|
||||
itemsRingImpl()
|
||||
|
||||
iterator items*[T](L: TDoublyLinkedRing[T]): T =
|
||||
iterator items*[T](L: DoublyLinkedRing[T]): T =
|
||||
## yields every value of `L`.
|
||||
itemsRingImpl()
|
||||
|
||||
iterator nodes*[T](L: TSinglyLinkedList[T]): PSinglyLinkedNode[T] =
|
||||
iterator nodes*[T](L: SinglyLinkedList[T]): SinglyLinkedNode[T] =
|
||||
## iterates over every node of `x`. Removing the current node from the
|
||||
## list during traversal is supported.
|
||||
nodesListImpl()
|
||||
|
||||
iterator nodes*[T](L: TDoublyLinkedList[T]): PDoublyLinkedNode[T] =
|
||||
iterator nodes*[T](L: DoublyLinkedList[T]): DoublyLinkedNode[T] =
|
||||
## iterates over every node of `x`. Removing the current node from the
|
||||
## list during traversal is supported.
|
||||
nodesListImpl()
|
||||
|
||||
iterator nodes*[T](L: TSinglyLinkedRing[T]): PSinglyLinkedNode[T] =
|
||||
iterator nodes*[T](L: SinglyLinkedRing[T]): SinglyLinkedNode[T] =
|
||||
## iterates over every node of `x`. Removing the current node from the
|
||||
## list during traversal is supported.
|
||||
nodesRingImpl()
|
||||
|
||||
iterator nodes*[T](L: TDoublyLinkedRing[T]): PDoublyLinkedNode[T] =
|
||||
iterator nodes*[T](L: DoublyLinkedRing[T]): DoublyLinkedNode[T] =
|
||||
## iterates over every node of `x`. Removing the current node from the
|
||||
## list during traversal is supported.
|
||||
nodesRingImpl()
|
||||
@@ -142,33 +148,33 @@ template dollarImpl() {.dirty.} =
|
||||
result.add($x.value)
|
||||
result.add("]")
|
||||
|
||||
proc `$`*[T](L: TSinglyLinkedList[T]): string =
|
||||
proc `$`*[T](L: SinglyLinkedList[T]): string =
|
||||
## turns a list into its string representation.
|
||||
dollarImpl()
|
||||
|
||||
proc `$`*[T](L: TDoublyLinkedList[T]): string =
|
||||
proc `$`*[T](L: DoublyLinkedList[T]): string =
|
||||
## turns a list into its string representation.
|
||||
dollarImpl()
|
||||
|
||||
proc `$`*[T](L: TSinglyLinkedRing[T]): string =
|
||||
proc `$`*[T](L: SinglyLinkedRing[T]): string =
|
||||
## turns a list into its string representation.
|
||||
dollarImpl()
|
||||
|
||||
proc `$`*[T](L: TDoublyLinkedRing[T]): string =
|
||||
proc `$`*[T](L: DoublyLinkedRing[T]): string =
|
||||
## turns a list into its string representation.
|
||||
dollarImpl()
|
||||
|
||||
proc find*[T](L: TSinglyLinkedList[T], value: T): PSinglyLinkedNode[T] =
|
||||
proc find*[T](L: SinglyLinkedList[T], value: T): SinglyLinkedNode[T] =
|
||||
## searches in the list for a value. Returns nil if the value does not
|
||||
## exist.
|
||||
findImpl()
|
||||
|
||||
proc find*[T](L: TDoublyLinkedList[T], value: T): PDoublyLinkedNode[T] =
|
||||
proc find*[T](L: DoublyLinkedList[T], value: T): DoublyLinkedNode[T] =
|
||||
## searches in the list for a value. Returns nil if the value does not
|
||||
## exist.
|
||||
findImpl()
|
||||
|
||||
proc find*[T](L: TSinglyLinkedRing[T], value: T): PSinglyLinkedNode[T] =
|
||||
proc find*[T](L: SinglyLinkedRing[T], value: T): SinglyLinkedNode[T] =
|
||||
## searches in the list for a value. Returns nil if the value does not
|
||||
## exist.
|
||||
findImpl()
|
||||
@@ -300,5 +306,3 @@ proc remove*[T](L: var TDoublyLinkedRing[T], n: PDoublyLinkedNode[T]) =
|
||||
L.head = nil
|
||||
else:
|
||||
L.head = L.head.prev
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -12,13 +12,15 @@
|
||||
import math
|
||||
|
||||
type
|
||||
TQueue* {.pure, final.}[T] = object ## a queue
|
||||
Queue*[T] = object ## a queue
|
||||
data: seq[T]
|
||||
rd, wr, count, mask: int
|
||||
|
||||
|
||||
{.deprecated: [TQueue: Queue].}
|
||||
|
||||
proc initQueue*[T](initialSize=4): TQueue[T] =
|
||||
## creates a new queue. `initialSize` needs to be a power of 2.
|
||||
assert IsPowerOfTwo(initialSize)
|
||||
assert isPowerOfTwo(initialSize)
|
||||
result.mask = initialSize-1
|
||||
newSeq(result.data, initialSize)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2011 Alex Mitchell
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -31,7 +31,7 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block::
|
||||
## let
|
||||
## s1 = @[1, 2, 3]
|
||||
## s2 = @[4, 5]
|
||||
@@ -50,7 +50,7 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
|
||||
proc deduplicate*[T](seq1: seq[T]): seq[T] =
|
||||
## Returns a new sequence without duplicates.
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block::
|
||||
## let
|
||||
## dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4]
|
||||
## dup2 = @["a", "a", "c", "d", "d"]
|
||||
@@ -69,7 +69,7 @@ proc zip*[S, T](seq1: seq[S], seq2: seq[T]): seq[tuple[a: S, b: T]] =
|
||||
## fields `a` and `b`. If one sequence is shorter, the remaining items in the
|
||||
## longer sequence are discarded. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block::
|
||||
## let
|
||||
## short = @[1, 2, 3]
|
||||
## long = @[6, 5, 4, 3, 2, 1]
|
||||
@@ -104,7 +104,7 @@ proc distribute*[T](s: seq[T], num: int, spread = true): seq[seq[T]] =
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block::
|
||||
## let numbers = @[1, 2, 3, 4, 5, 6, 7]
|
||||
## assert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
|
||||
## assert numbers.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]]
|
||||
@@ -155,7 +155,7 @@ iterator filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): T =
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block::
|
||||
## let numbers = @[1, 4, 5, 8, 9, 7, 4]
|
||||
## for n in filter(numbers, proc (x: int): bool = x mod 2 == 0):
|
||||
## echo($n)
|
||||
@@ -169,7 +169,7 @@ proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T] =
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block::
|
||||
## let
|
||||
## colors = @["red", "yellow", "black"]
|
||||
## f1 = filter(colors, proc(x: string): bool = x.len < 6)
|
||||
@@ -184,7 +184,7 @@ proc keepIf*[T](seq1: var seq[T], pred: proc(item: T): bool {.closure.}) =
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block::
|
||||
## var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1]
|
||||
## keepIf(floats, proc(x: float): bool = x > 10)
|
||||
## assert floats == @[13.0, 12.5, 10.1]
|
||||
@@ -202,7 +202,7 @@ proc delete*[T](s: var seq[T], first=0, last=0) =
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
##.. code-block:: nimrod
|
||||
##.. code-block::
|
||||
## let outcome = @[1,1,1,1,1,1,1,1]
|
||||
## var dest = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
|
||||
## dest.delete(3, 8)
|
||||
@@ -223,7 +223,7 @@ proc insert*[T](dest: var seq[T], src: openArray[T], pos=0) =
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
##.. code-block:: nimrod
|
||||
##.. code-block::
|
||||
## var dest = @[1,1,1,1,1,1,1,1]
|
||||
## let
|
||||
## src = @[2,2,2,2,2,2]
|
||||
@@ -254,7 +254,7 @@ template filterIt*(seq1, pred: expr): expr {.immediate.} =
|
||||
## the ``it`` variable for testing, like: ``filterIt("abcxyz", it == 'x')``.
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block::
|
||||
## let
|
||||
## temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44]
|
||||
## acceptable = filterIt(temperatures, it < 50 and it > -10)
|
||||
@@ -273,7 +273,7 @@ template keepItIf*(varSeq, pred: expr) =
|
||||
## the ``it`` variable for testing, like: ``keepItIf("abcxyz", it == 'x')``.
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block::
|
||||
## var candidates = @["foo", "bar", "baz", "foobar"]
|
||||
## keepItIf(candidates, it.len == 3 and it[0] == 'b')
|
||||
## assert candidates == @["bar", "baz"]
|
||||
@@ -292,7 +292,7 @@ template toSeq*(iter: expr): expr {.immediate.} =
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block::
|
||||
## let
|
||||
## numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
## odd_numbers = toSeq(filter(numeric) do (x: int) -> bool:
|
||||
@@ -318,18 +318,18 @@ template foldl*(sequence, operation: expr): expr =
|
||||
## the sequence of numbers 1, 2 and 3 will be parenthesized as (((1) - 2) -
|
||||
## 3). Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block::
|
||||
## let
|
||||
## numbers = @[5, 9, 11]
|
||||
## addition = foldl(numbers, a + b)
|
||||
## substraction = foldl(numbers, a - b)
|
||||
## multiplication = foldl(numbers, a * b)
|
||||
## words = @["nim", "rod", "is", "cool"]
|
||||
## words = @["nim", "is", "cool"]
|
||||
## concatenation = foldl(words, a & b)
|
||||
## assert addition == 25, "Addition is (((5)+9)+11)"
|
||||
## assert substraction == -15, "Substraction is (((5)-9)-11)"
|
||||
## assert multiplication == 495, "Multiplication is (((5)*9)*11)"
|
||||
## assert concatenation == "nimrodiscool"
|
||||
## assert concatenation == "nimiscool"
|
||||
assert sequence.len > 0, "Can't fold empty sequences"
|
||||
var result {.gensym.}: type(sequence[0])
|
||||
result = sequence[0]
|
||||
@@ -354,18 +354,18 @@ template foldr*(sequence, operation: expr): expr =
|
||||
## the sequence of numbers 1, 2 and 3 will be parenthesized as (1 - (2 -
|
||||
## (3))). Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block::
|
||||
## let
|
||||
## numbers = @[5, 9, 11]
|
||||
## addition = foldr(numbers, a + b)
|
||||
## substraction = foldr(numbers, a - b)
|
||||
## multiplication = foldr(numbers, a * b)
|
||||
## words = @["nim", "rod", "is", "cool"]
|
||||
## words = @["nim", "is", "cool"]
|
||||
## concatenation = foldr(words, a & b)
|
||||
## assert addition == 25, "Addition is (5+(9+(11)))"
|
||||
## assert substraction == 7, "Substraction is (5-(9-(11)))"
|
||||
## assert multiplication == 495, "Multiplication is (5*(9*(11)))"
|
||||
## assert concatenation == "nimrodiscool"
|
||||
## assert concatenation == "nimiscool"
|
||||
assert sequence.len > 0, "Can't fold empty sequences"
|
||||
var result {.gensym.}: type(sequence[0])
|
||||
result = sequence[sequence.len - 1]
|
||||
@@ -384,7 +384,7 @@ template mapIt*(seq1, typ, pred: expr): expr =
|
||||
## since the new returned sequence can have a different type than the
|
||||
## original. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block::
|
||||
## let
|
||||
## nums = @[1, 2, 3, 4]
|
||||
## strings = nums.mapIt(string, $(4 * it))
|
||||
@@ -401,7 +401,7 @@ template mapIt*(varSeq, pred: expr) =
|
||||
## expression. The expression has to return the same type as the sequence you
|
||||
## are mutating. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block::
|
||||
## var nums = @[1, 2, 3, 4]
|
||||
## nums.mapIt(it * 3)
|
||||
## assert nums[0] + nums[3] == 15
|
||||
@@ -412,7 +412,7 @@ template mapIt*(varSeq, pred: expr) =
|
||||
template newSeqWith*(len: int, init: expr): expr =
|
||||
## creates a new sequence, calling `init` to initialize each value. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block::
|
||||
## var seq2D = newSeqWith(20, newSeq[bool](10))
|
||||
## seq2D[0][0] = true
|
||||
## seq2D[1][0] = true
|
||||
@@ -503,12 +503,12 @@ when isMainModule:
|
||||
addition = foldl(numbers, a + b)
|
||||
substraction = foldl(numbers, a - b)
|
||||
multiplication = foldl(numbers, a * b)
|
||||
words = @["nim", "rod", "is", "cool"]
|
||||
words = @["nim", "is", "cool"]
|
||||
concatenation = foldl(words, a & b)
|
||||
assert addition == 25, "Addition is (((5)+9)+11)"
|
||||
assert substraction == -15, "Substraction is (((5)-9)-11)"
|
||||
assert multiplication == 495, "Multiplication is (((5)*9)*11)"
|
||||
assert concatenation == "nimrodiscool"
|
||||
assert concatenation == "nimiscool"
|
||||
|
||||
block: # foldr tests
|
||||
let
|
||||
@@ -516,12 +516,12 @@ when isMainModule:
|
||||
addition = foldr(numbers, a + b)
|
||||
substraction = foldr(numbers, a - b)
|
||||
multiplication = foldr(numbers, a * b)
|
||||
words = @["nim", "rod", "is", "cool"]
|
||||
words = @["nim", "is", "cool"]
|
||||
concatenation = foldr(words, a & b)
|
||||
assert addition == 25, "Addition is (5+(9+(11)))"
|
||||
assert substraction == 7, "Substraction is (5-(9-(11)))"
|
||||
assert multiplication == 495, "Multiplication is (5*(9*(11)))"
|
||||
assert concatenation == "nimrodiscool"
|
||||
assert concatenation == "nimiscool"
|
||||
|
||||
block: # delete tests
|
||||
let outcome = @[1,1,1,1,1,1,1,1]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -24,17 +24,19 @@ when not defined(nimhygiene):
|
||||
{.pragma: dirty.}
|
||||
|
||||
type
|
||||
TSlotEnum = enum seEmpty, seFilled, seDeleted
|
||||
TKeyValuePair[A] = tuple[slot: TSlotEnum, key: A]
|
||||
TKeyValuePairSeq[A] = seq[TKeyValuePair[A]]
|
||||
TSet* {.final, myShallow.}[A] = object ## \
|
||||
SlotEnum = enum seEmpty, seFilled, seDeleted
|
||||
KeyValuePair[A] = tuple[slot: SlotEnum, key: A]
|
||||
KeyValuePairSeq[A] = seq[KeyValuePair[A]]
|
||||
HashSet* {.myShallow.}[A] = object ## \
|
||||
## A generic hash set.
|
||||
##
|
||||
## Use `init() <#init,TSet[A],int>`_ or `initSet[type]() <#initSet>`_
|
||||
## Use `init() <#init,HashSet[A],int>`_ or `initSet[type]() <#initSet>`_
|
||||
## before calling other procs on it.
|
||||
data: TKeyValuePairSeq[A]
|
||||
data: KeyValuePairSeq[A]
|
||||
counter: int
|
||||
|
||||
{.deprecated: [TSet: HashSet].}
|
||||
|
||||
proc isValid*[A](s: TSet[A]): bool =
|
||||
## Returns `true` if the set has been initialized with `initSet <#initSet>`_.
|
||||
##
|
||||
@@ -43,7 +45,7 @@ proc isValid*[A](s: TSet[A]): bool =
|
||||
## your own procs to verify that sets passed to your procs are correctly
|
||||
## initialized. Example:
|
||||
##
|
||||
## .. code-block :: nimrod
|
||||
## .. code-block ::
|
||||
## proc savePreferences(options: TSet[string]) =
|
||||
## assert options.isValid, "Pass an initialized set!"
|
||||
## # Do stuff here, may crash in release builds!
|
||||
@@ -490,19 +492,20 @@ proc map*[A, B](data: TSet[A], op: proc (x: A): B {.closure.}): TSet[B] =
|
||||
# ------------------------------ ordered set ------------------------------
|
||||
|
||||
type
|
||||
TOrderedKeyValuePair[A] = tuple[
|
||||
slot: TSlotEnum, next: int, key: A]
|
||||
TOrderedKeyValuePairSeq[A] = seq[TOrderedKeyValuePair[A]]
|
||||
TOrderedSet* {.
|
||||
final, myShallow.}[A] = object ## \
|
||||
OrderedKeyValuePair[A] = tuple[
|
||||
slot: SlotEnum, next: int, key: A]
|
||||
OrderedKeyValuePairSeq[A] = seq[OrderedKeyValuePair[A]]
|
||||
OrderedSet* {.myShallow.}[A] = object ## \
|
||||
## A generic hash set that remembers insertion order.
|
||||
##
|
||||
## Use `init() <#init,TOrderedSet[A],int>`_ or `initOrderedSet[type]()
|
||||
## Use `init() <#init,OrderedSet[A],int>`_ or `initOrderedSet[type]()
|
||||
## <#initOrderedSet>`_ before calling other procs on it.
|
||||
data: TOrderedKeyValuePairSeq[A]
|
||||
counter, first, last: int
|
||||
|
||||
proc isValid*[A](s: TOrderedSet[A]): bool =
|
||||
{.deprecated: [TOrderedSet: OrderedSet].}
|
||||
|
||||
proc isValid*[A](s: OrderedSet[A]): bool =
|
||||
## Returns `true` if the ordered set has been initialized with `initSet
|
||||
## <#initOrderedSet>`_.
|
||||
##
|
||||
@@ -511,13 +514,13 @@ proc isValid*[A](s: TOrderedSet[A]): bool =
|
||||
## in your own procs to verify that ordered sets passed to your procs are
|
||||
## correctly initialized. Example:
|
||||
##
|
||||
## .. code-block :: nimrod
|
||||
## .. code-block::
|
||||
## proc saveTarotCards(cards: TOrderedSet[int]) =
|
||||
## assert cards.isValid, "Pass an initialized set!"
|
||||
## # Do stuff here, may crash in release builds!
|
||||
result = not s.data.isNil
|
||||
|
||||
proc len*[A](s: TOrderedSet[A]): int {.inline.} =
|
||||
proc len*[A](s: OrderedSet[A]): int {.inline.} =
|
||||
## Returns the number of keys in `s`.
|
||||
##
|
||||
## Due to an implementation detail you can call this proc on variables which
|
||||
@@ -734,14 +737,14 @@ proc `==`*[A](s, t: TOrderedSet[A]): bool =
|
||||
proc testModule() =
|
||||
## Internal micro test to validate docstrings and such.
|
||||
block isValidTest:
|
||||
var options: TSet[string]
|
||||
var options: HashSet[string]
|
||||
proc savePreferences(options: TSet[string]) =
|
||||
assert options.isValid, "Pass an initialized set!"
|
||||
options = initSet[string]()
|
||||
options.savePreferences
|
||||
|
||||
block lenTest:
|
||||
var values: TSet[int]
|
||||
var values: HashSet[int]
|
||||
assert(not values.isValid)
|
||||
assert values.len == 0
|
||||
assert values.card == 0
|
||||
@@ -835,14 +838,14 @@ proc testModule() =
|
||||
assert b == toSet(["1", "2", "3"])
|
||||
|
||||
block isValidTest:
|
||||
var cards: TOrderedSet[string]
|
||||
var cards: OrderedSet[string]
|
||||
proc saveTarotCards(cards: TOrderedSet[string]) =
|
||||
assert cards.isValid, "Pass an initialized set!"
|
||||
cards = initOrderedSet[string]()
|
||||
cards.saveTarotCards
|
||||
|
||||
block lenTest:
|
||||
var values: TOrderedSet[int]
|
||||
var values: OrderedSet[int]
|
||||
assert(not values.isValid)
|
||||
assert values.len == 0
|
||||
assert values.card == 0
|
||||
@@ -879,7 +882,7 @@ proc testModule() =
|
||||
assert(a == b) # https://github.com/Araq/Nimrod/issues/1413
|
||||
|
||||
block initBlocks:
|
||||
var a: TOrderedSet[int]
|
||||
var a: OrderedSet[int]
|
||||
a.init(4)
|
||||
a.incl(2)
|
||||
a.init
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -28,7 +28,7 @@
|
||||
## you add such a proc for your custom type everything will work. See this
|
||||
## example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block::
|
||||
## type
|
||||
## Person = object
|
||||
## firstName, lastName: string
|
||||
@@ -61,13 +61,15 @@ import
|
||||
{.pragma: myShallow.}
|
||||
|
||||
type
|
||||
TSlotEnum = enum seEmpty, seFilled, seDeleted
|
||||
TKeyValuePair[A, B] = tuple[slot: TSlotEnum, key: A, val: B]
|
||||
TKeyValuePairSeq[A, B] = seq[TKeyValuePair[A, B]]
|
||||
TTable* {.final, myShallow.}[A, B] = object ## generic hash table
|
||||
data: TKeyValuePairSeq[A, B]
|
||||
SlotEnum = enum seEmpty, seFilled, seDeleted
|
||||
KeyValuePair[A, B] = tuple[slot: SlotEnum, key: A, val: B]
|
||||
KeyValuePairSeq[A, B] = seq[KeyValuePair[A, B]]
|
||||
Table* {.myShallow.}[A, B] = object ## generic hash table
|
||||
data: KeyValuePairSeq[A, B]
|
||||
counter: int
|
||||
PTable*[A,B] = ref TTable[A, B]
|
||||
TableRef*[A,B] = ref Table[A, B]
|
||||
|
||||
{.deprecated: [TTable: Table, PTable: TableRef].}
|
||||
|
||||
when not defined(nimhygiene):
|
||||
{.pragma: dirty.}
|
||||
@@ -158,12 +160,12 @@ proc hasKey*[A, B](t: TTable[A, B], key: A): bool =
|
||||
## returns true iff `key` is in the table `t`.
|
||||
result = rawGet(t, key) >= 0
|
||||
|
||||
proc rawInsert[A, B](t: var TTable[A, B], data: var TKeyValuePairSeq[A, B],
|
||||
proc rawInsert[A, B](t: var TTable[A, B], data: var KeyValuePairSeq[A, B],
|
||||
key: A, val: B) =
|
||||
rawInsertImpl()
|
||||
|
||||
proc enlarge[A, B](t: var TTable[A, B]) =
|
||||
var n: TKeyValuePairSeq[A, B]
|
||||
var n: KeyValuePairSeq[A, B]
|
||||
newSeq(n, len(t.data) * growthFactor)
|
||||
for i in countup(0, high(t.data)):
|
||||
if t.data[i].slot == seFilled: rawInsert(t, n, t.data[i].key, t.data[i].val)
|
||||
@@ -347,14 +349,16 @@ proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): PTable[C, B] =
|
||||
# ------------------------------ ordered table ------------------------------
|
||||
|
||||
type
|
||||
TOrderedKeyValuePair[A, B] = tuple[
|
||||
slot: TSlotEnum, next: int, key: A, val: B]
|
||||
TOrderedKeyValuePairSeq[A, B] = seq[TOrderedKeyValuePair[A, B]]
|
||||
TOrderedTable* {.
|
||||
final, myShallow.}[A, B] = object ## table that remembers insertion order
|
||||
data: TOrderedKeyValuePairSeq[A, B]
|
||||
OrderedKeyValuePair[A, B] = tuple[
|
||||
slot: SlotEnum, next: int, key: A, val: B]
|
||||
OrderedKeyValuePairSeq[A, B] = seq[OrderedKeyValuePair[A, B]]
|
||||
OrderedTable* {.
|
||||
myShallow.}[A, B] = object ## table that remembers insertion order
|
||||
data: OrderedKeyValuePairSeq[A, B]
|
||||
counter, first, last: int
|
||||
POrderedTable*[A, B] = ref TOrderedTable[A, B]
|
||||
OrderedTableRef*[A, B] = ref OrderedTable[A, B]
|
||||
|
||||
{.deprecated: [TOrderedTable: OrderedTable, POrderedTable: OrderedTableRef].}
|
||||
|
||||
proc len*[A, B](t: TOrderedTable[A, B]): int {.inline.} =
|
||||
## returns the number of keys in `t`.
|
||||
@@ -608,11 +612,13 @@ proc sort*[A, B](t: POrderedTable[A, B],
|
||||
# ------------------------------ count tables -------------------------------
|
||||
|
||||
type
|
||||
TCountTable* {.final, myShallow.}[
|
||||
CountTable* {.myShallow.}[
|
||||
A] = object ## table that counts the number of each key
|
||||
data: seq[tuple[key: A, val: int]]
|
||||
counter: int
|
||||
PCountTable*[A] = ref TCountTable[A]
|
||||
CountTableRef*[A] = ref CountTable[A]
|
||||
|
||||
{.deprecated: [TCountTable: CountTable, PCountTable: CountTableRef].}
|
||||
|
||||
proc len*[A](t: TCountTable[A]): int =
|
||||
## returns the number of keys in `t`.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2010 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -12,18 +12,20 @@
|
||||
import strutils
|
||||
|
||||
type
|
||||
TColor* = distinct int ## a color stored as RGB
|
||||
Color* = distinct int ## a color stored as RGB
|
||||
|
||||
proc `==` *(a, b: TColor): bool {.borrow.}
|
||||
{.deprecated: [TColor: Color].}
|
||||
|
||||
proc `==` *(a, b: Color): bool {.borrow.}
|
||||
## compares two colors.
|
||||
|
||||
template extract(a: TColor, r, g, b: expr) {.immediate.}=
|
||||
template extract(a: Color, r, g, b: expr) {.immediate.}=
|
||||
var r = a.int shr 16 and 0xff
|
||||
var g = a.int shr 8 and 0xff
|
||||
var b = a.int and 0xff
|
||||
|
||||
template rawRGB(r, g, b: int): expr =
|
||||
TColor(r shl 16 or g shl 8 or b)
|
||||
Color(r shl 16 or g shl 8 or b)
|
||||
|
||||
template colorOp(op: expr) {.immediate.} =
|
||||
extract(a, ar, ag, ab)
|
||||
@@ -38,23 +40,23 @@ proc satMinus(a, b: int): int {.inline.} =
|
||||
result = a -% b
|
||||
if result < 0: result = 0
|
||||
|
||||
proc `+`*(a, b: TColor): TColor =
|
||||
proc `+`*(a, b: Color): Color =
|
||||
## adds two colors: This uses saturated artithmetic, so that each color
|
||||
## component cannot overflow (255 is used as a maximum).
|
||||
colorOp(satPlus)
|
||||
|
||||
proc `-`*(a, b: TColor): TColor =
|
||||
proc `-`*(a, b: Color): Color =
|
||||
## substracts two colors: This uses saturated artithmetic, so that each color
|
||||
## component cannot overflow (255 is used as a maximum).
|
||||
colorOp(satMinus)
|
||||
|
||||
proc extractRGB*(a: TColor): tuple[r, g, b: range[0..255]] =
|
||||
proc extractRGB*(a: Color): tuple[r, g, b: range[0..255]] =
|
||||
## extracts the red/green/blue components of the color `a`.
|
||||
result.r = a.int shr 16 and 0xff
|
||||
result.g = a.int shr 8 and 0xff
|
||||
result.b = a.int and 0xff
|
||||
|
||||
proc intensity*(a: TColor, f: float): TColor =
|
||||
proc intensity*(a: Color, f: float): Color =
|
||||
## returns `a` with intensity `f`. `f` should be a float from 0.0 (completely
|
||||
## dark) to 1.0 (full color intensity).
|
||||
var r = toInt(toFloat(a.int shr 16 and 0xff) * f)
|
||||
@@ -65,7 +67,7 @@ proc intensity*(a: TColor, f: float): TColor =
|
||||
if b >% 255: b = 255
|
||||
result = rawRGB(r, g, b)
|
||||
|
||||
template mix*(a, b: TColor, fn: expr): expr =
|
||||
template mix*(a, b: Color, fn: expr): expr =
|
||||
## uses `fn` to mix the colors `a` and `b`. `fn` is invoked for each component
|
||||
## R, G, and B. This is a template because `fn` should be inlined and the
|
||||
## compiler cannot inline proc pointers yet. If `fn`'s result is not in the
|
||||
@@ -84,146 +86,146 @@ template mix*(a, b: TColor, fn: expr): expr =
|
||||
|
||||
|
||||
const
|
||||
colAliceBlue* = TColor(0xF0F8FF)
|
||||
colAntiqueWhite* = TColor(0xFAEBD7)
|
||||
colAqua* = TColor(0x00FFFF)
|
||||
colAquamarine* = TColor(0x7FFFD4)
|
||||
colAzure* = TColor(0xF0FFFF)
|
||||
colBeige* = TColor(0xF5F5DC)
|
||||
colBisque* = TColor(0xFFE4C4)
|
||||
colBlack* = TColor(0x000000)
|
||||
colBlanchedAlmond* = TColor(0xFFEBCD)
|
||||
colBlue* = TColor(0x0000FF)
|
||||
colBlueViolet* = TColor(0x8A2BE2)
|
||||
colBrown* = TColor(0xA52A2A)
|
||||
colBurlyWood* = TColor(0xDEB887)
|
||||
colCadetBlue* = TColor(0x5F9EA0)
|
||||
colChartreuse* = TColor(0x7FFF00)
|
||||
colChocolate* = TColor(0xD2691E)
|
||||
colCoral* = TColor(0xFF7F50)
|
||||
colCornflowerBlue* = TColor(0x6495ED)
|
||||
colCornsilk* = TColor(0xFFF8DC)
|
||||
colCrimson* = TColor(0xDC143C)
|
||||
colCyan* = TColor(0x00FFFF)
|
||||
colDarkBlue* = TColor(0x00008B)
|
||||
colDarkCyan* = TColor(0x008B8B)
|
||||
colDarkGoldenRod* = TColor(0xB8860B)
|
||||
colDarkGray* = TColor(0xA9A9A9)
|
||||
colDarkGreen* = TColor(0x006400)
|
||||
colDarkKhaki* = TColor(0xBDB76B)
|
||||
colDarkMagenta* = TColor(0x8B008B)
|
||||
colDarkOliveGreen* = TColor(0x556B2F)
|
||||
colDarkorange* = TColor(0xFF8C00)
|
||||
colDarkOrchid* = TColor(0x9932CC)
|
||||
colDarkRed* = TColor(0x8B0000)
|
||||
colDarkSalmon* = TColor(0xE9967A)
|
||||
colDarkSeaGreen* = TColor(0x8FBC8F)
|
||||
colDarkSlateBlue* = TColor(0x483D8B)
|
||||
colDarkSlateGray* = TColor(0x2F4F4F)
|
||||
colDarkTurquoise* = TColor(0x00CED1)
|
||||
colDarkViolet* = TColor(0x9400D3)
|
||||
colDeepPink* = TColor(0xFF1493)
|
||||
colDeepSkyBlue* = TColor(0x00BFFF)
|
||||
colDimGray* = TColor(0x696969)
|
||||
colDodgerBlue* = TColor(0x1E90FF)
|
||||
colFireBrick* = TColor(0xB22222)
|
||||
colFloralWhite* = TColor(0xFFFAF0)
|
||||
colForestGreen* = TColor(0x228B22)
|
||||
colFuchsia* = TColor(0xFF00FF)
|
||||
colGainsboro* = TColor(0xDCDCDC)
|
||||
colGhostWhite* = TColor(0xF8F8FF)
|
||||
colGold* = TColor(0xFFD700)
|
||||
colGoldenRod* = TColor(0xDAA520)
|
||||
colGray* = TColor(0x808080)
|
||||
colGreen* = TColor(0x008000)
|
||||
colGreenYellow* = TColor(0xADFF2F)
|
||||
colHoneyDew* = TColor(0xF0FFF0)
|
||||
colHotPink* = TColor(0xFF69B4)
|
||||
colIndianRed* = TColor(0xCD5C5C)
|
||||
colIndigo* = TColor(0x4B0082)
|
||||
colIvory* = TColor(0xFFFFF0)
|
||||
colKhaki* = TColor(0xF0E68C)
|
||||
colLavender* = TColor(0xE6E6FA)
|
||||
colLavenderBlush* = TColor(0xFFF0F5)
|
||||
colLawnGreen* = TColor(0x7CFC00)
|
||||
colLemonChiffon* = TColor(0xFFFACD)
|
||||
colLightBlue* = TColor(0xADD8E6)
|
||||
colLightCoral* = TColor(0xF08080)
|
||||
colLightCyan* = TColor(0xE0FFFF)
|
||||
colLightGoldenRodYellow* = TColor(0xFAFAD2)
|
||||
colLightGrey* = TColor(0xD3D3D3)
|
||||
colLightGreen* = TColor(0x90EE90)
|
||||
colLightPink* = TColor(0xFFB6C1)
|
||||
colLightSalmon* = TColor(0xFFA07A)
|
||||
colLightSeaGreen* = TColor(0x20B2AA)
|
||||
colLightSkyBlue* = TColor(0x87CEFA)
|
||||
colLightSlateGray* = TColor(0x778899)
|
||||
colLightSteelBlue* = TColor(0xB0C4DE)
|
||||
colLightYellow* = TColor(0xFFFFE0)
|
||||
colLime* = TColor(0x00FF00)
|
||||
colLimeGreen* = TColor(0x32CD32)
|
||||
colLinen* = TColor(0xFAF0E6)
|
||||
colMagenta* = TColor(0xFF00FF)
|
||||
colMaroon* = TColor(0x800000)
|
||||
colMediumAquaMarine* = TColor(0x66CDAA)
|
||||
colMediumBlue* = TColor(0x0000CD)
|
||||
colMediumOrchid* = TColor(0xBA55D3)
|
||||
colMediumPurple* = TColor(0x9370D8)
|
||||
colMediumSeaGreen* = TColor(0x3CB371)
|
||||
colMediumSlateBlue* = TColor(0x7B68EE)
|
||||
colMediumSpringGreen* = TColor(0x00FA9A)
|
||||
colMediumTurquoise* = TColor(0x48D1CC)
|
||||
colMediumVioletRed* = TColor(0xC71585)
|
||||
colMidnightBlue* = TColor(0x191970)
|
||||
colMintCream* = TColor(0xF5FFFA)
|
||||
colMistyRose* = TColor(0xFFE4E1)
|
||||
colMoccasin* = TColor(0xFFE4B5)
|
||||
colNavajoWhite* = TColor(0xFFDEAD)
|
||||
colNavy* = TColor(0x000080)
|
||||
colOldLace* = TColor(0xFDF5E6)
|
||||
colOlive* = TColor(0x808000)
|
||||
colOliveDrab* = TColor(0x6B8E23)
|
||||
colOrange* = TColor(0xFFA500)
|
||||
colOrangeRed* = TColor(0xFF4500)
|
||||
colOrchid* = TColor(0xDA70D6)
|
||||
colPaleGoldenRod* = TColor(0xEEE8AA)
|
||||
colPaleGreen* = TColor(0x98FB98)
|
||||
colPaleTurquoise* = TColor(0xAFEEEE)
|
||||
colPaleVioletRed* = TColor(0xD87093)
|
||||
colPapayaWhip* = TColor(0xFFEFD5)
|
||||
colPeachPuff* = TColor(0xFFDAB9)
|
||||
colPeru* = TColor(0xCD853F)
|
||||
colPink* = TColor(0xFFC0CB)
|
||||
colPlum* = TColor(0xDDA0DD)
|
||||
colPowderBlue* = TColor(0xB0E0E6)
|
||||
colPurple* = TColor(0x800080)
|
||||
colRed* = TColor(0xFF0000)
|
||||
colRosyBrown* = TColor(0xBC8F8F)
|
||||
colRoyalBlue* = TColor(0x4169E1)
|
||||
colSaddleBrown* = TColor(0x8B4513)
|
||||
colSalmon* = TColor(0xFA8072)
|
||||
colSandyBrown* = TColor(0xF4A460)
|
||||
colSeaGreen* = TColor(0x2E8B57)
|
||||
colSeaShell* = TColor(0xFFF5EE)
|
||||
colSienna* = TColor(0xA0522D)
|
||||
colSilver* = TColor(0xC0C0C0)
|
||||
colSkyBlue* = TColor(0x87CEEB)
|
||||
colSlateBlue* = TColor(0x6A5ACD)
|
||||
colSlateGray* = TColor(0x708090)
|
||||
colSnow* = TColor(0xFFFAFA)
|
||||
colSpringGreen* = TColor(0x00FF7F)
|
||||
colSteelBlue* = TColor(0x4682B4)
|
||||
colTan* = TColor(0xD2B48C)
|
||||
colTeal* = TColor(0x008080)
|
||||
colThistle* = TColor(0xD8BFD8)
|
||||
colTomato* = TColor(0xFF6347)
|
||||
colTurquoise* = TColor(0x40E0D0)
|
||||
colViolet* = TColor(0xEE82EE)
|
||||
colWheat* = TColor(0xF5DEB3)
|
||||
colWhite* = TColor(0xFFFFFF)
|
||||
colWhiteSmoke* = TColor(0xF5F5F5)
|
||||
colYellow* = TColor(0xFFFF00)
|
||||
colYellowGreen* = TColor(0x9ACD32)
|
||||
colAliceBlue* = Color(0xF0F8FF)
|
||||
colAntiqueWhite* = Color(0xFAEBD7)
|
||||
colAqua* = Color(0x00FFFF)
|
||||
colAquamarine* = Color(0x7FFFD4)
|
||||
colAzure* = Color(0xF0FFFF)
|
||||
colBeige* = Color(0xF5F5DC)
|
||||
colBisque* = Color(0xFFE4C4)
|
||||
colBlack* = Color(0x000000)
|
||||
colBlanchedAlmond* = Color(0xFFEBCD)
|
||||
colBlue* = Color(0x0000FF)
|
||||
colBlueViolet* = Color(0x8A2BE2)
|
||||
colBrown* = Color(0xA52A2A)
|
||||
colBurlyWood* = Color(0xDEB887)
|
||||
colCadetBlue* = Color(0x5F9EA0)
|
||||
colChartreuse* = Color(0x7FFF00)
|
||||
colChocolate* = Color(0xD2691E)
|
||||
colCoral* = Color(0xFF7F50)
|
||||
colCornflowerBlue* = Color(0x6495ED)
|
||||
colCornsilk* = Color(0xFFF8DC)
|
||||
colCrimson* = Color(0xDC143C)
|
||||
colCyan* = Color(0x00FFFF)
|
||||
colDarkBlue* = Color(0x00008B)
|
||||
colDarkCyan* = Color(0x008B8B)
|
||||
colDarkGoldenRod* = Color(0xB8860B)
|
||||
colDarkGray* = Color(0xA9A9A9)
|
||||
colDarkGreen* = Color(0x006400)
|
||||
colDarkKhaki* = Color(0xBDB76B)
|
||||
colDarkMagenta* = Color(0x8B008B)
|
||||
colDarkOliveGreen* = Color(0x556B2F)
|
||||
colDarkorange* = Color(0xFF8C00)
|
||||
colDarkOrchid* = Color(0x9932CC)
|
||||
colDarkRed* = Color(0x8B0000)
|
||||
colDarkSalmon* = Color(0xE9967A)
|
||||
colDarkSeaGreen* = Color(0x8FBC8F)
|
||||
colDarkSlateBlue* = Color(0x483D8B)
|
||||
colDarkSlateGray* = Color(0x2F4F4F)
|
||||
colDarkTurquoise* = Color(0x00CED1)
|
||||
colDarkViolet* = Color(0x9400D3)
|
||||
colDeepPink* = Color(0xFF1493)
|
||||
colDeepSkyBlue* = Color(0x00BFFF)
|
||||
colDimGray* = Color(0x696969)
|
||||
colDodgerBlue* = Color(0x1E90FF)
|
||||
colFireBrick* = Color(0xB22222)
|
||||
colFloralWhite* = Color(0xFFFAF0)
|
||||
colForestGreen* = Color(0x228B22)
|
||||
colFuchsia* = Color(0xFF00FF)
|
||||
colGainsboro* = Color(0xDCDCDC)
|
||||
colGhostWhite* = Color(0xF8F8FF)
|
||||
colGold* = Color(0xFFD700)
|
||||
colGoldenRod* = Color(0xDAA520)
|
||||
colGray* = Color(0x808080)
|
||||
colGreen* = Color(0x008000)
|
||||
colGreenYellow* = Color(0xADFF2F)
|
||||
colHoneyDew* = Color(0xF0FFF0)
|
||||
colHotPink* = Color(0xFF69B4)
|
||||
colIndianRed* = Color(0xCD5C5C)
|
||||
colIndigo* = Color(0x4B0082)
|
||||
colIvory* = Color(0xFFFFF0)
|
||||
colKhaki* = Color(0xF0E68C)
|
||||
colLavender* = Color(0xE6E6FA)
|
||||
colLavenderBlush* = Color(0xFFF0F5)
|
||||
colLawnGreen* = Color(0x7CFC00)
|
||||
colLemonChiffon* = Color(0xFFFACD)
|
||||
colLightBlue* = Color(0xADD8E6)
|
||||
colLightCoral* = Color(0xF08080)
|
||||
colLightCyan* = Color(0xE0FFFF)
|
||||
colLightGoldenRodYellow* = Color(0xFAFAD2)
|
||||
colLightGrey* = Color(0xD3D3D3)
|
||||
colLightGreen* = Color(0x90EE90)
|
||||
colLightPink* = Color(0xFFB6C1)
|
||||
colLightSalmon* = Color(0xFFA07A)
|
||||
colLightSeaGreen* = Color(0x20B2AA)
|
||||
colLightSkyBlue* = Color(0x87CEFA)
|
||||
colLightSlateGray* = Color(0x778899)
|
||||
colLightSteelBlue* = Color(0xB0C4DE)
|
||||
colLightYellow* = Color(0xFFFFE0)
|
||||
colLime* = Color(0x00FF00)
|
||||
colLimeGreen* = Color(0x32CD32)
|
||||
colLinen* = Color(0xFAF0E6)
|
||||
colMagenta* = Color(0xFF00FF)
|
||||
colMaroon* = Color(0x800000)
|
||||
colMediumAquaMarine* = Color(0x66CDAA)
|
||||
colMediumBlue* = Color(0x0000CD)
|
||||
colMediumOrchid* = Color(0xBA55D3)
|
||||
colMediumPurple* = Color(0x9370D8)
|
||||
colMediumSeaGreen* = Color(0x3CB371)
|
||||
colMediumSlateBlue* = Color(0x7B68EE)
|
||||
colMediumSpringGreen* = Color(0x00FA9A)
|
||||
colMediumTurquoise* = Color(0x48D1CC)
|
||||
colMediumVioletRed* = Color(0xC71585)
|
||||
colMidnightBlue* = Color(0x191970)
|
||||
colMintCream* = Color(0xF5FFFA)
|
||||
colMistyRose* = Color(0xFFE4E1)
|
||||
colMoccasin* = Color(0xFFE4B5)
|
||||
colNavajoWhite* = Color(0xFFDEAD)
|
||||
colNavy* = Color(0x000080)
|
||||
colOldLace* = Color(0xFDF5E6)
|
||||
colOlive* = Color(0x808000)
|
||||
colOliveDrab* = Color(0x6B8E23)
|
||||
colOrange* = Color(0xFFA500)
|
||||
colOrangeRed* = Color(0xFF4500)
|
||||
colOrchid* = Color(0xDA70D6)
|
||||
colPaleGoldenRod* = Color(0xEEE8AA)
|
||||
colPaleGreen* = Color(0x98FB98)
|
||||
colPaleTurquoise* = Color(0xAFEEEE)
|
||||
colPaleVioletRed* = Color(0xD87093)
|
||||
colPapayaWhip* = Color(0xFFEFD5)
|
||||
colPeachPuff* = Color(0xFFDAB9)
|
||||
colPeru* = Color(0xCD853F)
|
||||
colPink* = Color(0xFFC0CB)
|
||||
colPlum* = Color(0xDDA0DD)
|
||||
colPowderBlue* = Color(0xB0E0E6)
|
||||
colPurple* = Color(0x800080)
|
||||
colRed* = Color(0xFF0000)
|
||||
colRosyBrown* = Color(0xBC8F8F)
|
||||
colRoyalBlue* = Color(0x4169E1)
|
||||
colSaddleBrown* = Color(0x8B4513)
|
||||
colSalmon* = Color(0xFA8072)
|
||||
colSandyBrown* = Color(0xF4A460)
|
||||
colSeaGreen* = Color(0x2E8B57)
|
||||
colSeaShell* = Color(0xFFF5EE)
|
||||
colSienna* = Color(0xA0522D)
|
||||
colSilver* = Color(0xC0C0C0)
|
||||
colSkyBlue* = Color(0x87CEEB)
|
||||
colSlateBlue* = Color(0x6A5ACD)
|
||||
colSlateGray* = Color(0x708090)
|
||||
colSnow* = Color(0xFFFAFA)
|
||||
colSpringGreen* = Color(0x00FF7F)
|
||||
colSteelBlue* = Color(0x4682B4)
|
||||
colTan* = Color(0xD2B48C)
|
||||
colTeal* = Color(0x008080)
|
||||
colThistle* = Color(0xD8BFD8)
|
||||
colTomato* = Color(0xFF6347)
|
||||
colTurquoise* = Color(0x40E0D0)
|
||||
colViolet* = Color(0xEE82EE)
|
||||
colWheat* = Color(0xF5DEB3)
|
||||
colWhite* = Color(0xFFFFFF)
|
||||
colWhiteSmoke* = Color(0xF5F5F5)
|
||||
colYellow* = Color(0xFFFF00)
|
||||
colYellowGreen* = Color(0x9ACD32)
|
||||
|
||||
colorNames = [
|
||||
("aliceblue", colAliceBlue),
|
||||
@@ -367,11 +369,11 @@ const
|
||||
("yellow", colYellow),
|
||||
("yellowgreen", colYellowGreen)]
|
||||
|
||||
proc `$`*(c: TColor): string =
|
||||
proc `$`*(c: Color): string =
|
||||
## converts a color into its textual representation. Example: ``#00FF00``.
|
||||
result = '#' & toHex(int(c), 6)
|
||||
|
||||
proc binaryStrSearch(x: openarray[tuple[name: string, col: TColor]],
|
||||
proc binaryStrSearch(x: openArray[tuple[name: string, col: Color]],
|
||||
y: string): int =
|
||||
var a = 0
|
||||
var b = len(x) - 1
|
||||
@@ -383,14 +385,14 @@ proc binaryStrSearch(x: openarray[tuple[name: string, col: TColor]],
|
||||
else: return mid
|
||||
result = - 1
|
||||
|
||||
proc parseColor*(name: string): TColor =
|
||||
proc parseColor*(name: string): Color =
|
||||
## parses `name` to a color value. If no valid color could be
|
||||
## parsed ``EInvalidValue`` is raised.
|
||||
if name[0] == '#':
|
||||
result = TColor(parseHexInt(name))
|
||||
result = Color(parseHexInt(name))
|
||||
else:
|
||||
var idx = binaryStrSearch(colorNames, name)
|
||||
if idx < 0: raise newException(EInvalidValue, "unkown color: " & name)
|
||||
if idx < 0: raise newException(ValueError, "unkown color: " & name)
|
||||
result = colorNames[idx][1]
|
||||
|
||||
proc isColor*(name: string): bool =
|
||||
@@ -403,7 +405,7 @@ proc isColor*(name: string): bool =
|
||||
else:
|
||||
result = binaryStrSearch(colorNames, name) >= 0
|
||||
|
||||
proc rgb*(r, g, b: range[0..255]): TColor =
|
||||
proc rgb*(r, g, b: range[0..255]): Color =
|
||||
## constructs a color from RGB values.
|
||||
result = rawRGB(r, g, b)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2010 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -25,9 +25,11 @@ const
|
||||
|
||||
|
||||
type
|
||||
TComplex* = tuple[re, im: float]
|
||||
Complex* = tuple[re, im: float]
|
||||
## a complex number, consisting of a real and an imaginary part
|
||||
|
||||
{.deprecated: [TComplex: Complex].}
|
||||
|
||||
proc `==` *(x, y: TComplex): bool =
|
||||
## Compare two complex numbers `x` and `y` for equality.
|
||||
result = x.re == y.re and x.im == y.im
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Implements Nimrod's 'spawn'.
|
||||
## Implements Nim's 'spawn'.
|
||||
|
||||
when not compileOption("threads"):
|
||||
{.error: "Threadpool requires --threads:on option.".}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import strtabs, times
|
||||
|
||||
proc parseCookies*(s: string): PStringTable =
|
||||
proc parseCookies*(s: string): StringTableRef =
|
||||
## parses cookies into a string table.
|
||||
result = newStringTable(modeCaseInsensitive)
|
||||
var i = 0
|
||||
@@ -39,7 +39,7 @@ proc setCookie*(key, value: string, domain = "", path = "",
|
||||
if path != "": result.add("; Path=" & path)
|
||||
if expires != "": result.add("; Expires=" & expires)
|
||||
|
||||
proc setCookie*(key, value: string, expires: TTimeInfo,
|
||||
proc setCookie*(key, value: string, expires: TimeInfo,
|
||||
domain = "", path = "", noName = false): string =
|
||||
## Creates a command in the format of
|
||||
## ``Set-Cookie: key=value; Domain=...; ...``
|
||||
@@ -50,7 +50,7 @@ proc setCookie*(key, value: string, expires: TTimeInfo,
|
||||
format(expires, "ddd',' dd MMM yyyy HH:mm:ss 'UTC'"), noName)
|
||||
|
||||
when isMainModule:
|
||||
var tim = TTime(int(getTime()) + 76 * (60 * 60 * 24))
|
||||
var tim = Time(int(getTime()) + 76 * (60 * 60 * 24))
|
||||
|
||||
echo(setCookie("test", "value", tim.getGMTime()))
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -12,7 +12,9 @@
|
||||
## Windows ``LoadLibrary``.
|
||||
|
||||
type
|
||||
TLibHandle* = pointer ## a handle to a dynamically loaded library
|
||||
LibHandle* = pointer ## a handle to a dynamically loaded library
|
||||
|
||||
{.deprecated: [TLibHandle: LibHandle].}
|
||||
|
||||
proc loadLib*(path: string, global_symbols=false): TLibHandle
|
||||
## loads a library from `path`. Returns nil if the library could not
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -14,18 +14,20 @@ import os, parseutils, strutils
|
||||
|
||||
when not defined(windows):
|
||||
type
|
||||
TConverter = object
|
||||
PConverter* = ptr TConverter ## can convert between two character sets
|
||||
ConverterObj = object
|
||||
EncodingConverter* = ptr ConverterObj ## can convert between two character sets
|
||||
|
||||
else:
|
||||
type
|
||||
TCodePage = distinct int32
|
||||
PConverter* = object
|
||||
dest, src: TCodePage
|
||||
CodePage = distinct int32
|
||||
EncodingConverter* = object
|
||||
dest, src: CodePage
|
||||
|
||||
type
|
||||
EInvalidEncoding* = object of EInvalidValue ## exception that is raised
|
||||
## for encoding errors
|
||||
EncodingError* = object of ValueError ## exception that is raised
|
||||
## for encoding errors
|
||||
|
||||
{.deprecated: [EInvalidEncoding: EncodingError, PConverter: EncodingConverter].}
|
||||
|
||||
when defined(windows):
|
||||
proc eqEncodingNames(a, b: string): bool =
|
||||
@@ -214,26 +216,26 @@ when defined(windows):
|
||||
defaultChar: array[0..1, char]
|
||||
leadByte: array[0..12-1, char]
|
||||
|
||||
proc getCPInfo(codePage: TCodePage, lpCPInfo: var TCpInfo): int32 {.
|
||||
proc getCPInfo(codePage: CodePage, lpCPInfo: var TCpInfo): int32 {.
|
||||
stdcall, importc: "GetCPInfo", dynlib: "kernel32".}
|
||||
|
||||
proc nameToCodePage(name: string): TCodePage =
|
||||
proc nameToCodePage(name: string): CodePage =
|
||||
var nameAsInt: int
|
||||
if parseInt(name, nameAsInt) == 0: nameAsInt = -1
|
||||
for no, na in items(winEncodings):
|
||||
if no == nameAsInt or eqEncodingNames(na, name): return TCodePage(no)
|
||||
result = TCodePage(-1)
|
||||
if no == nameAsInt or eqEncodingNames(na, name): return CodePage(no)
|
||||
result = CodePage(-1)
|
||||
|
||||
proc codePageToName(c: TCodePage): string =
|
||||
proc codePageToName(c: CodePage): string =
|
||||
for no, na in items(winEncodings):
|
||||
if no == int(c):
|
||||
return if na.len != 0: na else: $no
|
||||
result = ""
|
||||
|
||||
proc getACP(): TCodePage {.stdcall, importc: "GetACP", dynlib: "kernel32".}
|
||||
proc getACP(): CodePage {.stdcall, importc: "GetACP", dynlib: "kernel32".}
|
||||
|
||||
proc multiByteToWideChar(
|
||||
codePage: TCodePage,
|
||||
codePage: CodePage,
|
||||
dwFlags: int32,
|
||||
lpMultiByteStr: cstring,
|
||||
cbMultiByte: cint,
|
||||
@@ -242,7 +244,7 @@ when defined(windows):
|
||||
stdcall, importc: "MultiByteToWideChar", dynlib: "kernel32".}
|
||||
|
||||
proc wideCharToMultiByte(
|
||||
codePage: TCodePage,
|
||||
codePage: CodePage,
|
||||
dwFlags: int32,
|
||||
lpWideCharStr: cstring,
|
||||
cchWideChar: cint,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2011 Alex Mitchell
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -14,10 +14,10 @@
|
||||
## it was inspired by Python's PyEE module. There are two ways you can use
|
||||
## events: one is a python-inspired way; the other is more of a C-style way.
|
||||
##
|
||||
## .. code-block:: Nimrod
|
||||
## .. code-block:: Nim
|
||||
## var ee = initEventEmitter()
|
||||
## var genericargs: TEventArgs
|
||||
## proc handleevent(e: TEventArgs) =
|
||||
## var genericargs: EventArgs
|
||||
## proc handleevent(e: EventArgs) =
|
||||
## echo("Handled!")
|
||||
##
|
||||
## # Python way
|
||||
@@ -27,21 +27,24 @@
|
||||
## # C/Java way
|
||||
## # Declare a type
|
||||
## type
|
||||
## TSomeObject = object of TObject
|
||||
## SomeEvent: TEventHandler
|
||||
## var myobj: TSomeObject
|
||||
## SomeObject = object of RootObj
|
||||
## SomeEvent: EventHandler
|
||||
## var myobj: SomeObject
|
||||
## myobj.SomeEvent = initEventHandler("SomeEvent")
|
||||
## myobj.SomeEvent.addHandler(handleevent)
|
||||
## ee.emit(myobj.SomeEvent, genericargs)
|
||||
|
||||
type
|
||||
TEventArgs* = object of TObject ## Base object for event arguments that are passed to callback functions.
|
||||
TEventHandler* = tuple[name: string, handlers: seq[proc(e:TEventArgs) {.closure.}]] ## An eventhandler for an event.
|
||||
EventArgs* = object of RootObj ## Base object for event arguments that are passed to callback functions.
|
||||
EventHandler* = tuple[name: string, handlers: seq[proc(e: EventArgs) {.closure.}]] ## An eventhandler for an event.
|
||||
|
||||
type
|
||||
TEventEmitter* = object {.pure, final.} ## An object that fires events and holds event handlers for an object.
|
||||
s: seq[TEventHandler]
|
||||
EInvalidEvent* = object of EInvalidValue
|
||||
EventEmitter* = object ## An object that fires events and holds event handlers for an object.
|
||||
s: seq[EventHandler]
|
||||
EventError* = object of ValueError
|
||||
|
||||
{.deprecated: [TEventArgs: EventArgs, TEventHandler: EventHandler,
|
||||
TEventEmitter: EventEmitter, EInvalidEvent: EventError].}
|
||||
|
||||
proc initEventHandler*(name: string): TEventHandler =
|
||||
## Initializes an EventHandler with the specified name and returns it.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Dominik Picheta
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -25,13 +25,13 @@ else:
|
||||
import inotify, os, asyncio, tables
|
||||
|
||||
type
|
||||
PFSMonitor* = ref TFSMonitor
|
||||
TFSMonitor = object of TObject
|
||||
FSMonitor* = ref FSMonitorObj
|
||||
FSMonitorObj = object of RootObj
|
||||
fd: cint
|
||||
handleEvent: proc (m: PFSMonitor, ev: TMonitorEvent) {.closure.}
|
||||
handleEvent: proc (m: FSMonitor, ev: MonitorEvent) {.closure.}
|
||||
targets: TTable[cint, string]
|
||||
|
||||
TMonitorEventType* = enum ## Monitor event type
|
||||
MonitorEventType* = enum ## Monitor event type
|
||||
MonitorAccess, ## File was accessed.
|
||||
MonitorAttrib, ## Metadata changed.
|
||||
MonitorCloseWrite, ## Writtable file was closed.
|
||||
@@ -45,8 +45,8 @@ type
|
||||
MonitorOpen, ## File was opened.
|
||||
MonitorAll ## Filter for all event types.
|
||||
|
||||
TMonitorEvent* = object
|
||||
case kind*: TMonitorEventType ## Type of the event.
|
||||
MonitorEvent* = object
|
||||
case kind*: MonitorEventType ## Type of the event.
|
||||
of MonitorMoveSelf, MonitorMoved:
|
||||
oldPath*: string ## Old absolute location
|
||||
newPath*: string ## New absolute location
|
||||
@@ -58,6 +58,9 @@ type
|
||||
## watched.
|
||||
wd*: cint ## Watch descriptor.
|
||||
|
||||
{.deprecated: [PFSMonitor: FSMonitor, TFSMonitor: FSMonitorObj,
|
||||
TMonitorEventType: MonitorEventType, TMonitorEvent: MonitorEvent].}
|
||||
|
||||
const
|
||||
MaxEvents = 100
|
||||
|
||||
@@ -67,7 +70,7 @@ proc newMonitor*(): PFSMonitor =
|
||||
result.targets = initTable[cint, string]()
|
||||
result.fd = inotifyInit()
|
||||
if result.fd < 0:
|
||||
OSError(OSLastError())
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc add*(monitor: PFSMonitor, target: string,
|
||||
filters = {MonitorAll}): cint {.discardable.} =
|
||||
@@ -101,7 +104,7 @@ proc del*(monitor: PFSMonitor, wd: cint) =
|
||||
##
|
||||
## If ``wd`` is not a part of ``monitor`` an EOS error is raised.
|
||||
if inotifyRmWatch(monitor.fd, wd) < 0:
|
||||
OSError(OSLastError())
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc getEvent(m: PFSMonitor, fd: cint): seq[TMonitorEvent] =
|
||||
result = @[]
|
||||
@@ -197,18 +200,19 @@ proc register*(d: PDispatcher, monitor: PFSMonitor,
|
||||
d.register(deleg)
|
||||
|
||||
when isMainModule:
|
||||
var disp = newDispatcher()
|
||||
var monitor = newMonitor()
|
||||
echo monitor.add("/home/dom/inotifytests/")
|
||||
disp.register(monitor,
|
||||
proc (m: PFSMonitor, ev: TMonitorEvent) =
|
||||
echo("Got event: ", ev.kind)
|
||||
if ev.kind == MonitorMoved:
|
||||
echo("From ", ev.oldPath, " to ", ev.newPath)
|
||||
echo("Name is ", ev.name)
|
||||
else:
|
||||
echo("Name ", ev.name, " fullname ", ev.fullName))
|
||||
|
||||
while true:
|
||||
if not disp.poll(): break
|
||||
|
||||
proc main =
|
||||
var disp = newDispatcher()
|
||||
var monitor = newMonitor()
|
||||
echo monitor.add("/home/dom/inotifytests/")
|
||||
disp.register(monitor,
|
||||
proc (m: PFSMonitor, ev: TMonitorEvent) =
|
||||
echo("Got event: ", ev.kind)
|
||||
if ev.kind == MonitorMoved:
|
||||
echo("From ", ev.oldPath, " to ", ev.newPath)
|
||||
echo("Name is ", ev.name)
|
||||
else:
|
||||
echo("Name ", ev.name, " fullname ", ev.fullName))
|
||||
|
||||
while true:
|
||||
if not disp.poll(): break
|
||||
main()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Dominik Picheta
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
@@ -14,8 +14,8 @@ import sockets, strutils, parseutils, times, os, asyncio
|
||||
## by `RFC 959 <http://tools.ietf.org/html/rfc959>`_.
|
||||
##
|
||||
## This module provides both a synchronous and asynchronous implementation.
|
||||
## The asynchronous implementation requires you to use the ``AsyncFTPClient``
|
||||
## function. You are then required to register the ``PAsyncFTPClient`` with a
|
||||
## The asynchronous implementation requires you to use the ``asyncFTPClient``
|
||||
## function. You are then required to register the ``AsyncFTPClient`` with a
|
||||
## asyncio dispatcher using the ``register`` function. Take a look at the
|
||||
## asyncio module documentation for more information.
|
||||
##
|
||||
@@ -24,64 +24,63 @@ import sockets, strutils, parseutils, times, os, asyncio
|
||||
##
|
||||
## Here is some example usage of this module:
|
||||
##
|
||||
## .. code-block:: Nimrod
|
||||
## var ftp = FTPClient("example.org", user = "user", pass = "pass")
|
||||
## .. code-block:: Nim
|
||||
## var ftp = ftpClient("example.org", user = "user", pass = "pass")
|
||||
## ftp.connect()
|
||||
## ftp.retrFile("file.ext", "file.ext")
|
||||
##
|
||||
## **Warning:** The API of this module is unstable, and therefore is subject
|
||||
## to change.
|
||||
|
||||
|
||||
type
|
||||
TFTPClient* = object of TObject
|
||||
FTPClientObj* = object of RootObj
|
||||
case isAsync: bool
|
||||
of false:
|
||||
csock: TSocket # Command connection socket
|
||||
dsock: TSocket # Data connection socket
|
||||
csock: Socket # Command connection socket
|
||||
dsock: Socket # Data connection socket
|
||||
else:
|
||||
dummyA, dummyB: pointer # workaround a Nimrod API issue
|
||||
asyncCSock: PAsyncSocket
|
||||
asyncDSock: PAsyncSocket
|
||||
handleEvent*: proc (ftp: PAsyncFTPClient, ev: TFTPEvent){.closure,gcsafe.}
|
||||
disp: PDispatcher
|
||||
asyncDSockID: PDelegate
|
||||
asyncCSock: AsyncSocket
|
||||
asyncDSock: AsyncSocket
|
||||
handleEvent*: proc (ftp: AsyncFTPClient, ev: FTPEvent){.closure,gcsafe.}
|
||||
disp: Dispatcher
|
||||
asyncDSockID: Delegate
|
||||
user, pass: string
|
||||
address: string
|
||||
port: TPort
|
||||
port: Port
|
||||
|
||||
jobInProgress: bool
|
||||
job: ref TFTPJob
|
||||
job: ref FTPJob
|
||||
|
||||
dsockConnected: bool
|
||||
|
||||
PFTPClient* = ref TFTPClient
|
||||
FTPClient* = ref FTPClientObj
|
||||
|
||||
FTPJobType* = enum
|
||||
JRetrText, JRetr, JStore
|
||||
|
||||
TFTPJob = object
|
||||
FTPJob = object
|
||||
prc: proc (ftp: PFTPClient, async: bool): bool {.nimcall, gcsafe.}
|
||||
case typ*: FTPJobType
|
||||
of JRetrText:
|
||||
lines: string
|
||||
of JRetr, JStore:
|
||||
file: TFile
|
||||
file: File
|
||||
filename: string
|
||||
total: biggestInt # In bytes.
|
||||
progress: biggestInt # In bytes.
|
||||
oneSecond: biggestInt # Bytes transferred in one second.
|
||||
total: BiggestInt # In bytes.
|
||||
progress: BiggestInt # In bytes.
|
||||
oneSecond: BiggestInt # Bytes transferred in one second.
|
||||
lastProgressReport: float # Time
|
||||
toStore: string # Data left to upload (Only used with async)
|
||||
else: nil
|
||||
|
||||
PAsyncFTPClient* = ref TAsyncFTPClient ## Async alternative to TFTPClient.
|
||||
TAsyncFTPClient* = object of TFTPClient
|
||||
AsyncFTPClient* = ref AsyncFTPClientObj ## Async alternative to TFTPClient.
|
||||
AsyncFTPClientObj* = object of FTPClientObj
|
||||
|
||||
FTPEventType* = enum
|
||||
EvTransferProgress, EvLines, EvRetr, EvStore
|
||||
|
||||
TFTPEvent* = object ## Event
|
||||
FTPEvent* = object ## Event
|
||||
filename*: string
|
||||
case typ*: FTPEventType
|
||||
of EvLines:
|
||||
@@ -89,13 +88,19 @@ type
|
||||
of EvRetr, EvStore: ## Retr/Store operation finished.
|
||||
nil
|
||||
of EvTransferProgress:
|
||||
bytesTotal*: biggestInt ## Bytes total.
|
||||
bytesFinished*: biggestInt ## Bytes transferred.
|
||||
speed*: biggestInt ## Speed in bytes/s
|
||||
bytesTotal*: BiggestInt ## Bytes total.
|
||||
bytesFinished*: BiggestInt ## Bytes transferred.
|
||||
speed*: BiggestInt ## Speed in bytes/s
|
||||
currentJob*: FTPJobType ## The current job being performed.
|
||||
|
||||
EInvalidReply* = object of ESynch
|
||||
EFTP* = object of ESynch
|
||||
ReplyError* = object of IOError
|
||||
FTPError* = object of IOError
|
||||
|
||||
{.deprecated: [
|
||||
TFTPClient: FTPClientObj, TFTPJob: FTPJob, PAsyncFTPClient: AsyncFTPClient,
|
||||
TAsyncFTPClient: AsyncFTPClientObj, TFTPEvent: FTPEvent,
|
||||
EInvalidReply: ReplyError, EFTP: FTPError
|
||||
].}
|
||||
|
||||
proc ftpClient*(address: string, port = TPort(21),
|
||||
user, pass = ""): PFTPClient =
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2014 Dominik Picheta
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -12,6 +12,8 @@
|
||||
## may be any Nimrod or user defined type. This module supports matching
|
||||
## of keys in case-sensitive, case-insensitive and style-insensitive modes.
|
||||
|
||||
{.deprecated.}
|
||||
|
||||
import
|
||||
os, hashes, strutils
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -8,13 +8,13 @@
|
||||
#
|
||||
|
||||
## This module implements efficient computations of hash values for diverse
|
||||
## Nimrod types. All the procs are based on these two building blocks: the `!&
|
||||
## Nim types. All the procs are based on these two building blocks: the `!&
|
||||
## proc <#!&>`_ used to start or mix a hash value, and the `!$ proc <#!$>`_
|
||||
## used to *finish* the hash value. If you want to implement hash procs for
|
||||
## your custom types you will end up writing the following kind of skeleton of
|
||||
## code:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: Nim
|
||||
## proc hash(x: Something): THash =
|
||||
## ## Computes a THash from `x`.
|
||||
## var h: THash = 0
|
||||
@@ -29,7 +29,7 @@
|
||||
## like for example objects made up of ``strings``, you can simply hash
|
||||
## together the hash value of the individual fields:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: Nim
|
||||
## proc hash(x: Something): THash =
|
||||
## ## Computes a THash from `x`.
|
||||
## var h: THash = 0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -18,13 +18,13 @@
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## var nim = "Nimrod"
|
||||
## echo h1(a(href="http://nimrod-lang.org", nim))
|
||||
## .. code-block:: Nim
|
||||
## var nim = "Nim"
|
||||
## echo h1(a(href="http://nim-lang.org", nim))
|
||||
##
|
||||
## Writes the string::
|
||||
##
|
||||
## <h1><a href="http://nimrod-lang.org">Nimrod</a></h1>
|
||||
## <h1><a href="http://nim-lang.org">Nim</a></h1>
|
||||
##
|
||||
|
||||
import
|
||||
@@ -53,8 +53,7 @@ proc delete[T](s: var seq[T], attr: T): bool =
|
||||
setLen(s, L-1)
|
||||
result = true
|
||||
|
||||
proc xmlCheckedTag*(e: PNimrodNode, tag: string,
|
||||
optAttr = "", reqAttr = "",
|
||||
proc xmlCheckedTag*(e: PNimrodNode, tag: string, optAttr = "", reqAttr = "",
|
||||
isLeaf = false): PNimrodNode {.compileTime.} =
|
||||
## use this procedure to define a new XML tag
|
||||
|
||||
@@ -484,7 +483,7 @@ macro `var`*(e: expr): expr {.immediate.} =
|
||||
result = xmlCheckedTag(e, "var", commonAttr)
|
||||
|
||||
when isMainModule:
|
||||
var nim = "Nimrod"
|
||||
echo h1(a(href="http://nimrod-code.org", nim))
|
||||
var nim = "Nim"
|
||||
echo h1(a(href="http://nim-lang.org", nim))
|
||||
echo form(action="test", `accept-charset` = "Content-Type")
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -13,7 +13,7 @@
|
||||
## It can be used to parse a wild HTML document and output it as valid XHTML
|
||||
## document (well, if you are lucky):
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: Nim
|
||||
##
|
||||
## echo loadHtml("mydirty.html")
|
||||
##
|
||||
@@ -29,7 +29,7 @@
|
||||
## and write back the modified version. In this case we look for hyperlinks
|
||||
## ending with the extension ``.rst`` and convert them to ``.html``.
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: Nim
|
||||
##
|
||||
## import htmlparser
|
||||
## import xmltree # To use '$' for PXmlNode
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2010 Dominik Picheta, Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -21,7 +21,7 @@
|
||||
## This example uses HTTP GET to retrieve
|
||||
## ``http://google.com``
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: Nim
|
||||
## echo(getContent("http://google.com"))
|
||||
##
|
||||
## Using HTTP POST
|
||||
@@ -31,7 +31,7 @@
|
||||
## uses ``multipart/form-data`` as the ``Content-Type`` to send the HTML to
|
||||
## the server.
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: Nim
|
||||
## var headers: string = "Content-Type: multipart/form-data; boundary=xyz\c\L"
|
||||
## var body: string = "--xyz\c\L"
|
||||
## # soap 1.2 output
|
||||
@@ -54,7 +54,7 @@
|
||||
## on many operating systems. httpclient will use SSL automatically if you give
|
||||
## any of the functions a url with the ``https`` schema, for example:
|
||||
## ``https://github.com/``, you also have to compile with ``ssl`` defined like so:
|
||||
## ``nimrod c -d:ssl ...``.
|
||||
## ``nim c -d:ssl ...``.
|
||||
##
|
||||
## Timeouts
|
||||
## ========
|
||||
@@ -80,34 +80,38 @@ import asyncnet, asyncdispatch
|
||||
import rawsockets
|
||||
|
||||
type
|
||||
TResponse* = tuple[
|
||||
Response* = tuple[
|
||||
version: string,
|
||||
status: string,
|
||||
headers: PStringTable,
|
||||
headers: StringTableRef,
|
||||
body: string]
|
||||
|
||||
PProxy* = ref object
|
||||
Proxy* = ref object
|
||||
url*: TUrl
|
||||
auth*: string
|
||||
|
||||
EInvalidProtocol* = object of ESynch ## exception that is raised when server
|
||||
ProtocolError* = object of IOError ## exception that is raised when server
|
||||
## does not conform to the implemented
|
||||
## protocol
|
||||
|
||||
EHttpRequestErr* = object of ESynch ## Thrown in the ``getContent`` proc
|
||||
## and ``postContent`` proc,
|
||||
## when the server returns an error
|
||||
HttpRequestError* = object of IOError ## Thrown in the ``getContent`` proc
|
||||
## and ``postContent`` proc,
|
||||
## when the server returns an error
|
||||
|
||||
const defUserAgent* = "Nimrod httpclient/0.1"
|
||||
{.deprecated: [TResponse: Response, PProxy: Proxy,
|
||||
EInvalidProtocol: ProtocolError, EHttpRequestErr: HttpRequestError
|
||||
].}
|
||||
|
||||
const defUserAgent* = "Nim httpclient/0.1"
|
||||
|
||||
proc httpError(msg: string) =
|
||||
var e: ref EInvalidProtocol
|
||||
var e: ref ProtocolError
|
||||
new(e)
|
||||
e.msg = msg
|
||||
raise e
|
||||
|
||||
proc fileError(msg: string) =
|
||||
var e: ref EIO
|
||||
var e: ref IOError
|
||||
new(e)
|
||||
e.msg = msg
|
||||
raise e
|
||||
@@ -232,7 +236,7 @@ proc parseResponse(s: TSocket, getBody: bool, timeout: int): TResponse =
|
||||
result.body = ""
|
||||
|
||||
type
|
||||
THttpMethod* = enum ## the requested HttpMethod
|
||||
HttpMethod* = enum ## the requested HttpMethod
|
||||
httpHEAD, ## Asks for the response identical to the one that would
|
||||
## correspond to a GET request, but without the response
|
||||
## body.
|
||||
@@ -250,6 +254,8 @@ type
|
||||
httpCONNECT ## Converts the request connection to a transparent
|
||||
## TCP/IP tunnel, usually used for proxies.
|
||||
|
||||
{.deprecated: [THttpMethod: HttpMethod].}
|
||||
|
||||
when not defined(ssl):
|
||||
type PSSLContext = ref object
|
||||
let defaultSSLContext: PSSLContext = nil
|
||||
@@ -288,7 +294,7 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
|
||||
add(headers, "\c\L")
|
||||
|
||||
var s = socket()
|
||||
if s == InvalidSocket: osError(osLastError())
|
||||
if s == InvalidSocket: raiseOSError(osLastError())
|
||||
var port = sockets.TPort(80)
|
||||
if r.scheme == "https":
|
||||
when defined(ssl):
|
||||
@@ -431,16 +437,18 @@ proc generateHeaders(r: TURL, httpMethod: THttpMethod,
|
||||
add(result, "\c\L")
|
||||
|
||||
type
|
||||
PAsyncHttpClient* = ref object
|
||||
socket: PAsyncSocket
|
||||
AsyncHttpClient* = ref object
|
||||
socket: AsyncSocket
|
||||
connected: bool
|
||||
currentURL: TURL ## Where we are currently connected.
|
||||
headers: PStringTable
|
||||
headers: StringTableRef
|
||||
maxRedirects: int
|
||||
userAgent: string
|
||||
|
||||
{.deprecated: [PAsyncHttpClient: AsyncHttpClient].}
|
||||
|
||||
proc newAsyncHttpClient*(userAgent = defUserAgent,
|
||||
maxRedirects = 5): PAsyncHttpClient =
|
||||
maxRedirects = 5): AsyncHttpClient =
|
||||
## Creates a new PAsyncHttpClient instance.
|
||||
##
|
||||
## ``userAgent`` specifies the user agent that will be used when making
|
||||
@@ -453,7 +461,7 @@ proc newAsyncHttpClient*(userAgent = defUserAgent,
|
||||
result.userAgent = defUserAgent
|
||||
result.maxRedirects = maxRedirects
|
||||
|
||||
proc close*(client: PAsyncHttpClient) =
|
||||
proc close*(client: AsyncHttpClient) =
|
||||
## Closes any connections held by the HTTP client.
|
||||
if client.connected:
|
||||
client.socket.close()
|
||||
|
||||
@@ -145,7 +145,7 @@ when false:
|
||||
var buf = alloc(contentLength)
|
||||
if recv(client, buf, contentLength) != contentLength:
|
||||
dealloc(buf)
|
||||
OSError()
|
||||
raiseOSError()
|
||||
var inp = process.inputStream
|
||||
inp.writeData(buf, contentLength)
|
||||
dealloc(buf)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf, Dominik Picheta
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2014 Andreas Rumpf, Dominik Picheta
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
@@ -35,7 +35,7 @@ import
|
||||
hashes, strutils, lexbase, streams, unicode
|
||||
|
||||
type
|
||||
TJsonEventKind* = enum ## enumeration of all events that may occur when parsing
|
||||
JsonEventKind* = enum ## enumeration of all events that may occur when parsing
|
||||
jsonError, ## an error ocurred during parsing
|
||||
jsonEof, ## end of file reached
|
||||
jsonString, ## a string literal
|
||||
@@ -65,7 +65,7 @@ type
|
||||
tkColon,
|
||||
tkComma
|
||||
|
||||
TJsonError* = enum ## enumeration that lists all errors that can occur
|
||||
JsonError* = enum ## enumeration that lists all errors that can occur
|
||||
errNone, ## no error
|
||||
errInvalidToken, ## invalid token
|
||||
errStringExpected, ## string expected
|
||||
@@ -78,20 +78,23 @@ type
|
||||
errEofExpected, ## EOF expected
|
||||
errExprExpected ## expr expected
|
||||
|
||||
TParserState = enum
|
||||
ParserState = enum
|
||||
stateEof, stateStart, stateObject, stateArray, stateExpectArrayComma,
|
||||
stateExpectObjectComma, stateExpectColon, stateExpectValue
|
||||
|
||||
TJsonParser* = object of TBaseLexer ## the parser object.
|
||||
JsonParser* = object of TBaseLexer ## the parser object.
|
||||
a: string
|
||||
tok: TTokKind
|
||||
kind: TJsonEventKind
|
||||
err: TJsonError
|
||||
state: seq[TParserState]
|
||||
kind: JsonEventKind
|
||||
err: JsonError
|
||||
state: seq[ParserState]
|
||||
filename: string
|
||||
|
||||
{.deprecated: [TJsonEventKind: JsonEventKind, TJsonError: JsonError,
|
||||
TJsonParser: JsonParser].}
|
||||
|
||||
const
|
||||
errorMessages: array [TJsonError, string] = [
|
||||
errorMessages: array [JsonError, string] = [
|
||||
"no error",
|
||||
"invalid token",
|
||||
"string expected",
|
||||
@@ -116,7 +119,7 @@ const
|
||||
"{", "}", "[", "]", ":", ","
|
||||
]
|
||||
|
||||
proc open*(my: var TJsonParser, input: PStream, filename: string) =
|
||||
proc open*(my: var JsonParser, input: PStream, filename: string) =
|
||||
## initializes the parser with an input stream. `Filename` is only used
|
||||
## for nice error messages.
|
||||
lexbase.open(my, input)
|
||||
@@ -125,49 +128,49 @@ proc open*(my: var TJsonParser, input: PStream, filename: string) =
|
||||
my.kind = jsonError
|
||||
my.a = ""
|
||||
|
||||
proc close*(my: var TJsonParser) {.inline.} =
|
||||
proc close*(my: var JsonParser) {.inline.} =
|
||||
## closes the parser `my` and its associated input stream.
|
||||
lexbase.close(my)
|
||||
|
||||
proc str*(my: TJsonParser): string {.inline.} =
|
||||
proc str*(my: JsonParser): string {.inline.} =
|
||||
## returns the character data for the events: ``jsonInt``, ``jsonFloat``,
|
||||
## ``jsonString``
|
||||
assert(my.kind in {jsonInt, jsonFloat, jsonString})
|
||||
return my.a
|
||||
|
||||
proc getInt*(my: TJsonParser): BiggestInt {.inline.} =
|
||||
proc getInt*(my: JsonParser): BiggestInt {.inline.} =
|
||||
## returns the number for the event: ``jsonInt``
|
||||
assert(my.kind == jsonInt)
|
||||
return parseBiggestInt(my.a)
|
||||
|
||||
proc getFloat*(my: TJsonParser): float {.inline.} =
|
||||
proc getFloat*(my: JsonParser): float {.inline.} =
|
||||
## returns the number for the event: ``jsonFloat``
|
||||
assert(my.kind == jsonFloat)
|
||||
return parseFloat(my.a)
|
||||
|
||||
proc kind*(my: TJsonParser): TJsonEventKind {.inline.} =
|
||||
proc kind*(my: JsonParser): JsonEventKind {.inline.} =
|
||||
## returns the current event type for the JSON parser
|
||||
return my.kind
|
||||
|
||||
proc getColumn*(my: TJsonParser): int {.inline.} =
|
||||
proc getColumn*(my: JsonParser): int {.inline.} =
|
||||
## get the current column the parser has arrived at.
|
||||
result = getColNumber(my, my.bufpos)
|
||||
|
||||
proc getLine*(my: TJsonParser): int {.inline.} =
|
||||
proc getLine*(my: JsonParser): int {.inline.} =
|
||||
## get the current line the parser has arrived at.
|
||||
result = my.lineNumber
|
||||
|
||||
proc getFilename*(my: TJsonParser): string {.inline.} =
|
||||
proc getFilename*(my: JsonParser): string {.inline.} =
|
||||
## get the filename of the file that the parser processes.
|
||||
result = my.filename
|
||||
|
||||
proc errorMsg*(my: TJsonParser): string =
|
||||
proc errorMsg*(my: JsonParser): string =
|
||||
## returns a helpful error message for the event ``jsonError``
|
||||
assert(my.kind == jsonError)
|
||||
result = "$1($2, $3) Error: $4" % [
|
||||
my.filename, $getLine(my), $getColumn(my), errorMessages[my.err]]
|
||||
|
||||
proc errorMsgExpected*(my: TJsonParser, e: string): string =
|
||||
proc errorMsgExpected*(my: JsonParser, e: string): string =
|
||||
## returns an error message "`e` expected" in the same format as the
|
||||
## other error messages
|
||||
result = "$1($2, $3) Error: $4" % [
|
||||
@@ -181,7 +184,7 @@ proc handleHexChar(c: char, x: var int): bool =
|
||||
of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
|
||||
else: result = false # error
|
||||
|
||||
proc parseString(my: var TJsonParser): TTokKind =
|
||||
proc parseString(my: var JsonParser): TTokKind =
|
||||
result = tkString
|
||||
var pos = my.bufpos + 1
|
||||
var buf = my.buf
|
||||
@@ -239,7 +242,7 @@ proc parseString(my: var TJsonParser): TTokKind =
|
||||
inc(pos)
|
||||
my.bufpos = pos # store back
|
||||
|
||||
proc skip(my: var TJsonParser) =
|
||||
proc skip(my: var JsonParser) =
|
||||
var pos = my.bufpos
|
||||
var buf = my.buf
|
||||
while true:
|
||||
@@ -297,7 +300,7 @@ proc skip(my: var TJsonParser) =
|
||||
break
|
||||
my.bufpos = pos
|
||||
|
||||
proc parseNumber(my: var TJsonParser) =
|
||||
proc parseNumber(my: var JsonParser) =
|
||||
var pos = my.bufpos
|
||||
var buf = my.buf
|
||||
if buf[pos] == '-':
|
||||
@@ -328,7 +331,7 @@ proc parseNumber(my: var TJsonParser) =
|
||||
inc(pos)
|
||||
my.bufpos = pos
|
||||
|
||||
proc parseName(my: var TJsonParser) =
|
||||
proc parseName(my: var JsonParser) =
|
||||
var pos = my.bufpos
|
||||
var buf = my.buf
|
||||
if buf[pos] in IdentStartChars:
|
||||
@@ -337,7 +340,7 @@ proc parseName(my: var TJsonParser) =
|
||||
inc(pos)
|
||||
my.bufpos = pos
|
||||
|
||||
proc getTok(my: var TJsonParser): TTokKind =
|
||||
proc getTok(my: var JsonParser): TTokKind =
|
||||
setLen(my.a, 0)
|
||||
skip(my) # skip whitespace, comments
|
||||
case my.buf[my.bufpos]
|
||||
@@ -381,7 +384,7 @@ proc getTok(my: var TJsonParser): TTokKind =
|
||||
result = tkError
|
||||
my.tok = result
|
||||
|
||||
proc next*(my: var TJsonParser) =
|
||||
proc next*(my: var JsonParser) =
|
||||
## retrieves the first/next event. This controls the parser.
|
||||
var tk = getTok(my)
|
||||
var i = my.state.len-1
|
||||
@@ -529,7 +532,10 @@ type
|
||||
of JArray:
|
||||
elems*: seq[PJsonNode]
|
||||
|
||||
EJsonParsingError* = object of ValueError ## is raised for a JSON error
|
||||
JsonParsingError* = object of ValueError ## is raised for a JSON error
|
||||
|
||||
{.deprecated: [EJsonParsingError: JsonParsingError, TJsonNode: JsonNodeObj,
|
||||
PJsonNode: JsonNode, TJsonNodeKind: JsonNodeKind].}
|
||||
|
||||
proc raiseParseErr*(p: TJsonParser, msg: string) {.noinline, noreturn.} =
|
||||
## raises an `EJsonParsingError` exception.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# The Nimrod Compiler
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2009 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -36,6 +36,8 @@ type
|
||||
lineStart: int # index of last line start in buffer
|
||||
fileOpened: bool
|
||||
|
||||
{.deprecated: [TBaseLexer: BaseLexer].}
|
||||
|
||||
proc open*(L: var TBaseLexer, input: PStream, bufLen: int = 8192)
|
||||
## inits the TBaseLexer with a stream to read from
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2014 Andreas Rumpf, Dominik Picheta
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -26,7 +26,7 @@
|
||||
## The following example demonstrates logging to three different handlers
|
||||
## simultaneously:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## var L = newConsoleLogger()
|
||||
## var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
|
||||
@@ -42,7 +42,7 @@
|
||||
import strutils, os, times
|
||||
|
||||
type
|
||||
TLevel* = enum ## logging level
|
||||
Level* = enum ## logging level
|
||||
lvlAll, ## all levels active
|
||||
lvlDebug, ## debug level (and any above) active
|
||||
lvlInfo, ## info level (and any above) active
|
||||
@@ -52,7 +52,7 @@ type
|
||||
lvlNone ## no levels active
|
||||
|
||||
const
|
||||
LevelNames*: array [TLevel, string] = [
|
||||
LevelNames*: array [Level, string] = [
|
||||
"DEBUG", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "NONE"
|
||||
]
|
||||
|
||||
@@ -60,26 +60,29 @@ const
|
||||
verboseFmtStr* = "$date $time "
|
||||
|
||||
type
|
||||
PLogger* = ref object of PObject ## abstract logger; the base type of all loggers
|
||||
levelThreshold*: TLevel ## only messages of level >= levelThreshold
|
||||
Logger* = ref object of RootObj ## abstract logger; the base type of all loggers
|
||||
levelThreshold*: Leve l ## only messages of level >= levelThreshold
|
||||
## should be processed
|
||||
fmtStr: string ## = defaultFmtStr by default, see substituteLog for $date etc.
|
||||
|
||||
PConsoleLogger* = ref object of PLogger ## logger that writes the messages to the
|
||||
## console
|
||||
ConsoleLogger* = ref object of Logger ## logger that writes the messages to the
|
||||
## console
|
||||
|
||||
PFileLogger* = ref object of PLogger ## logger that writes the messages to a file
|
||||
FileLogger* = ref object of Logger ## logger that writes the messages to a file
|
||||
f: TFile
|
||||
|
||||
PRollingFileLogger* = ref object of PFileLogger ## logger that writes the
|
||||
## messages to a file and
|
||||
## performs log rotation
|
||||
RollingFileLogger* = ref object of FileLogger ## logger that writes the
|
||||
## messages to a file and
|
||||
## performs log rotation
|
||||
maxLines: int # maximum number of lines
|
||||
curLine : int
|
||||
baseName: string # initial filename
|
||||
baseMode: TFileMode # initial file mode
|
||||
baseMode: FileMode # initial file mode
|
||||
logFiles: int # how many log files already created, e.g. basename.1, basename.2...
|
||||
|
||||
{.deprecated: [TLevel: Level, PLogger: Logger, PConsoleLogger: ConsoleLogger,
|
||||
PFileLogger: FileLogger, PRollingFileLogger: RollingFileLogger].}
|
||||
|
||||
proc substituteLog(frmt: string): string =
|
||||
## converts $date to the current date
|
||||
## converts $time to the current time
|
||||
@@ -105,7 +108,7 @@ proc substituteLog(frmt: string): string =
|
||||
of "appdir": result.add(app.splitFile.dir)
|
||||
of "appname": result.add(app.splitFile.name)
|
||||
|
||||
method log*(logger: PLogger, level: TLevel,
|
||||
method log*(logger: Logger, level: Level,
|
||||
frmt: string, args: varargs[string, `$`]) {.raises: [EBase], tags: [FTime, FWriteIO, FReadIO].} =
|
||||
## Override this method in custom loggers. Default implementation does
|
||||
## nothing.
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module contains procs for serialization and deseralization of
|
||||
## arbitrary Nimrod data structures. The serialization format uses JSON.
|
||||
## arbitrary Nim data structures. The serialization format uses JSON.
|
||||
##
|
||||
## **Restriction**: For objects their type is **not** serialized. This means
|
||||
## essentially that it does not work if the object has some other runtime
|
||||
## type than its compiletime type:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## type
|
||||
## TA = object
|
||||
@@ -211,7 +211,7 @@ proc loadAny(p: var TJsonParser, a: TAny, t: var TTable[biggestInt, pointer]) =
|
||||
raiseParseErr(p, "float expected")
|
||||
of akRange: loadAny(p, a.skipRange, t)
|
||||
|
||||
proc loadAny(s: PStream, a: TAny, t: var TTable[biggestInt, pointer]) =
|
||||
proc loadAny(s: PStream, a: TAny, t: var TTable[BiggestInt, pointer]) =
|
||||
var p: TJsonParser
|
||||
open(p, s, "unknown file")
|
||||
next(p)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
@@ -44,7 +44,7 @@ proc validEmailAddress*(s: string): bool {.noSideEffect,
|
||||
"aero", "jobs", "museum": return true
|
||||
return false
|
||||
|
||||
proc parseInt*(s: string, value: var int, validRange: TSlice[int]) {.
|
||||
proc parseInt*(s: string, value: var int, validRange: Slice[int]) {.
|
||||
noSideEffect, rtl, extern: "nmatchParseInt".} =
|
||||
## parses `s` into an integer in the range `validRange`. If successful,
|
||||
## `value` is modified to contain the result. Otherwise no exception is
|
||||
@@ -53,7 +53,7 @@ proc parseInt*(s: string, value: var int, validRange: TSlice[int]) {.
|
||||
var x = value
|
||||
try:
|
||||
discard parseutils.parseInt(s, x, 0)
|
||||
except EOverflow:
|
||||
except OverflowError:
|
||||
discard
|
||||
if x in validRange: value = x
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
## Constructive mathematics is naturally typed. -- Simon Thompson
|
||||
##
|
||||
## Basic math routines for Nimrod.
|
||||
## Basic math routines for Nim.
|
||||
## This module is available for the `JavaScript target
|
||||
## <backends.html#the-javascript-target>`_.
|
||||
|
||||
@@ -41,8 +41,8 @@ const
|
||||
## for Nimrod's ``float`` type.
|
||||
|
||||
type
|
||||
TFloatClass* = enum ## describes the class a floating point value belongs to.
|
||||
## This is the type that is returned by `classify`.
|
||||
FloatClass* = enum ## describes the class a floating point value belongs to.
|
||||
## This is the type that is returned by `classify`.
|
||||
fcNormal, ## value is an ordinary nonzero floating point value
|
||||
fcSubnormal, ## value is a subnormal (a very small) floating point value
|
||||
fcZero, ## value is zero
|
||||
@@ -51,10 +51,10 @@ type
|
||||
fcInf, ## value is positive infinity
|
||||
fcNegInf ## value is negative infinity
|
||||
|
||||
proc classify*(x: float): TFloatClass =
|
||||
proc classify*(x: float): FloatClass =
|
||||
## classifies a floating point value. Returns `x`'s class as specified by
|
||||
## `TFloatClass`.
|
||||
|
||||
## `FloatClass`.
|
||||
|
||||
# JavaScript and most C compilers have no classify:
|
||||
if x == 0.0:
|
||||
if 1.0/x == Inf:
|
||||
@@ -287,12 +287,14 @@ proc random[T](a: openArray[T]): T =
|
||||
result = a[random(a.low..a.len)]
|
||||
|
||||
type
|
||||
TRunningStat* {.pure,final.} = object ## an accumulator for statistical data
|
||||
n*: int ## number of pushed data
|
||||
sum*, min*, max*, mean*: float ## self-explaining
|
||||
RunningStat* = object ## an accumulator for statistical data
|
||||
n*: int ## number of pushed data
|
||||
sum*, min*, max*, mean*: float ## self-explaining
|
||||
oldM, oldS, newS: float
|
||||
|
||||
proc push*(s: var TRunningStat, x: float) =
|
||||
{.deprecated: [TFloatClass: FloatClass, TRunningStat: RunningStat].}
|
||||
|
||||
proc push*(s: var RunningStat, x: float) =
|
||||
## pushes a value `x` for processing
|
||||
inc(s.n)
|
||||
# See Knuth TAOCP vol 2, 3rd edition, page 232
|
||||
@@ -313,16 +315,16 @@ proc push*(s: var TRunningStat, x: float) =
|
||||
s.oldS = s.newS
|
||||
s.sum = s.sum + x
|
||||
|
||||
proc push*(s: var TRunningStat, x: int) =
|
||||
proc push*(s: var RunningStat, x: int) =
|
||||
## pushes a value `x` for processing. `x` is simply converted to ``float``
|
||||
## and the other push operation is called.
|
||||
push(s, toFloat(x))
|
||||
|
||||
proc variance*(s: TRunningStat): float =
|
||||
proc variance*(s: RunningStat): float =
|
||||
## computes the current variance of `s`
|
||||
if s.n > 1: result = s.newS / (toFloat(s.n - 1))
|
||||
|
||||
proc standardDeviation*(s: TRunningStat): float =
|
||||
proc standardDeviation*(s: RunningStat): float =
|
||||
## computes the current standard deviation of `s`
|
||||
result = sqrt(variance(s))
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2010 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2014 Nimrod Contributors
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -22,7 +22,7 @@ else:
|
||||
import os
|
||||
|
||||
type
|
||||
TMemFile* = object {.pure.} ## represents a memory mapped file
|
||||
MemFile* = object ## represents a memory mapped file
|
||||
mem*: pointer ## a pointer to the memory mapped file. The pointer
|
||||
## can be used directly to change the contents of the
|
||||
## file, if it was opened with write access.
|
||||
@@ -34,8 +34,9 @@ type
|
||||
else:
|
||||
handle: cint
|
||||
|
||||
{.deprecated: [TMemFile: MemFile].}
|
||||
|
||||
proc mapMem*(m: var TMemFile, mode: FileMode = fmRead,
|
||||
proc mapMem*(m: var MemFile, mode: FileMode = fmRead,
|
||||
mappedSize = -1, offset = 0): pointer =
|
||||
var readonly = mode == fmRead
|
||||
when defined(windows):
|
||||
@@ -60,27 +61,27 @@ proc mapMem*(m: var TMemFile, mode: FileMode = fmRead,
|
||||
osError(osLastError())
|
||||
|
||||
|
||||
proc unmapMem*(f: var TMemFile, p: pointer, size: int) =
|
||||
proc unmapMem*(f: var MemFile, p: pointer, size: int) =
|
||||
## unmaps the memory region ``(p, <p+size)`` of the mapped file `f`.
|
||||
## All changes are written back to the file system, if `f` was opened
|
||||
## with write access. ``size`` must be of exactly the size that was requested
|
||||
## via ``mapMem``.
|
||||
when defined(windows):
|
||||
if unmapViewOfFile(p) == 0: osError(osLastError())
|
||||
if unmapViewOfFile(p) == 0: raiseOSError(osLastError())
|
||||
else:
|
||||
if munmap(p, size) != 0: osError(osLastError())
|
||||
if munmap(p, size) != 0: raiseOSError(osLastError())
|
||||
|
||||
|
||||
proc open*(filename: string, mode: FileMode = fmRead,
|
||||
mappedSize = -1, offset = 0, newFileSize = -1): TMemFile =
|
||||
mappedSize = -1, offset = 0, newFileSize = -1): MemFile =
|
||||
## opens a memory mapped file. If this fails, ``EOS`` is raised.
|
||||
## `newFileSize` can only be set if the file does not exist and is opened
|
||||
## with write access (e.g., with fmReadWrite). `mappedSize` and `offset`
|
||||
## can be used to map only a slice of the file. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## var
|
||||
## mm, mm_full, mm_half: TMemFile
|
||||
## mm, mm_full, mm_half: MemFile
|
||||
##
|
||||
## mm = memfiles.open("/tmp/test.mmap", mode = fmWrite, newFileSize = 1024) # Create a new file
|
||||
## mm.close()
|
||||
@@ -100,11 +101,11 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||
result.size = 0
|
||||
|
||||
when defined(windows):
|
||||
template fail(errCode: TOSErrorCode, msg: expr) =
|
||||
template fail(errCode: OSErrorCode, msg: expr) =
|
||||
rollback()
|
||||
if result.fHandle != 0: discard closeHandle(result.fHandle)
|
||||
if result.mapHandle != 0: discard closeHandle(result.mapHandle)
|
||||
osError(errCode)
|
||||
raiseOSError(errCode)
|
||||
# return false
|
||||
#raise newException(EIO, msg)
|
||||
|
||||
@@ -169,10 +170,10 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||
else: result.size = fileSize.int
|
||||
|
||||
else:
|
||||
template fail(errCode: TOSErrorCode, msg: expr) =
|
||||
template fail(errCode: OSErrorCode, msg: expr) =
|
||||
rollback()
|
||||
if result.handle != 0: discard close(result.handle)
|
||||
osError(errCode)
|
||||
raiseOSError(errCode)
|
||||
|
||||
var flags = if readonly: O_RDONLY else: O_RDWR
|
||||
|
||||
@@ -214,12 +215,12 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||
if result.mem == cast[pointer](MAP_FAILED):
|
||||
fail(osLastError(), "file mapping failed")
|
||||
|
||||
proc close*(f: var TMemFile) =
|
||||
proc close*(f: var MemFile) =
|
||||
## closes the memory mapped file `f`. All changes are written back to the
|
||||
## file system, if `f` was opened with write access.
|
||||
|
||||
var error = false
|
||||
var lastErr: TOSErrorCode
|
||||
var lastErr: OSErrorCode
|
||||
|
||||
when defined(windows):
|
||||
if f.fHandle != INVALID_HANDLE_VALUE:
|
||||
@@ -242,5 +243,5 @@ proc close*(f: var TMemFile) =
|
||||
else:
|
||||
f.handle = 0
|
||||
|
||||
if error: osError(lastErr)
|
||||
if error: raiseOSError(lastErr)
|
||||
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
import unsigned
|
||||
|
||||
type
|
||||
TMersenneTwister* = object
|
||||
MersenneTwister* = object
|
||||
mt: array[0..623, uint32]
|
||||
index: int
|
||||
|
||||
proc newMersenneTwister*(seed: int): TMersenneTwister =
|
||||
{.deprecated: [TMersenneTwister: MersenneTwister].}
|
||||
|
||||
proc newMersenneTwister*(seed: int): MersenneTwister =
|
||||
result.index = 0
|
||||
result.mt[0]= uint32(seed)
|
||||
for i in 1..623'u32:
|
||||
result.mt[i]= (0x6c078965'u32 * (result.mt[i-1] xor (result.mt[i-1] shr 30'u32)) + i)
|
||||
|
||||
proc generateNumbers(m: var TMersenneTwister) =
|
||||
proc generateNumbers(m: var MersenneTwister) =
|
||||
for i in 0..623:
|
||||
var y = (m.mt[i] and 0x80000000'u32) + (m.mt[(i+1) mod 624] and 0x7fffffff'u32)
|
||||
m.mt[i] = m.mt[(i+397) mod 624] xor uint32(y shr 1'u32)
|
||||
if (y mod 2'u32) != 0:
|
||||
m.mt[i] = m.mt[i] xor 0x9908b0df'u32
|
||||
|
||||
proc getNum*(m: var TMersenneTwister): int =
|
||||
proc getNum*(m: var MersenneTwister): int =
|
||||
if m.index == 0:
|
||||
generateNumbers(m)
|
||||
var y = m.mt[m.index]
|
||||
@@ -29,11 +31,9 @@ proc getNum*(m: var TMersenneTwister): int =
|
||||
m.index = (m.index+1) mod 624
|
||||
return int(y)
|
||||
|
||||
|
||||
|
||||
# Test
|
||||
when isMainModule:
|
||||
var mt = newMersenneTwister(2525)
|
||||
|
||||
for i in 0..99:
|
||||
echo mt.getNum
|
||||
echo mt.getNum
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Dominik Picheta
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -10,8 +10,10 @@
|
||||
## This module implements a mimetypes database
|
||||
import strtabs
|
||||
type
|
||||
TMimeDB* = object
|
||||
mimes: PStringTable
|
||||
MimeDB* = object
|
||||
mimes: StringTableRef
|
||||
|
||||
{.deprecated: [TMimeDB: MimeDB].}
|
||||
|
||||
const mimes* = {
|
||||
"ez": "application/andrew-inset",
|
||||
@@ -489,20 +491,19 @@ const mimes* = {
|
||||
"vrml": "x-world/x-vrml",
|
||||
"wrl": "x-world/x-vrml"}
|
||||
|
||||
proc newMimetypes*(): TMimeDB =
|
||||
proc newMimetypes*(): MimeDB =
|
||||
## Creates a new Mimetypes database. The database will contain the most
|
||||
## common mimetypes.
|
||||
|
||||
result.mimes = mimes.newStringTable()
|
||||
|
||||
proc getMimetype*(mimedb: TMimeDB, ext: string, default = "text/plain"): string =
|
||||
proc getMimetype*(mimedb: MimeDB, ext: string, default = "text/plain"): string =
|
||||
## Gets mimetype which corresponds to ``ext``. Returns ``default`` if ``ext``
|
||||
## could not be found.
|
||||
result = mimedb.mimes[ext]
|
||||
if result == "":
|
||||
return default
|
||||
|
||||
proc getExt*(mimedb: TMimeDB, mimetype: string, default = "txt"): string =
|
||||
proc getExt*(mimedb: MimeDB, mimetype: string, default = "txt"): string =
|
||||
## Gets extension which corresponds to ``mimetype``. Returns ``default`` if
|
||||
## ``mimetype`` could not be found. Extensions are returned without the
|
||||
## leading dot.
|
||||
@@ -511,11 +512,11 @@ proc getExt*(mimedb: TMimeDB, mimetype: string, default = "txt"): string =
|
||||
if m == mimetype:
|
||||
result = e
|
||||
|
||||
proc register*(mimedb: var TMimeDB, ext: string, mimetype: string) =
|
||||
proc register*(mimedb: var MimeDB, ext: string, mimetype: string) =
|
||||
## Adds ``mimetype`` to the ``mimedb``.
|
||||
mimedb.mimes[ext] = mimetype
|
||||
|
||||
when isMainModule:
|
||||
var m = newMimetypes()
|
||||
echo m.getMimetype("mp4")
|
||||
echo m.getExt("text/html")
|
||||
echo m.getExt("text/html")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2014 Dominik Picheta
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
{.deadCodeElim: on.}
|
||||
import rawsockets, os, strutils, unsigned, parseutils, times
|
||||
export TPort, `$`, `==`
|
||||
export Port, `$`, `==`
|
||||
|
||||
const useWinVersion = defined(Windows) or defined(nimdoc)
|
||||
|
||||
@@ -22,25 +22,25 @@ when defined(ssl):
|
||||
|
||||
when defined(ssl):
|
||||
type
|
||||
ESSL* = object of ESynch
|
||||
SSLError* = object of Exception
|
||||
|
||||
TSSLCVerifyMode* = enum
|
||||
SSLCVerifyMode* = enum
|
||||
CVerifyNone, CVerifyPeer
|
||||
|
||||
TSSLProtVersion* = enum
|
||||
SSLProtVersion* = enum
|
||||
protSSLv2, protSSLv3, protTLSv1, protSSLv23
|
||||
|
||||
PSSLContext* = distinct PSSLCTX
|
||||
SSLContext* = distinct PSSLCTX
|
||||
|
||||
TSSLAcceptResult* = enum
|
||||
SSLAcceptResult* = enum
|
||||
AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess
|
||||
|
||||
const
|
||||
BufferSize*: int = 4000 ## size of a buffered socket's buffer
|
||||
|
||||
type
|
||||
TSocketImpl* = object ## socket type
|
||||
fd*: TSocketHandle
|
||||
SocketImpl* = object ## socket type
|
||||
fd*: SocketHandle
|
||||
case isBuffered*: bool # determines whether this socket is buffered.
|
||||
of true:
|
||||
buffer*: array[0..BufferSize, char]
|
||||
@@ -57,34 +57,40 @@ type
|
||||
sslPeekChar*: char
|
||||
of false: nil
|
||||
|
||||
PSocket* = ref TSocketImpl
|
||||
Socket* = ref SocketImpl
|
||||
|
||||
TSOBool* = enum ## Boolean socket options.
|
||||
SOBool* = enum ## Boolean socket options.
|
||||
OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive,
|
||||
OptOOBInline, OptReuseAddr
|
||||
|
||||
TReadLineResult* = enum ## result for readLineAsync
|
||||
ReadLineResult* = enum ## result for readLineAsync
|
||||
ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone
|
||||
|
||||
ETimeout* = object of ESynch
|
||||
TimeoutError* = object of Exception
|
||||
|
||||
TSocketFlags* {.pure.} = enum
|
||||
SocketFlag* {.pure.} = enum
|
||||
Peek,
|
||||
SafeDisconn ## Ensures disconnection exceptions (ECONNRESET, EPIPE etc) are not thrown.
|
||||
|
||||
proc isDisconnectionError*(flags: set[TSocketFlags],
|
||||
lastError: TOSErrorCode): bool =
|
||||
{.deprecated: [TSocketFlags: SocketFlag, ETimeout: TimeoutError,
|
||||
TReadLineResult: ReadLineResult, TSOBool: SOBool, PSocket: Socket,
|
||||
TSocketImpl: SocketImpl, ESSL: SSLError, TSSLCVerifyMode: SSLCVerifyMode,
|
||||
TSSLProtVersion: SSLProtVersion, PSSLContext: SSLContext,
|
||||
TSSLAcceptResult: SSLAcceptResult].}
|
||||
|
||||
proc isDisconnectionError*(flags: set[SocketFlag],
|
||||
lastError: OSErrorCode): bool =
|
||||
## Determines whether ``lastError`` is a disconnection error. Only does this
|
||||
## if flags contains ``SafeDisconn``.
|
||||
when useWinVersion:
|
||||
TSocketFlags.SafeDisconn in flags and
|
||||
SocketFlag.SafeDisconn in flags and
|
||||
lastError.int32 in {WSAECONNRESET, WSAECONNABORTED, WSAENETRESET,
|
||||
WSAEDISCON, ERROR_NETNAME_DELETED}
|
||||
else:
|
||||
TSocketFlags.SafeDisconn in flags and
|
||||
SocketFlag.SafeDisconn in flags and
|
||||
lastError.int32 in {ECONNRESET, EPIPE, ENETRESET}
|
||||
|
||||
proc toOSFlags*(socketFlags: set[TSocketFlags]): cint =
|
||||
proc toOSFlags*(socketFlags: set[SocketFlag]): cint =
|
||||
## Converts the flags into the underlying OS representation.
|
||||
for f in socketFlags:
|
||||
case f
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Profiling support for Nimrod. This is an embedded profiler that requires
|
||||
## Profiling support for Nim. This is an embedded profiler that requires
|
||||
## ``--profiler:on``. You only need to import this module to get a profiling
|
||||
## report at program exit.
|
||||
|
||||
@@ -64,7 +64,7 @@ when withThreads:
|
||||
var
|
||||
profilingLock: TLock
|
||||
|
||||
InitLock profilingLock
|
||||
initLock profilingLock
|
||||
|
||||
proc hookAux(st: TStackTrace, costs: int) =
|
||||
# this is quite performance sensitive!
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2013 Robert Persson
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
type OneVarFunction* = proc (x: float): float
|
||||
|
||||
type TOneVarFunction* =proc (x:float):float
|
||||
{.deprecated: [TOneVarFunction: OneVarFunction].}
|
||||
|
||||
proc brent*(xmin,xmax:float ,function:TOneVarFunction, tol:float,maxiter=1000):
|
||||
proc brent*(xmin,xmax:float, function:OneVarFunction, tol:float,maxiter=1000):
|
||||
tuple[rootx, rooty: float, success: bool]=
|
||||
## Searches `function` for a root between `xmin` and `xmax`
|
||||
## using brents method. If the function value at `xmin`and `xmax` has the
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Nimrod OID support. An OID is a global ID that consists of a timestamp,
|
||||
## Nim OID support. An OID is a global ID that consists of a timestamp,
|
||||
## a unique counter and a random value. This combination should suffice to
|
||||
## produce a globally distributed unique ID. This implementation was extracted
|
||||
## from the Mongodb interface and it thus binary compatible with a Mongo OID.
|
||||
@@ -18,11 +18,13 @@
|
||||
import times, endians
|
||||
|
||||
type
|
||||
Toid* {.pure, final.} = object ## an OID
|
||||
Oid* = object ## an OID
|
||||
time: int32 ##
|
||||
fuzz: int32 ##
|
||||
count: int32 ##
|
||||
|
||||
{.deprecated: [Toid: Oid].}
|
||||
|
||||
proc hexbyte*(hex: char): int =
|
||||
case hex
|
||||
of '0'..'9': result = (ord(hex) - ord('0'))
|
||||
@@ -30,7 +32,7 @@ proc hexbyte*(hex: char): int =
|
||||
of 'A'..'F': result = (ord(hex) - ord('A') + 10)
|
||||
else: discard
|
||||
|
||||
proc parseOid*(str: cstring): TOid =
|
||||
proc parseOid*(str: cstring): Oid =
|
||||
## parses an OID.
|
||||
var bytes = cast[cstring](addr(result.time))
|
||||
var i = 0
|
||||
@@ -38,7 +40,7 @@ proc parseOid*(str: cstring): TOid =
|
||||
bytes[i] = chr((hexbyte(str[2 * i]) shl 4) or hexbyte(str[2 * i + 1]))
|
||||
inc(i)
|
||||
|
||||
proc oidToString*(oid: TOid, str: cstring) =
|
||||
proc oidToString*(oid: Oid, str: cstring) =
|
||||
const hex = "0123456789abcdef"
|
||||
# work around a compiler bug:
|
||||
var str = str
|
||||
@@ -52,7 +54,7 @@ proc oidToString*(oid: TOid, str: cstring) =
|
||||
inc(i)
|
||||
str[24] = '\0'
|
||||
|
||||
proc `$`*(oid: TOid): string =
|
||||
proc `$`*(oid: Oid): string =
|
||||
result = newString(25)
|
||||
oidToString(oid, result)
|
||||
|
||||
@@ -60,7 +62,7 @@ var
|
||||
incr: int
|
||||
fuzz: int32
|
||||
|
||||
proc genOid*(): TOid =
|
||||
proc genOid*(): Oid =
|
||||
## generates a new OID.
|
||||
proc rand(): cint {.importc: "rand", header: "<stdlib.h>", nodecl.}
|
||||
proc gettime(dummy: ptr cint): cint {.importc: "time", header: "<time.h>".}
|
||||
@@ -79,12 +81,12 @@ proc genOid*(): TOid =
|
||||
result.fuzz = fuzz
|
||||
bigEndian32(addr result.count, addr(i))
|
||||
|
||||
proc generatedTime*(oid: TOid): TTime =
|
||||
proc generatedTime*(oid: Oid): Time =
|
||||
## returns the generated timestamp of the OID.
|
||||
var tmp: int32
|
||||
var dummy = oid.time
|
||||
bigEndian32(addr(tmp), addr(dummy))
|
||||
result = TTime(tmp)
|
||||
result = Time(tmp)
|
||||
|
||||
when isMainModule:
|
||||
let xo = genOID()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -29,18 +29,23 @@ else:
|
||||
include "system/ansi_c"
|
||||
|
||||
type
|
||||
FReadEnv* = object of ReadIOEffect ## effect that denotes a read
|
||||
## from an environment variable
|
||||
FWriteEnv* = object of WriteIOEffect ## effect that denotes a write
|
||||
## to an environment variable
|
||||
ReadEnvEffect* = object of ReadIOEffect ## effect that denotes a read
|
||||
## from an environment variable
|
||||
WriteEnvEffect* = object of WriteIOEffect ## effect that denotes a write
|
||||
## to an environment variable
|
||||
|
||||
FReadDir* = object of ReadIOEffect ## effect that denotes a write operation to
|
||||
## the directory structure
|
||||
FWriteDir* = object of WriteIOEffect ## effect that denotes a write operation to
|
||||
## the directory structure
|
||||
ReadDirEffect* = object of ReadIOEffect ## effect that denotes a write
|
||||
## operation to the directory structure
|
||||
WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write operation to
|
||||
## the directory structure
|
||||
|
||||
TOSErrorCode* = distinct int32 ## Specifies an OS Error Code.
|
||||
OSErrorCode* = distinct int32 ## Specifies an OS Error Code.
|
||||
|
||||
{.deprecated: [FReadEnv: ReadEnvEffect, FWriteEnv: WriteEnvEffect,
|
||||
FReadDir: ReadDirEffect,
|
||||
FWriteDir: WriteDirEffect,
|
||||
TOSErrorCode: OSErrorCode
|
||||
].}
|
||||
const
|
||||
doslike = defined(windows) or defined(OS2) or defined(DOS)
|
||||
# DOS-like filesystem
|
||||
@@ -204,7 +209,8 @@ proc osErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} =
|
||||
result = $os.strerror(errno)
|
||||
|
||||
{.push warning[deprecated]: off.}
|
||||
proc osError*(msg: string = "") {.noinline, rtl, extern: "nos$1", deprecated.} =
|
||||
proc raiseOSError*(msg: string = "") {.noinline, rtl, extern: "nos$1",
|
||||
deprecated.} =
|
||||
## raises an EOS exception with the given message ``msg``.
|
||||
## If ``msg == ""``, the operating system's error flag
|
||||
## (``errno``) is converted to a readable error message. On Windows
|
||||
@@ -219,10 +225,12 @@ proc osError*(msg: string = "") {.noinline, rtl, extern: "nos$1", deprecated.} =
|
||||
raise newException(OSError, msg)
|
||||
{.pop.}
|
||||
|
||||
proc `==`*(err1, err2: TOSErrorCode): bool {.borrow.}
|
||||
proc `$`*(err: TOSErrorCode): string {.borrow.}
|
||||
{.deprecated: [osError: raiseOSError].}
|
||||
|
||||
proc osErrorMsg*(errorCode: TOSErrorCode): string =
|
||||
proc `==`*(err1, err2: OSErrorCode): bool {.borrow.}
|
||||
proc `$`*(err: OSErrorCode): string {.borrow.}
|
||||
|
||||
proc osErrorMsg*(errorCode: OSErrorCode): string =
|
||||
## Converts an OS error code into a human readable string.
|
||||
##
|
||||
## The error code can be retrieved using the ``OSLastError`` proc.
|
||||
@@ -249,10 +257,10 @@ proc osErrorMsg*(errorCode: TOSErrorCode): string =
|
||||
result = $msgbuf
|
||||
if msgbuf != nil: localFree(msgbuf)
|
||||
else:
|
||||
if errorCode != TOSErrorCode(0'i32):
|
||||
if errorCode != OSErrorCode(0'i32):
|
||||
result = $os.strerror(errorCode.int32)
|
||||
|
||||
proc osError*(errorCode: TOSErrorCode) =
|
||||
proc raiseOSError*(errorCode: OSErrorCode) =
|
||||
## Raises an ``EOS`` exception. The ``errorCode`` will determine the
|
||||
## message, ``OSErrorMsg`` will be used to get this message.
|
||||
##
|
||||
@@ -268,7 +276,7 @@ proc osError*(errorCode: TOSErrorCode) =
|
||||
raise e
|
||||
|
||||
{.push stackTrace:off.}
|
||||
proc osLastError*(): TOSErrorCode =
|
||||
proc osLastError*(): OSErrorCode =
|
||||
## Retrieves the last operating system error code.
|
||||
##
|
||||
## This procedure is useful in the event when an OS call fails. In that case
|
||||
@@ -285,7 +293,7 @@ proc osLastError*(): TOSErrorCode =
|
||||
when defined(windows):
|
||||
result = TOSErrorCode(getLastError())
|
||||
else:
|
||||
result = TOSErrorCode(errno)
|
||||
result = OSErrorCode(errno)
|
||||
{.pop.}
|
||||
|
||||
proc unixToNativePath*(path: string, drive=""): string {.
|
||||
@@ -371,7 +379,7 @@ when defined(windows):
|
||||
f.cFileName[1].int == dot and f.cFileName[2].int == 0)
|
||||
|
||||
proc existsFile*(filename: string): bool {.rtl, extern: "nos$1",
|
||||
tags: [FReadDir].} =
|
||||
tags: [ReadDirEffect].} =
|
||||
## Returns true if the file exists, false otherwise.
|
||||
when defined(windows):
|
||||
when useWinUnicode:
|
||||
@@ -384,7 +392,7 @@ proc existsFile*(filename: string): bool {.rtl, extern: "nos$1",
|
||||
var res: TStat
|
||||
return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode)
|
||||
|
||||
proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [FReadDir].} =
|
||||
proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect].} =
|
||||
## Returns true iff the directory `dir` exists. If `dir` is a file, false
|
||||
## is returned.
|
||||
when defined(windows):
|
||||
@@ -399,7 +407,7 @@ proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [FReadDir].} =
|
||||
return stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode)
|
||||
|
||||
proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
|
||||
tags: [FReadDir].} =
|
||||
tags: [ReadDirEffect].} =
|
||||
## Returns true iff the symlink `link` exists. Will return true
|
||||
## regardless of whether the link points to a directory or file.
|
||||
when defined(windows):
|
||||
@@ -701,7 +709,7 @@ proc extractFilename*(path: string): string {.
|
||||
result = splitPath(path).tail
|
||||
|
||||
proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
|
||||
tags: [FReadDir].} =
|
||||
tags: [ReadDirEffect].} =
|
||||
## Returns the full path of `filename`, raises EOS in case of an error.
|
||||
when defined(windows):
|
||||
const bufsize = 3072'i32
|
||||
@@ -800,7 +808,7 @@ when defined(Windows):
|
||||
)
|
||||
|
||||
proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
|
||||
tags: [FReadDir].} =
|
||||
tags: [ReadDirEffect].} =
|
||||
## Returns True if both pathname arguments refer to the same physical
|
||||
## file or directory. Raises an exception if any of the files does not
|
||||
## exist or information about it can not be obtained.
|
||||
@@ -883,7 +891,7 @@ type
|
||||
fpOthersRead ## read access for others
|
||||
|
||||
proc getFilePermissions*(filename: string): set[TFilePermission] {.
|
||||
rtl, extern: "nos$1", tags: [FReadDir].} =
|
||||
rtl, extern: "nos$1", tags: [ReadDirEffect].} =
|
||||
## retrieves file permissions for `filename`. `OSError` is raised in case of
|
||||
## an error. On Windows, only the ``readonly`` flag is checked, every other
|
||||
## permission is available in any case.
|
||||
@@ -915,7 +923,7 @@ proc getFilePermissions*(filename: string): set[TFilePermission] {.
|
||||
result = {fpUserExec..fpOthersRead}
|
||||
|
||||
proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {.
|
||||
rtl, extern: "nos$1", tags: [FWriteDir].} =
|
||||
rtl, extern: "nos$1", tags: [WriteDirEffect].} =
|
||||
## sets the file permissions for `filename`. `OSError` is raised in case of
|
||||
## an error. On Windows, only the ``readonly`` flag is changed, depending on
|
||||
## ``fpUserWrite``.
|
||||
@@ -1014,7 +1022,7 @@ when defined(Windows):
|
||||
template setFileAttributes(file, attrs: expr): expr {.immediate.} =
|
||||
setFileAttributesA(file, attrs)
|
||||
|
||||
proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} =
|
||||
proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} =
|
||||
## Removes the `file`. If this fails, `EOS` is raised. This does not fail
|
||||
## if the file never existed in the first place.
|
||||
## On Windows, ignores the read-only attribute.
|
||||
@@ -1076,7 +1084,7 @@ when defined(windows):
|
||||
while true:
|
||||
var eend = strEnd(e)
|
||||
add(environment, $e)
|
||||
e = cast[WideCString](cast[ByteAddress](eend)+2)
|
||||
e = cast[WideCString](cast[TAddress](eend)+2)
|
||||
if eend[1].int == 0: break
|
||||
discard freeEnvironmentStringsW(env)
|
||||
else:
|
||||
@@ -1130,7 +1138,7 @@ proc findEnvVar(key: string): int =
|
||||
if startsWith(environment[i], temp): return i
|
||||
return -1
|
||||
|
||||
proc getEnv*(key: string): TaintedString {.tags: [FReadEnv].} =
|
||||
proc getEnv*(key: string): TaintedString {.tags: [ReadEnvEffect].} =
|
||||
## Returns the value of the `environment variable`:idx: named `key`.
|
||||
##
|
||||
## If the variable does not exist, "" is returned. To distinguish
|
||||
@@ -1144,13 +1152,13 @@ proc getEnv*(key: string): TaintedString {.tags: [FReadEnv].} =
|
||||
if env == nil: return TaintedString("")
|
||||
result = TaintedString($env)
|
||||
|
||||
proc existsEnv*(key: string): bool {.tags: [FReadEnv].} =
|
||||
proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
|
||||
## Checks whether the environment variable named `key` exists.
|
||||
## Returns true if it exists, false otherwise.
|
||||
if c_getenv(key) != nil: return true
|
||||
else: return findEnvVar(key) >= 0
|
||||
|
||||
proc putEnv*(key, val: string) {.tags: [FWriteEnv].} =
|
||||
proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
|
||||
## Sets the value of the `environment variable`:idx: named `key` to `val`.
|
||||
## If an error occurs, `EInvalidEnvVar` is raised.
|
||||
|
||||
@@ -1175,7 +1183,7 @@ proc putEnv*(key, val: string) {.tags: [FWriteEnv].} =
|
||||
else:
|
||||
if setEnvironmentVariableA(key, val) == 0'i32: osError(osLastError())
|
||||
|
||||
iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [FReadEnv].} =
|
||||
iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} =
|
||||
## Iterate over all `environments variables`:idx:. In the first component
|
||||
## of the tuple is the name of the current variable stored, in the second
|
||||
## its value.
|
||||
@@ -1185,7 +1193,7 @@ iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [FReadEnv].} =
|
||||
yield (TaintedString(substr(environment[i], 0, p-1)),
|
||||
TaintedString(substr(environment[i], p+1)))
|
||||
|
||||
iterator walkFiles*(pattern: string): string {.tags: [FReadDir].} =
|
||||
iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect].} =
|
||||
## Iterate over all the files that match the `pattern`. On POSIX this uses
|
||||
## the `glob`:idx: call.
|
||||
##
|
||||
@@ -1225,7 +1233,7 @@ type
|
||||
pcLinkToDir ## path refers to a symbolic link to a directory
|
||||
|
||||
iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] {.
|
||||
tags: [FReadDir].} =
|
||||
tags: [ReadDirEffect].} =
|
||||
## walks over the directory `dir` and yields for each directory or file in
|
||||
## `dir`. The component type and full path for each item is returned.
|
||||
## Walking is not recursive.
|
||||
@@ -1278,7 +1286,7 @@ iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] {.
|
||||
discard closedir(d)
|
||||
|
||||
iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string {.
|
||||
tags: [FReadDir].} =
|
||||
tags: [ReadDirEffect].} =
|
||||
## walks over the directory `dir` and yields for each file in `dir`. The
|
||||
## full path for each file is returned.
|
||||
## **Warning**:
|
||||
@@ -1318,7 +1326,7 @@ proc rawRemoveDir(dir: string) =
|
||||
if rmdir(dir) != 0'i32 and errno != ENOENT: osError(osLastError())
|
||||
|
||||
proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [
|
||||
FWriteDir, FReadDir].} =
|
||||
WriteDirEffect, ReadDirEffect].} =
|
||||
## Removes the directory `dir` including all subdirectories and files
|
||||
## in `dir` (recursively).
|
||||
##
|
||||
@@ -1345,7 +1353,7 @@ proc rawCreateDir(dir: string) =
|
||||
if res == 0'i32 and getLastError() != 183'i32:
|
||||
osError(osLastError())
|
||||
|
||||
proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} =
|
||||
proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} =
|
||||
## Creates the `directory`:idx: `dir`.
|
||||
##
|
||||
## The directory may contain several subdirectories that do not exist yet.
|
||||
@@ -1568,7 +1576,7 @@ proc copyDirWithPermissions*(source, dest: string,
|
||||
|
||||
proc inclFilePermissions*(filename: string,
|
||||
permissions: set[TFilePermission]) {.
|
||||
rtl, extern: "nos$1", tags: [FReadDir, FWriteDir].} =
|
||||
rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect].} =
|
||||
## a convenience procedure for:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
@@ -1577,14 +1585,14 @@ proc inclFilePermissions*(filename: string,
|
||||
|
||||
proc exclFilePermissions*(filename: string,
|
||||
permissions: set[TFilePermission]) {.
|
||||
rtl, extern: "nos$1", tags: [FReadDir, FWriteDir].} =
|
||||
rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect].} =
|
||||
## a convenience procedure for:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## setFilePermissions(filename, getFilePermissions(filename)-permissions)
|
||||
setFilePermissions(filename, getFilePermissions(filename)-permissions)
|
||||
|
||||
proc getHomeDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} =
|
||||
proc getHomeDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect].} =
|
||||
## Returns the home directory of the current user.
|
||||
##
|
||||
## This proc is wrapped by the expandTilde proc for the convenience of
|
||||
@@ -1592,12 +1600,12 @@ proc getHomeDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} =
|
||||
when defined(windows): return string(getEnv("USERPROFILE")) & "\\"
|
||||
else: return string(getEnv("HOME")) & "/"
|
||||
|
||||
proc getConfigDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} =
|
||||
proc getConfigDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect].} =
|
||||
## Returns the config directory of the current user for applications.
|
||||
when defined(windows): return string(getEnv("APPDATA")) & "\\"
|
||||
else: return string(getEnv("HOME")) & "/.config/"
|
||||
|
||||
proc getTempDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} =
|
||||
proc getTempDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect].} =
|
||||
## Returns the temporary directory of the current user for applications to
|
||||
## save temporary files in.
|
||||
when defined(windows): return string(getEnv("TEMP")) & "\\"
|
||||
@@ -1605,7 +1613,7 @@ proc getTempDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} =
|
||||
|
||||
when defined(nimdoc):
|
||||
# Common forward declaration docstring block for parameter retrieval procs.
|
||||
proc paramCount*(): int {.tags: [FReadIO].} =
|
||||
proc paramCount*(): int {.tags: [ReadIOEffect].} =
|
||||
## Returns the number of `command line arguments`:idx: given to the
|
||||
## application.
|
||||
##
|
||||
@@ -1625,7 +1633,7 @@ when defined(nimdoc):
|
||||
## else:
|
||||
## # Do something else!
|
||||
|
||||
proc paramStr*(i: int): TaintedString {.tags: [FReadIO].} =
|
||||
proc paramStr*(i: int): TaintedString {.tags: [ReadIOEffect].} =
|
||||
## Returns the `i`-th `command line argument`:idx: given to the application.
|
||||
##
|
||||
## `i` should be in the range `1..paramCount()`, the `EInvalidIndex`
|
||||
@@ -1826,7 +1834,7 @@ proc getFileSize*(file: string): BiggestInt {.rtl, extern: "nos$1",
|
||||
close(f)
|
||||
else: osError(osLastError())
|
||||
|
||||
proc findExe*(exe: string): string {.tags: [FReadDir, FReadEnv].} =
|
||||
proc findExe*(exe: string): string {.tags: [ReadDirEffect, ReadEnvEffect].} =
|
||||
## Searches for `exe` in the current working directory and then
|
||||
## in directories listed in the ``PATH`` environment variable.
|
||||
## Returns "" if the `exe` cannot be found. On DOS-like platforms, `exe`
|
||||
@@ -1854,7 +1862,6 @@ proc expandTilde*(path: string): string =
|
||||
## let configFile = expandTilde("~" / "appname.cfg")
|
||||
## echo configFile
|
||||
## # --> C:\Users\amber\appname.cfg
|
||||
|
||||
if len(path) > 1 and path[0] == '~' and (path[1] == '/' or path[1] == '\\'):
|
||||
result = getHomeDir() / path[2..len(path)-1]
|
||||
else:
|
||||
@@ -1961,7 +1968,7 @@ proc getFileInfo*(handle: FileHandle): FileInfo =
|
||||
rawToFormalFileInfo(rawInfo, result)
|
||||
|
||||
proc getFileInfo*(file: File): FileInfo =
|
||||
result = getFileInfo(file.fileHandle())
|
||||
result = getFileInfo(file.getFileHandle())
|
||||
|
||||
proc getFileInfo*(path: string, followSymlink = true): FileInfo =
|
||||
## Retrieves file information for the file object pointed to by `path`.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -24,7 +24,7 @@ when defined(linux):
|
||||
import linux
|
||||
|
||||
type
|
||||
TProcess = object of RootObj
|
||||
ProcessObj = object of RootObj
|
||||
when defined(windows):
|
||||
fProcessHandle: THandle
|
||||
inHandle, outHandle, errHandle: FileHandle
|
||||
@@ -35,9 +35,9 @@ type
|
||||
id: TPid
|
||||
exitCode: cint
|
||||
|
||||
PProcess* = ref TProcess ## represents an operating system process
|
||||
Process* = ref ProcessObj ## represents an operating system process
|
||||
|
||||
TProcessOption* = enum ## options that can be passed `startProcess`
|
||||
ProcessOption* = enum ## options that can be passed `startProcess`
|
||||
poEchoCmd, ## echo the command before execution
|
||||
poUsePath, ## Asks system to search for executable using PATH environment
|
||||
## variable.
|
||||
@@ -47,6 +47,9 @@ type
|
||||
poStdErrToStdOut, ## merge stdout and stderr to the stdout stream
|
||||
poParentStreams ## use the parent's streams
|
||||
|
||||
{.deprecated: [TProcess: ProcessObj, PProcess: Process,
|
||||
TProcessOption: ProcessOption].}
|
||||
|
||||
const poUseShell* {.deprecated.} = poUsePath
|
||||
## Deprecated alias for poUsePath.
|
||||
|
||||
@@ -125,7 +128,7 @@ proc startProcess*(command: string,
|
||||
args: openArray[string] = [],
|
||||
env: PStringTable = nil,
|
||||
options: set[TProcessOption] = {poStdErrToStdOut}):
|
||||
PProcess {.rtl, extern: "nosp$1", tags: [ExecIOEffect, FReadEnv].}
|
||||
PProcess {.rtl, extern: "nosp$1", tags: [ExecIOEffect, ReadEnvEffect].}
|
||||
## Starts a process. `Command` is the executable file, `workingDir` is the
|
||||
## process's working directory. If ``workingDir == ""`` the current directory
|
||||
## is used. `args` are the command line arguments that are passed to the
|
||||
@@ -150,7 +153,7 @@ proc startProcess*(command: string,
|
||||
|
||||
proc startCmd*(command: string, options: set[TProcessOption] = {
|
||||
poStdErrToStdOut, poUsePath}): PProcess {.
|
||||
tags: [ExecIOEffect, FReadEnv], deprecated.} =
|
||||
tags: [ExecIOEffect, ReadEnvEffect], deprecated.} =
|
||||
## Deprecated - use `startProcess` directly.
|
||||
result = startProcess(command=command, options=options + {poEvalCommand})
|
||||
|
||||
@@ -233,7 +236,7 @@ proc countProcessors*(): int {.rtl, extern: "nosp$1".} =
|
||||
proc execProcesses*(cmds: openArray[string],
|
||||
options = {poStdErrToStdOut, poParentStreams},
|
||||
n = countProcessors()): int {.rtl, extern: "nosp$1",
|
||||
tags: [ExecIOEffect, TimeEffect, FReadEnv]} =
|
||||
tags: [ExecIOEffect, TimeEffect, ReadEnvEffect]} =
|
||||
## executes the commands `cmds` in parallel. Creates `n` processes
|
||||
## that execute in parallel. The highest return value of all processes
|
||||
## is returned.
|
||||
@@ -584,12 +587,12 @@ elif not defined(useNimRtl):
|
||||
|
||||
when not defined(useFork):
|
||||
proc startProcessAuxSpawn(data: TStartProcessData): TPid {.
|
||||
tags: [ExecIOEffect, FReadEnv], gcsafe.}
|
||||
tags: [ExecIOEffect, ReadEnvEffect], gcsafe.}
|
||||
proc startProcessAuxFork(data: TStartProcessData): TPid {.
|
||||
tags: [ExecIOEffect, FReadEnv], gcsafe.}
|
||||
tags: [ExecIOEffect, ReadEnvEffect], gcsafe.}
|
||||
{.push stacktrace: off, profiler: off.}
|
||||
proc startProcessAfterFork(data: ptr TStartProcessData) {.
|
||||
tags: [ExecIOEffect, FReadEnv], cdecl, gcsafe.}
|
||||
tags: [ExecIOEffect, ReadEnvEffect], cdecl, gcsafe.}
|
||||
{.pop.}
|
||||
|
||||
proc startProcess(command: string,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2010 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -20,24 +20,24 @@
|
||||
## The file ``examples/parsecfgex.nim`` demonstrates how to use the
|
||||
## configuration file parser:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## :file: examples/parsecfgex.nim
|
||||
|
||||
|
||||
import
|
||||
import
|
||||
hashes, strutils, lexbase, streams
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
type
|
||||
TCfgEventKind* = enum ## enumeration of all events that may occur when parsing
|
||||
type
|
||||
CfgEventKind* = enum ## enumeration of all events that may occur when parsing
|
||||
cfgEof, ## end of file reached
|
||||
cfgSectionStart, ## a ``[section]`` has been parsed
|
||||
cfgKeyValuePair, ## a ``key=value`` pair has been detected
|
||||
cfgOption, ## a ``--key=value`` command line option
|
||||
cfgError ## an error ocurred during parsing
|
||||
|
||||
TCfgEvent* = object of TObject ## describes a parsing event
|
||||
CfgEvent* = object of RootObj ## describes a parsing event
|
||||
case kind*: TCfgEventKind ## the kind of the event
|
||||
of cfgEof: nil
|
||||
of cfgSectionStart:
|
||||
@@ -53,28 +53,30 @@ type
|
||||
msg*: string ## contains the error message. No exceptions
|
||||
## are thrown if a parse error occurs.
|
||||
|
||||
TTokKind = enum
|
||||
TokKind = enum
|
||||
tkInvalid, tkEof,
|
||||
tkSymbol, tkEquals, tkColon, tkBracketLe, tkBracketRi, tkDashDash
|
||||
TToken {.final.} = object # a token
|
||||
kind: TTokKind # the type of the token
|
||||
Token = object # a token
|
||||
kind: TokKind # the type of the token
|
||||
literal: string # the parsed (string) literal
|
||||
|
||||
TCfgParser* = object of TBaseLexer ## the parser object.
|
||||
tok: TToken
|
||||
CfgParser* = object of BaseLexer ## the parser object.
|
||||
tok: Token
|
||||
filename: string
|
||||
|
||||
{.deprecated: [TCfgEventKind: CfgEventKind, TCfgEvent: CfgEvent,
|
||||
TTokKind: TokKind, TToken: Token, TCfgParser: CfgParser].}
|
||||
|
||||
# implementation
|
||||
|
||||
const
|
||||
SymChars: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF', '.',
|
||||
'/', '\\'}
|
||||
SymChars: CharSet = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF', '.',
|
||||
'/', '\\'}
|
||||
|
||||
proc rawGetTok(c: var TCfgParser, tok: var TToken) {.gcsafe.}
|
||||
proc rawGetTok(c: var CfgParser, tok: var Token) {.gcsafe.}
|
||||
|
||||
proc open*(c: var TCfgParser, input: PStream, filename: string,
|
||||
lineOffset = 0) {.
|
||||
rtl, extern: "npc$1".} =
|
||||
proc open*(c: var CfgParser, input: PStream, filename: string,
|
||||
lineOffset = 0) {.rtl, extern: "npc$1".} =
|
||||
## initializes the parser with an input stream. `Filename` is only used
|
||||
## for nice error messages. `lineOffset` can be used to influence the line
|
||||
## number information in the generated error messages.
|
||||
@@ -85,23 +87,23 @@ proc open*(c: var TCfgParser, input: PStream, filename: string,
|
||||
inc(c.lineNumber, lineOffset)
|
||||
rawGetTok(c, c.tok)
|
||||
|
||||
proc close*(c: var TCfgParser) {.rtl, extern: "npc$1".} =
|
||||
proc close*(c: var CfgParser) {.rtl, extern: "npc$1".} =
|
||||
## closes the parser `c` and its associated input stream.
|
||||
lexbase.close(c)
|
||||
|
||||
proc getColumn*(c: TCfgParser): int {.rtl, extern: "npc$1".} =
|
||||
proc getColumn*(c: CfgParser): int {.rtl, extern: "npc$1".} =
|
||||
## get the current column the parser has arrived at.
|
||||
result = getColNumber(c, c.bufpos)
|
||||
|
||||
proc getLine*(c: TCfgParser): int {.rtl, extern: "npc$1".} =
|
||||
proc getLine*(c: CfgParser): int {.rtl, extern: "npc$1".} =
|
||||
## get the current line the parser has arrived at.
|
||||
result = c.lineNumber
|
||||
|
||||
proc getFilename*(c: TCfgParser): string {.rtl, extern: "npc$1".} =
|
||||
proc getFilename*(c: CfgParser): string {.rtl, extern: "npc$1".} =
|
||||
## get the filename of the file that the parser processes.
|
||||
result = c.filename
|
||||
|
||||
proc handleHexChar(c: var TCfgParser, xi: var int) =
|
||||
proc handleHexChar(c: var CfgParser, xi: var int) =
|
||||
case c.buf[c.bufpos]
|
||||
of '0'..'9':
|
||||
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0'))
|
||||
@@ -115,12 +117,12 @@ proc handleHexChar(c: var TCfgParser, xi: var int) =
|
||||
else:
|
||||
discard
|
||||
|
||||
proc handleDecChars(c: var TCfgParser, xi: var int) =
|
||||
proc handleDecChars(c: var CfgParser, xi: var int) =
|
||||
while c.buf[c.bufpos] in {'0'..'9'}:
|
||||
xi = (xi * 10) + (ord(c.buf[c.bufpos]) - ord('0'))
|
||||
inc(c.bufpos)
|
||||
|
||||
proc getEscapedChar(c: var TCfgParser, tok: var TToken) =
|
||||
proc getEscapedChar(c: var CfgParser, tok: var TToken) =
|
||||
inc(c.bufpos) # skip '\'
|
||||
case c.buf[c.bufpos]
|
||||
of 'n', 'N':
|
||||
@@ -169,13 +171,13 @@ proc getEscapedChar(c: var TCfgParser, tok: var TToken) =
|
||||
else: tok.kind = tkInvalid
|
||||
else: tok.kind = tkInvalid
|
||||
|
||||
proc handleCRLF(c: var TCfgParser, pos: int): int =
|
||||
proc handleCRLF(c: var CfgParser, pos: int): int =
|
||||
case c.buf[pos]
|
||||
of '\c': result = lexbase.handleCR(c, pos)
|
||||
of '\L': result = lexbase.handleLF(c, pos)
|
||||
else: result = pos
|
||||
|
||||
proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
|
||||
proc getString(c: var CfgParser, tok: var Token, rawMode: bool) =
|
||||
var pos = c.bufpos + 1 # skip "
|
||||
var buf = c.buf # put `buf` in a register
|
||||
tok.kind = tkSymbol
|
||||
@@ -221,7 +223,7 @@ proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
|
||||
inc(pos)
|
||||
c.bufpos = pos
|
||||
|
||||
proc getSymbol(c: var TCfgParser, tok: var TToken) =
|
||||
proc getSymbol(c: var CfgParser, tok: var Token) =
|
||||
var pos = c.bufpos
|
||||
var buf = c.buf
|
||||
while true:
|
||||
@@ -231,7 +233,7 @@ proc getSymbol(c: var TCfgParser, tok: var TToken) =
|
||||
c.bufpos = pos
|
||||
tok.kind = tkSymbol
|
||||
|
||||
proc skip(c: var TCfgParser) =
|
||||
proc skip(c: var CfgParser) =
|
||||
var pos = c.bufpos
|
||||
var buf = c.buf
|
||||
while true:
|
||||
@@ -247,7 +249,7 @@ proc skip(c: var TCfgParser) =
|
||||
break # EndOfFile also leaves the loop
|
||||
c.bufpos = pos
|
||||
|
||||
proc rawGetTok(c: var TCfgParser, tok: var TToken) =
|
||||
proc rawGetTok(c: var CfgParser, tok: var Token) =
|
||||
tok.kind = tkInvalid
|
||||
setLen(tok.literal, 0)
|
||||
skip(c)
|
||||
@@ -286,19 +288,19 @@ proc rawGetTok(c: var TCfgParser, tok: var TToken) =
|
||||
tok.literal = "[EOF]"
|
||||
else: getSymbol(c, tok)
|
||||
|
||||
proc errorStr*(c: TCfgParser, msg: string): string {.rtl, extern: "npc$1".} =
|
||||
proc errorStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} =
|
||||
## returns a properly formated error message containing current line and
|
||||
## column information.
|
||||
result = `%`("$1($2, $3) Error: $4",
|
||||
[c.filename, $getLine(c), $getColumn(c), msg])
|
||||
|
||||
proc warningStr*(c: TCfgParser, msg: string): string {.rtl, extern: "npc$1".} =
|
||||
proc warningStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} =
|
||||
## returns a properly formated warning message containing current line and
|
||||
## column information.
|
||||
result = `%`("$1($2, $3) Warning: $4",
|
||||
[c.filename, $getLine(c), $getColumn(c), msg])
|
||||
|
||||
proc ignoreMsg*(c: TCfgParser, e: TCfgEvent): string {.rtl, extern: "npc$1".} =
|
||||
proc ignoreMsg*(c: CfgParser, e: CfgEvent): string {.rtl, extern: "npc$1".} =
|
||||
## returns a properly formated warning message containing that
|
||||
## an entry is ignored.
|
||||
case e.kind
|
||||
@@ -309,7 +311,7 @@ proc ignoreMsg*(c: TCfgParser, e: TCfgEvent): string {.rtl, extern: "npc$1".} =
|
||||
of cfgError: result = e.msg
|
||||
of cfgEof: result = ""
|
||||
|
||||
proc getKeyValPair(c: var TCfgParser, kind: TCfgEventKind): TCfgEvent =
|
||||
proc getKeyValPair(c: var CfgParser, kind: CfgEventKind): CfgEvent =
|
||||
if c.tok.kind == tkSymbol:
|
||||
result.kind = kind
|
||||
result.key = c.tok.literal
|
||||
@@ -329,7 +331,7 @@ proc getKeyValPair(c: var TCfgParser, kind: TCfgEventKind): TCfgEvent =
|
||||
result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
|
||||
rawGetTok(c, c.tok)
|
||||
|
||||
proc next*(c: var TCfgParser): TCfgEvent {.rtl, extern: "npc$1".} =
|
||||
proc next*(c: var CfgParser): CfgEvent {.rtl, extern: "npc$1".} =
|
||||
## retrieves the first/next event. This controls the parser.
|
||||
case c.tok.kind
|
||||
of tkEof:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2009 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -13,16 +13,16 @@
|
||||
## Example: How to use the parser
|
||||
## ==============================
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## import os, parsecsv, streams
|
||||
## var s = newFileStream(ParamStr(1), fmRead)
|
||||
## if s == nil: quit("cannot open the file" & ParamStr(1))
|
||||
## var x: TCsvParser
|
||||
## open(x, s, ParamStr(1))
|
||||
## var s = newFileStream(paramStr(1), fmRead)
|
||||
## if s == nil: quit("cannot open the file" & paramStr(1))
|
||||
## var x: CsvParser
|
||||
## open(x, s, paramStr(1))
|
||||
## while readRow(x):
|
||||
## Echo "new row: "
|
||||
## echo "new row: "
|
||||
## for val in items(x.row):
|
||||
## Echo "##", val, "##"
|
||||
## echo "##", val, "##"
|
||||
## close(x)
|
||||
##
|
||||
|
||||
@@ -30,28 +30,30 @@ import
|
||||
lexbase, streams
|
||||
|
||||
type
|
||||
TCsvRow* = seq[string] ## a row in a CSV file
|
||||
TCsvParser* = object of TBaseLexer ## the parser object.
|
||||
row*: TCsvRow ## the current row
|
||||
CsvRow* = seq[string] ## a row in a CSV file
|
||||
CsvParser* = object of BaseLexer ## the parser object.
|
||||
row*: CsvRow ## the current row
|
||||
filename: string
|
||||
sep, quote, esc: char
|
||||
skipWhite: bool
|
||||
currRow: int
|
||||
|
||||
EInvalidCsv* = object of EIO ## exception that is raised if
|
||||
## a parsing error occurs
|
||||
CsvError* = object of IOError ## exception that is raised if
|
||||
## a parsing error occurs
|
||||
|
||||
{.deprecated: [TCsvRow: CsvRow, TCsvParser: CsvParser, EInvalidCsv: CsvError].}
|
||||
|
||||
proc raiseEInvalidCsv(filename: string, line, col: int,
|
||||
msg: string) {.noreturn.} =
|
||||
var e: ref EInvalidCsv
|
||||
var e: ref CsvError
|
||||
new(e)
|
||||
e.msg = filename & "(" & $line & ", " & $col & ") Error: " & msg
|
||||
raise e
|
||||
|
||||
proc error(my: TCsvParser, pos: int, msg: string) =
|
||||
proc error(my: CsvParser, pos: int, msg: string) =
|
||||
raiseEInvalidCsv(my.filename, my.LineNumber, getColNumber(my, pos), msg)
|
||||
|
||||
proc open*(my: var TCsvParser, input: PStream, filename: string,
|
||||
proc open*(my: var CsvParser, input: Stream, filename: string,
|
||||
separator = ',', quote = '"', escape = '\0',
|
||||
skipInitialSpace = false) =
|
||||
## initializes the parser with an input stream. `Filename` is only used
|
||||
@@ -75,7 +77,7 @@ proc open*(my: var TCsvParser, input: PStream, filename: string,
|
||||
my.row = @[]
|
||||
my.currRow = 0
|
||||
|
||||
proc parseField(my: var TCsvParser, a: var string) =
|
||||
proc parseField(my: var CsvParser, a: var string) =
|
||||
var pos = my.bufpos
|
||||
var buf = my.buf
|
||||
if my.skipWhite:
|
||||
@@ -83,7 +85,7 @@ proc parseField(my: var TCsvParser, a: var string) =
|
||||
setLen(a, 0) # reuse memory
|
||||
if buf[pos] == my.quote and my.quote != '\0':
|
||||
inc(pos)
|
||||
while true:
|
||||
while true:
|
||||
var c = buf[pos]
|
||||
if c == '\0':
|
||||
my.bufpos = pos # can continue after exception?
|
||||
@@ -121,11 +123,11 @@ proc parseField(my: var TCsvParser, a: var string) =
|
||||
inc(pos)
|
||||
my.bufpos = pos
|
||||
|
||||
proc processedRows*(my: var TCsvParser): int =
|
||||
proc processedRows*(my: var CsvParser): int =
|
||||
## returns number of the processed rows
|
||||
return my.currRow
|
||||
|
||||
proc readRow*(my: var TCsvParser, columns = 0): bool =
|
||||
proc readRow*(my: var CsvParser, columns = 0): bool =
|
||||
## reads the next row; if `columns` > 0, it expects the row to have
|
||||
## exactly this many columns. Returns false if the end of the file
|
||||
## has been encountered else true.
|
||||
@@ -160,19 +162,19 @@ proc readRow*(my: var TCsvParser, columns = 0): bool =
|
||||
$col & " columns")
|
||||
inc(my.currRow)
|
||||
|
||||
proc close*(my: var TCsvParser) {.inline.} =
|
||||
proc close*(my: var CsvParser) {.inline.} =
|
||||
## closes the parser `my` and its associated input stream.
|
||||
lexbase.close(my)
|
||||
|
||||
when isMainModule:
|
||||
import os
|
||||
var s = newFileStream(ParamStr(1), fmRead)
|
||||
if s == nil: quit("cannot open the file" & ParamStr(1))
|
||||
var x: TCsvParser
|
||||
open(x, s, ParamStr(1))
|
||||
var s = newFileStream(paramStr(1), fmRead)
|
||||
if s == nil: quit("cannot open the file" & paramStr(1))
|
||||
var x: CsvParser
|
||||
open(x, s, paramStr(1))
|
||||
while readRow(x):
|
||||
Echo "new row: "
|
||||
echo "new row: "
|
||||
for val in items(x.row):
|
||||
Echo "##", val, "##"
|
||||
echo "##", val, "##"
|
||||
close(x)
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module provides the standard Nimrod command line parser.
|
||||
## This module provides the standard Nim command line parser.
|
||||
## It supports one convenience iterator over all command line options and some
|
||||
## lower-level features.
|
||||
##
|
||||
@@ -22,26 +22,28 @@ import
|
||||
os, strutils
|
||||
|
||||
type
|
||||
TCmdLineKind* = enum ## the detected command line token
|
||||
CmdLineKind* = enum ## the detected command line token
|
||||
cmdEnd, ## end of command line reached
|
||||
cmdArgument, ## argument detected
|
||||
cmdLongoption, ## a long option ``--option`` detected
|
||||
cmdShortOption ## a short option ``-c`` detected
|
||||
TOptParser* =
|
||||
OptParser* =
|
||||
object of RootObj ## this object implements the command line parser
|
||||
cmd: string
|
||||
pos: int
|
||||
inShortState: bool
|
||||
kind*: TCmdLineKind ## the dected command line token
|
||||
kind*: CmdLineKind ## the dected command line token
|
||||
key*, val*: TaintedString ## key and value pair; ``key`` is the option
|
||||
## or the argument, ``value`` is not "" if
|
||||
## the option was given a value
|
||||
|
||||
{.deprecated: [TCmdLineKind: CmdLineKind, TOptParser: OptParser].}
|
||||
|
||||
when declared(os.paramCount):
|
||||
# we cannot provide this for NimRtl creation on Posix, because we can't
|
||||
# access the command line arguments then!
|
||||
|
||||
proc initOptParser*(cmdline = ""): TOptParser =
|
||||
proc initOptParser*(cmdline = ""): OptParser =
|
||||
## inits the option parser. If ``cmdline == ""``, the real command line
|
||||
## (as provided by the ``OS`` module) is taken.
|
||||
result.pos = 0
|
||||
@@ -57,7 +59,7 @@ when declared(os.paramCount):
|
||||
result.val = TaintedString""
|
||||
|
||||
proc parseWord(s: string, i: int, w: var string,
|
||||
delim: TCharSet = {'\x09', ' ', '\0'}): int =
|
||||
delim: CharSet = {'\x09', ' ', '\0'}): int =
|
||||
result = i
|
||||
if s[result] == '\"':
|
||||
inc(result)
|
||||
@@ -70,7 +72,7 @@ proc parseWord(s: string, i: int, w: var string,
|
||||
add(w, s[result])
|
||||
inc(result)
|
||||
|
||||
proc handleShortOption(p: var TOptParser) =
|
||||
proc handleShortOption(p: var OptParser) =
|
||||
var i = p.pos
|
||||
p.kind = cmdShortOption
|
||||
add(p.key.string, p.cmd[i])
|
||||
@@ -87,8 +89,7 @@ proc handleShortOption(p: var TOptParser) =
|
||||
if p.cmd[i] == '\0': p.inShortState = false
|
||||
p.pos = i
|
||||
|
||||
proc next*(p: var TOptParser) {.
|
||||
rtl, extern: "npo$1".} =
|
||||
proc next*(p: var OptParser) {.rtl, extern: "npo$1".} =
|
||||
## parses the first or next option; ``p.kind`` describes what token has been
|
||||
## parsed. ``p.key`` and ``p.val`` are set accordingly.
|
||||
var i = p.pos
|
||||
@@ -122,14 +123,12 @@ proc next*(p: var TOptParser) {.
|
||||
p.kind = cmdArgument
|
||||
p.pos = parseWord(p.cmd, i, p.key.string)
|
||||
|
||||
proc cmdLineRest*(p: TOptParser): TaintedString {.
|
||||
rtl, extern: "npo$1".} =
|
||||
proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo$1".} =
|
||||
## retrieves the rest of the command line that has not been parsed yet.
|
||||
result = strip(substr(p.cmd, p.pos, len(p.cmd) - 1)).TaintedString
|
||||
|
||||
when declared(initOptParser):
|
||||
|
||||
iterator getopt*(): tuple[kind: TCmdLineKind, key, val: TaintedString] =
|
||||
iterator getopt*(): tuple[kind: CmdLineKind, key, val: TaintedString] =
|
||||
## This is an convenience iterator for iterating over the command line.
|
||||
## This uses the TOptParser object. Example:
|
||||
##
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module provides the standard Nimrod command line parser.
|
||||
## This module provides the standard Nim command line parser.
|
||||
## It supports one convenience iterator over all command line options and some
|
||||
## lower-level features.
|
||||
##
|
||||
@@ -25,22 +25,24 @@ import
|
||||
os, strutils
|
||||
|
||||
type
|
||||
TCmdLineKind* = enum ## the detected command line token
|
||||
CmdLineKind* = enum ## the detected command line token
|
||||
cmdEnd, ## end of command line reached
|
||||
cmdArgument, ## argument detected
|
||||
cmdLongOption, ## a long option ``--option`` detected
|
||||
cmdShortOption ## a short option ``-c`` detected
|
||||
TOptParser* =
|
||||
OptParser* =
|
||||
object of TObject ## this object implements the command line parser
|
||||
cmd: seq[string]
|
||||
pos: int
|
||||
remainingShortOptions: string
|
||||
kind*: TCmdLineKind ## the dected command line token
|
||||
kind*: CmdLineKind ## the dected command line token
|
||||
key*, val*: TaintedString ## key and value pair; ``key`` is the option
|
||||
## or the argument, ``value`` is not "" if
|
||||
## the option was given a value
|
||||
|
||||
proc initOptParser*(cmdline: seq[string]): TOptParser {.rtl.} =
|
||||
{.deprecated: [TCmdLineKind: CmdLineKind, TOptParser: OptParser].}
|
||||
|
||||
proc initOptParser*(cmdline: seq[string]): OptParser {.rtl.} =
|
||||
## Initalizes option parses with cmdline. cmdline should not contain
|
||||
## argument 0 - program name.
|
||||
## If cmdline == nil default to current command line arguments.
|
||||
@@ -54,7 +56,7 @@ proc initOptParser*(cmdline: seq[string]): TOptParser {.rtl.} =
|
||||
|
||||
result.cmd = @cmdline
|
||||
|
||||
proc initOptParser*(cmdline: string): TOptParser {.rtl, deprecated.} =
|
||||
proc initOptParser*(cmdline: string): OptParser {.rtl, deprecated.} =
|
||||
## Initalizes option parses with cmdline. Splits cmdline in on spaces
|
||||
## and calls initOptParser(openarray[string])
|
||||
## Do not use.
|
||||
@@ -64,13 +66,13 @@ proc initOptParser*(cmdline: string): TOptParser {.rtl, deprecated.} =
|
||||
return initOptParser(cmdline.split)
|
||||
|
||||
when not defined(createNimRtl):
|
||||
proc initOptParser*(): TOptParser =
|
||||
proc initOptParser*(): OptParser =
|
||||
## Initializes option parser from current command line arguments.
|
||||
return initOptParser(commandLineParams())
|
||||
|
||||
proc next*(p: var TOptParser) {.rtl, extern: "npo$1".}
|
||||
proc next*(p: var OptParser) {.rtl, extern: "npo$1".}
|
||||
|
||||
proc nextOption(p: var TOptParser, token: string, allowEmpty: bool) =
|
||||
proc nextOption(p: var OptParser, token: string, allowEmpty: bool) =
|
||||
for splitchar in [':', '=']:
|
||||
if splitchar in token:
|
||||
let pos = token.find(splitchar)
|
||||
@@ -85,7 +87,7 @@ proc nextOption(p: var TOptParser, token: string, allowEmpty: bool) =
|
||||
p.remainingShortOptions = token[0..token.len-1]
|
||||
p.next()
|
||||
|
||||
proc next(p: var TOptParser) =
|
||||
proc next(p: var OptParser) =
|
||||
if p.remainingShortOptions.len != 0:
|
||||
p.kind = cmdShortOption
|
||||
p.key = TaintedString(p.remainingShortOptions[0..0])
|
||||
@@ -111,20 +113,22 @@ proc next(p: var TOptParser) =
|
||||
p.key = token
|
||||
p.val = ""
|
||||
|
||||
proc cmdLineRest*(p: TOptParser): TaintedString {.rtl, extern: "npo$1", deprecated.} =
|
||||
proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo$1", deprecated.} =
|
||||
## Returns part of command line string that has not been parsed yet.
|
||||
## Do not use - does not correctly handle whitespace.
|
||||
return p.cmd[p.pos..p.cmd.len-1].join(" ")
|
||||
|
||||
type
|
||||
TGetoptResult* = tuple[kind: TCmdLineKind, key, val: TaintedString]
|
||||
GetoptResult* = tuple[kind: CmdLineKind, key, val: TaintedString]
|
||||
|
||||
{.deprecated: [TGetoptResult: GetoptResult].}
|
||||
|
||||
when declared(paramCount):
|
||||
iterator getopt*(): TGetoptResult =
|
||||
iterator getopt*(): GetoptResult =
|
||||
## This is an convenience iterator for iterating over the command line.
|
||||
## This uses the TOptParser object. Example:
|
||||
## This uses the OptParser object. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## var
|
||||
## filename = ""
|
||||
## for kind, key, val in getopt():
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2009 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -10,13 +10,13 @@
|
||||
## The ``parsesql`` module implements a high performance SQL file
|
||||
## parser. It parses PostgreSQL syntax and the SQL ANSI standard.
|
||||
|
||||
import
|
||||
import
|
||||
hashes, strutils, lexbase, streams
|
||||
|
||||
# ------------------- scanner -------------------------------------------------
|
||||
|
||||
type
|
||||
TTokKind = enum ## enumeration of all SQL tokens
|
||||
TokKind = enum ## enumeration of all SQL tokens
|
||||
tkInvalid, ## invalid token
|
||||
tkEof, ## end of file reached
|
||||
tkIdentifier, ## abc
|
||||
@@ -38,33 +38,35 @@ type
|
||||
tkBracketRi, ## ']'
|
||||
tkDot ## '.'
|
||||
|
||||
TToken {.final.} = object # a token
|
||||
kind: TTokKind # the type of the token
|
||||
Token = object # a token
|
||||
kind: TokKind # the type of the token
|
||||
literal: string # the parsed (string) literal
|
||||
|
||||
TSqlLexer* = object of TBaseLexer ## the parser object.
|
||||
SqlLexer* = object of BaseLexer ## the parser object.
|
||||
filename: string
|
||||
|
||||
{.deprecated: [TToken: Token, TSqlLexer: SqlLexer].}
|
||||
|
||||
const
|
||||
tokKindToStr: array[TTokKind, string] = [
|
||||
tokKindToStr: array[TokKind, string] = [
|
||||
"invalid", "[EOF]", "identifier", "quoted identifier", "string constant",
|
||||
"escape string constant", "dollar quoted constant", "bit string constant",
|
||||
"hex string constant", "integer constant", "numeric constant", "operator",
|
||||
";", ":", ",", "(", ")", "[", "]", "."
|
||||
]
|
||||
|
||||
proc open(L: var TSqlLexer, input: PStream, filename: string) =
|
||||
proc open(L: var SqlLexer, input: Stream, filename: string) =
|
||||
lexbase.open(L, input)
|
||||
L.filename = filename
|
||||
|
||||
proc close(L: var TSqlLexer) =
|
||||
proc close(L: var SqlLexer) =
|
||||
lexbase.close(L)
|
||||
|
||||
proc getColumn(L: TSqlLexer): int =
|
||||
proc getColumn(L: SqlLexer): int =
|
||||
## get the current column the parser has arrived at.
|
||||
result = getColNumber(L, L.bufPos)
|
||||
|
||||
proc getLine(L: TSqlLexer): int =
|
||||
proc getLine(L: SqlLexer): int =
|
||||
result = L.linenumber
|
||||
|
||||
proc handleHexChar(c: var TSqlLexer, xi: var int) =
|
||||
@@ -137,10 +139,10 @@ proc getEscapedChar(c: var TSqlLexer, tok: var TToken) =
|
||||
else: tok.kind = tkInvalid
|
||||
else: tok.kind = tkInvalid
|
||||
|
||||
proc HandleCRLF(c: var TSqlLexer, pos: int): int =
|
||||
proc handleCRLF(c: var TSqlLexer, pos: int): int =
|
||||
case c.buf[pos]
|
||||
of '\c': result = lexbase.HandleCR(c, pos)
|
||||
of '\L': result = lexbase.HandleLF(c, pos)
|
||||
of '\c': result = lexbase.handleCR(c, pos)
|
||||
of '\L': result = lexbase.handleLF(c, pos)
|
||||
else: result = pos
|
||||
|
||||
proc skip(c: var TSqlLexer) =
|
||||
@@ -163,7 +165,7 @@ proc skip(c: var TSqlLexer) =
|
||||
case buf[pos]
|
||||
of '\0': break
|
||||
of '\c', '\L':
|
||||
pos = HandleCRLF(c, pos)
|
||||
pos = handleCRLF(c, pos)
|
||||
buf = c.buf
|
||||
of '*':
|
||||
if buf[pos+1] == '/':
|
||||
@@ -181,13 +183,13 @@ proc skip(c: var TSqlLexer) =
|
||||
else: inc(pos)
|
||||
else: break
|
||||
of '\c', '\L':
|
||||
pos = HandleCRLF(c, pos)
|
||||
pos = handleCRLF(c, pos)
|
||||
buf = c.buf
|
||||
else:
|
||||
break # EndOfFile also leaves the loop
|
||||
c.bufpos = pos
|
||||
|
||||
proc getString(c: var TSqlLexer, tok: var TToken, kind: TTokKind) =
|
||||
proc getString(c: var TSqlLexer, tok: var TToken, kind: TokKind) =
|
||||
var pos = c.bufPos + 1
|
||||
var buf = c.buf
|
||||
tok.kind = kind
|
||||
@@ -465,7 +467,7 @@ proc errorStr(L: TSqlLexer, msg: string): string =
|
||||
# :: left PostgreSQL-style typecast
|
||||
# [ ] left array element selection
|
||||
# - right unary minus
|
||||
# ^ left exponentiation
|
||||
# ^ left exponentiation
|
||||
# * / % left multiplication, division, modulo
|
||||
# + - left addition, subtraction
|
||||
# IS IS TRUE, IS FALSE, IS UNKNOWN, IS NULL
|
||||
@@ -483,7 +485,7 @@ proc errorStr(L: TSqlLexer, msg: string): string =
|
||||
# OR left logical disjunction
|
||||
|
||||
type
|
||||
TSqlNodeKind* = enum ## kind of SQL abstract syntax tree
|
||||
SqlNodeKind* = enum ## kind of SQL abstract syntax tree
|
||||
nkNone,
|
||||
nkIdent,
|
||||
nkStringLit,
|
||||
@@ -536,79 +538,82 @@ type
|
||||
nkEnumDef
|
||||
|
||||
type
|
||||
EInvalidSql* = object of EInvalidValue ## Invalid SQL encountered
|
||||
PSqlNode* = ref TSqlNode ## an SQL abstract syntax tree node
|
||||
TSqlNode* = object ## an SQL abstract syntax tree node
|
||||
case kind*: TSqlNodeKind ## kind of syntax tree
|
||||
SqlParseError* = object of ValueError ## Invalid SQL encountered
|
||||
SqlNode* = ref SqlNodeObj ## an SQL abstract syntax tree node
|
||||
SqlNodeObj* = object ## an SQL abstract syntax tree node
|
||||
case kind*: SqlNodeKind ## kind of syntax tree
|
||||
of nkIdent, nkStringLit, nkBitStringLit, nkHexStringLit,
|
||||
nkIntegerLit, nkNumericLit:
|
||||
strVal*: string ## AST leaf: the identifier, numeric literal
|
||||
## string literal, etc.
|
||||
else:
|
||||
sons*: seq[PSqlNode] ## the node's children
|
||||
sons*: seq[SqlNode] ## the node's children
|
||||
|
||||
TSqlParser* = object of TSqlLexer ## SQL parser object
|
||||
tok: TToken
|
||||
SqlParser* = object of SqlLexer ## SQL parser object
|
||||
tok: Token
|
||||
|
||||
proc newNode(k: TSqlNodeKind): PSqlNode =
|
||||
{.deprecated: [EInvalidSql: SqlParseError, PSqlNode: SqlNode,
|
||||
TSqlNode: SqlNodeObj, TSqlParser: SqlParser, TSqlNodeKind: SqlNodeKind].}
|
||||
|
||||
proc newNode(k: SqlNodeKind): SqlNode =
|
||||
new(result)
|
||||
result.kind = k
|
||||
|
||||
proc newNode(k: TSqlNodeKind, s: string): PSqlNode =
|
||||
proc newNode(k: SqlNodeKind, s: string): SqlNode =
|
||||
new(result)
|
||||
result.kind = k
|
||||
result.strVal = s
|
||||
|
||||
proc len*(n: PSqlNode): int =
|
||||
proc len*(n: SqlNode): int =
|
||||
if isNil(n.sons): result = 0
|
||||
else: result = n.sons.len
|
||||
|
||||
proc add*(father, n: PSqlNode) =
|
||||
proc add*(father, n: SqlNode) =
|
||||
if isNil(father.sons): father.sons = @[]
|
||||
add(father.sons, n)
|
||||
|
||||
proc getTok(p: var TSqlParser) =
|
||||
proc getTok(p: var SqlParser) =
|
||||
getTok(p, p.tok)
|
||||
|
||||
proc sqlError(p: TSqlParser, msg: string) =
|
||||
var e: ref EInvalidSql
|
||||
proc sqlError(p: SqlParser, msg: string) =
|
||||
var e: ref SqlParseError
|
||||
new(e)
|
||||
e.msg = errorStr(p, msg)
|
||||
raise e
|
||||
|
||||
proc isKeyw(p: TSqlParser, keyw: string): bool =
|
||||
proc isKeyw(p: SqlParser, keyw: string): bool =
|
||||
result = p.tok.kind == tkIdentifier and
|
||||
cmpIgnoreCase(p.tok.literal, keyw) == 0
|
||||
|
||||
proc isOpr(p: TSqlParser, opr: string): bool =
|
||||
proc isOpr(p: SqlParser, opr: string): bool =
|
||||
result = p.tok.kind == tkOperator and
|
||||
cmpIgnoreCase(p.tok.literal, opr) == 0
|
||||
|
||||
proc optKeyw(p: var TSqlParser, keyw: string) =
|
||||
proc optKeyw(p: var SqlParser, keyw: string) =
|
||||
if p.tok.kind == tkIdentifier and cmpIgnoreCase(p.tok.literal, keyw) == 0:
|
||||
getTok(p)
|
||||
|
||||
proc expectIdent(p: TSqlParser) =
|
||||
proc expectIdent(p: SqlParser) =
|
||||
if p.tok.kind != tkIdentifier and p.tok.kind != tkQuotedIdentifier:
|
||||
sqlError(p, "identifier expected")
|
||||
|
||||
proc expect(p: TSqlParser, kind: TTokKind) =
|
||||
proc expect(p: SqlParser, kind: TokKind) =
|
||||
if p.tok.kind != kind:
|
||||
sqlError(p, tokKindToStr[kind] & " expected")
|
||||
|
||||
proc eat(p: var TSqlParser, kind: TTokKind) =
|
||||
proc eat(p: var SqlParser, kind: TokKind) =
|
||||
if p.tok.kind == kind:
|
||||
getTok(p)
|
||||
else:
|
||||
sqlError(p, tokKindToStr[kind] & " expected")
|
||||
|
||||
proc eat(p: var TSqlParser, keyw: string) =
|
||||
proc eat(p: var SqlParser, keyw: string) =
|
||||
if isKeyw(p, keyw):
|
||||
getTok(p)
|
||||
else:
|
||||
sqlError(p, keyw.toUpper() & " expected")
|
||||
|
||||
proc parseDataType(p: var TSqlParser): PSqlNode =
|
||||
proc parseDataType(p: var SqlParser): SqlNode =
|
||||
if isKeyw(p, "enum"):
|
||||
result = newNode(nkEnumDef)
|
||||
getTok(p)
|
||||
@@ -636,7 +641,7 @@ proc parseDataType(p: var TSqlParser): PSqlNode =
|
||||
getTok(p)
|
||||
eat(p, tkParRi)
|
||||
|
||||
proc getPrecedence(p: TSqlParser): int =
|
||||
proc getPrecedence(p: SqlParser): int =
|
||||
if isOpr(p, "*") or isOpr(p, "/") or isOpr(p, "%"):
|
||||
result = 6
|
||||
elif isOpr(p, "+") or isOpr(p, "-"):
|
||||
@@ -655,9 +660,9 @@ proc getPrecedence(p: TSqlParser): int =
|
||||
else:
|
||||
result = - 1
|
||||
|
||||
proc parseExpr(p: var TSqlParser): PSqlNode
|
||||
proc parseExpr(p: var SqlParser): SqlNode
|
||||
|
||||
proc identOrLiteral(p: var TSqlParser): PSqlNode =
|
||||
proc identOrLiteral(p: var SqlParser): SqlNode =
|
||||
case p.tok.kind
|
||||
of tkIdentifier, tkQuotedIdentifier:
|
||||
result = newNode(nkIdent, p.tok.literal)
|
||||
@@ -685,7 +690,7 @@ proc identOrLiteral(p: var TSqlParser): PSqlNode =
|
||||
sqlError(p, "expression expected")
|
||||
getTok(p) # we must consume a token here to prevend endless loops!
|
||||
|
||||
proc primary(p: var TSqlParser): PSqlNode =
|
||||
proc primary(p: var SqlParser): SqlNode =
|
||||
if p.tok.kind == tkOperator or isKeyw(p, "not"):
|
||||
result = newNode(nkPrefix)
|
||||
result.add(newNode(nkIdent, p.tok.literal))
|
||||
@@ -723,9 +728,9 @@ proc primary(p: var TSqlParser): PSqlNode =
|
||||
getTok(p)
|
||||
else: break
|
||||
|
||||
proc lowestExprAux(p: var TSqlParser, v: var PSqlNode, limit: int): int =
|
||||
proc lowestExprAux(p: var SqlParser, v: var SqlNode, limit: int): int =
|
||||
var
|
||||
v2, node, opNode: PSqlNode
|
||||
v2, node, opNode: SqlNode
|
||||
v = primary(p) # expand while operators have priorities higher than 'limit'
|
||||
var opPred = getPrecedence(p)
|
||||
result = opPred
|
||||
@@ -740,14 +745,14 @@ proc lowestExprAux(p: var TSqlParser, v: var PSqlNode, limit: int): int =
|
||||
v = node
|
||||
opPred = getPrecedence(p)
|
||||
|
||||
proc parseExpr(p: var TSqlParser): PSqlNode =
|
||||
proc parseExpr(p: var SqlParser): SqlNode =
|
||||
discard lowestExprAux(p, result, - 1)
|
||||
|
||||
proc parseTableName(p: var TSqlParser): PSqlNode =
|
||||
proc parseTableName(p: var SqlParser): SqlNode =
|
||||
expectIdent(p)
|
||||
result = primary(p)
|
||||
|
||||
proc parseColumnReference(p: var TSqlParser): PSqlNode =
|
||||
proc parseColumnReference(p: var SqlParser): SqlNode =
|
||||
result = parseTableName(p)
|
||||
if p.tok.kind == tkParLe:
|
||||
getTok(p)
|
||||
@@ -760,12 +765,12 @@ proc parseColumnReference(p: var TSqlParser): PSqlNode =
|
||||
result.add(parseTableName(p))
|
||||
eat(p, tkParRi)
|
||||
|
||||
proc parseCheck(p: var TSqlParser): PSqlNode =
|
||||
proc parseCheck(p: var SqlParser): SqlNode =
|
||||
getTok(p)
|
||||
result = newNode(nkCheck)
|
||||
result.add(parseExpr(p))
|
||||
|
||||
proc parseConstraint(p: var TSqlParser): PSqlNode =
|
||||
proc parseConstraint(p: var SqlParser): SqlNode =
|
||||
getTok(p)
|
||||
result = newNode(nkConstraint)
|
||||
expectIdent(p)
|
||||
@@ -774,7 +779,7 @@ proc parseConstraint(p: var TSqlParser): PSqlNode =
|
||||
eat(p, "check")
|
||||
result.add(parseExpr(p))
|
||||
|
||||
proc parseColumnConstraints(p: var TSqlParser, result: PSqlNode) =
|
||||
proc parseColumnConstraints(p: var SqlParser, result: SqlNode) =
|
||||
while true:
|
||||
if isKeyw(p, "default"):
|
||||
getTok(p)
|
||||
@@ -806,7 +811,7 @@ proc parseColumnConstraints(p: var TSqlParser, result: PSqlNode) =
|
||||
else:
|
||||
break
|
||||
|
||||
proc parseColumnDef(p: var TSqlParser): PSqlNode =
|
||||
proc parseColumnDef(p: var SqlParser): SqlNode =
|
||||
expectIdent(p)
|
||||
result = newNode(nkColumnDef)
|
||||
result.add(newNode(nkIdent, p.tok.literal))
|
||||
@@ -814,7 +819,7 @@ proc parseColumnDef(p: var TSqlParser): PSqlNode =
|
||||
result.add(parseDataType(p))
|
||||
parseColumnConstraints(p, result)
|
||||
|
||||
proc parseIfNotExists(p: var TSqlParser, k: TSqlNodeKind): PSqlNode =
|
||||
proc parseIfNotExists(p: var SqlParser, k: TSqlNodeKind): SqlNode =
|
||||
getTok(p)
|
||||
if isKeyw(p, "if"):
|
||||
getTok(p)
|
||||
@@ -824,7 +829,7 @@ proc parseIfNotExists(p: var TSqlParser, k: TSqlNodeKind): PSqlNode =
|
||||
else:
|
||||
result = newNode(k)
|
||||
|
||||
proc parseParIdentList(p: var TSqlParser, father: PSqlNode) =
|
||||
proc parseParIdentList(p: var SqlParser, father: SqlNode) =
|
||||
eat(p, tkParLe)
|
||||
while true:
|
||||
expectIdent(p)
|
||||
@@ -834,7 +839,7 @@ proc parseParIdentList(p: var TSqlParser, father: PSqlNode) =
|
||||
getTok(p)
|
||||
eat(p, tkParRi)
|
||||
|
||||
proc parseTableConstraint(p: var TSqlParser): PSqlNode =
|
||||
proc parseTableConstraint(p: var SqlParser): SqlNode =
|
||||
if isKeyw(p, "primary"):
|
||||
getTok(p)
|
||||
eat(p, "key")
|
||||
@@ -861,7 +866,7 @@ proc parseTableConstraint(p: var TSqlParser): PSqlNode =
|
||||
else:
|
||||
sqlError(p, "column definition expected")
|
||||
|
||||
proc parseTableDef(p: var TSqlParser): PSqlNode =
|
||||
proc parseTableDef(p: var SqlParser): SqlNode =
|
||||
result = parseIfNotExists(p, nkCreateTable)
|
||||
expectIdent(p)
|
||||
result.add(newNode(nkIdent, p.tok.literal))
|
||||
@@ -876,7 +881,7 @@ proc parseTableDef(p: var TSqlParser): PSqlNode =
|
||||
if p.tok.kind != tkComma: break
|
||||
eat(p, tkParRi)
|
||||
|
||||
proc parseTypeDef(p: var TSqlParser): PSqlNode =
|
||||
proc parseTypeDef(p: var SqlParser): SqlNode =
|
||||
result = parseIfNotExists(p, nkCreateType)
|
||||
expectIdent(p)
|
||||
result.add(newNode(nkIdent, p.tok.literal))
|
||||
@@ -884,12 +889,12 @@ proc parseTypeDef(p: var TSqlParser): PSqlNode =
|
||||
eat(p, "as")
|
||||
result.add(parseDataType(p))
|
||||
|
||||
proc parseWhere(p: var TSqlParser): PSqlNode =
|
||||
proc parseWhere(p: var SqlParser): SqlNode =
|
||||
getTok(p)
|
||||
result = newNode(nkWhere)
|
||||
result.add(parseExpr(p))
|
||||
|
||||
proc parseIndexDef(p: var TSqlParser): PSqlNode =
|
||||
proc parseIndexDef(p: var SqlParser): SqlNode =
|
||||
result = parseIfNotExists(p, nkCreateIndex)
|
||||
if isKeyw(p, "primary"):
|
||||
getTok(p)
|
||||
@@ -914,7 +919,7 @@ proc parseIndexDef(p: var TSqlParser): PSqlNode =
|
||||
getTok(p)
|
||||
eat(p, tkParRi)
|
||||
|
||||
proc parseInsert(p: var TSqlParser): PSqlNode =
|
||||
proc parseInsert(p: var SqlParser): SqlNode =
|
||||
getTok(p)
|
||||
eat(p, "into")
|
||||
expectIdent(p)
|
||||
@@ -941,7 +946,7 @@ proc parseInsert(p: var TSqlParser): PSqlNode =
|
||||
result.add(n)
|
||||
eat(p, tkParRi)
|
||||
|
||||
proc parseUpdate(p: var TSqlParser): PSqlNode =
|
||||
proc parseUpdate(p: var SqlParser): SqlNode =
|
||||
getTok(p)
|
||||
result = newNode(nkUpdate)
|
||||
result.add(primary(p))
|
||||
@@ -962,7 +967,7 @@ proc parseUpdate(p: var TSqlParser): PSqlNode =
|
||||
else:
|
||||
result.add(nil)
|
||||
|
||||
proc parseDelete(p: var TSqlParser): PSqlNode =
|
||||
proc parseDelete(p: var SqlParser): SqlNode =
|
||||
getTok(p)
|
||||
result = newNode(nkDelete)
|
||||
eat(p, "from")
|
||||
@@ -972,7 +977,7 @@ proc parseDelete(p: var TSqlParser): PSqlNode =
|
||||
else:
|
||||
result.add(nil)
|
||||
|
||||
proc parseSelect(p: var TSqlParser): PSqlNode =
|
||||
proc parseSelect(p: var SqlParser): SqlNode =
|
||||
getTok(p)
|
||||
if isKeyw(p, "distinct"):
|
||||
getTok(p)
|
||||
@@ -1041,7 +1046,7 @@ proc parseSelect(p: var TSqlParser): PSqlNode =
|
||||
getTok(p)
|
||||
result.add(n)
|
||||
|
||||
proc parseStmt(p: var TSqlParser): PSqlNode =
|
||||
proc parseStmt(p: var SqlParser): SqlNode =
|
||||
if isKeyw(p, "create"):
|
||||
getTok(p)
|
||||
optKeyw(p, "cached")
|
||||
@@ -1071,7 +1076,7 @@ proc parseStmt(p: var TSqlParser): PSqlNode =
|
||||
else:
|
||||
sqlError(p, "CREATE expected")
|
||||
|
||||
proc open(p: var TSqlParser, input: PStream, filename: string) =
|
||||
proc open(p: var SqlParser, input: PStream, filename: string) =
|
||||
## opens the parser `p` and assigns the input stream `input` to it.
|
||||
## `filename` is only used for error messages.
|
||||
open(TSqlLexer(p), input, filename)
|
||||
@@ -1079,7 +1084,7 @@ proc open(p: var TSqlParser, input: PStream, filename: string) =
|
||||
p.tok.literal = ""
|
||||
getTok(p)
|
||||
|
||||
proc parse(p: var TSqlParser): PSqlNode =
|
||||
proc parse(p: var SqlParser): SqlNode =
|
||||
## parses the content of `p`'s input stream and returns the SQL AST.
|
||||
## Syntax errors raise an `EInvalidSql` exception.
|
||||
result = newNode(nkStmtList)
|
||||
@@ -1090,24 +1095,24 @@ proc parse(p: var TSqlParser): PSqlNode =
|
||||
if result.len == 1:
|
||||
result = result.sons[0]
|
||||
|
||||
proc close(p: var TSqlParser) =
|
||||
proc close(p: var SqlParser) =
|
||||
## closes the parser `p`. The associated input stream is closed too.
|
||||
close(TSqlLexer(p))
|
||||
|
||||
proc parseSQL*(input: PStream, filename: string): PSqlNode =
|
||||
proc parseSQL*(input: PStream, filename: string): SqlNode =
|
||||
## parses the SQL from `input` into an AST and returns the AST.
|
||||
## `filename` is only used for error messages.
|
||||
## Syntax errors raise an `EInvalidSql` exception.
|
||||
var p: TSqlParser
|
||||
var p: SqlParser
|
||||
open(p, input, filename)
|
||||
try:
|
||||
result = parse(p)
|
||||
finally:
|
||||
close(p)
|
||||
|
||||
proc ra(n: PSqlNode, s: var string, indent: int)
|
||||
proc ra(n: SqlNode, s: var string, indent: int)
|
||||
|
||||
proc rs(n: PSqlNode, s: var string, indent: int,
|
||||
proc rs(n: SqlNode, s: var string, indent: int,
|
||||
prefix = "(", suffix = ")",
|
||||
sep = ", ") =
|
||||
if n.len > 0:
|
||||
@@ -1117,7 +1122,7 @@ proc rs(n: PSqlNode, s: var string, indent: int,
|
||||
ra(n.sons[i], s, indent)
|
||||
s.add(suffix)
|
||||
|
||||
proc ra(n: PSqlNode, s: var string, indent: int) =
|
||||
proc ra(n: SqlNode, s: var string, indent: int) =
|
||||
if n == nil: return
|
||||
case n.kind
|
||||
of nkNone: discard
|
||||
@@ -1320,7 +1325,7 @@ proc ra(n: PSqlNode, s: var string, indent: int) =
|
||||
#for x, y, z in db.select(fromm = a, b where = a.name == b.name):
|
||||
# writeln x, y, z
|
||||
|
||||
proc renderSQL*(n: PSqlNode): string =
|
||||
proc renderSQL*(n: SqlNode): string =
|
||||
## Converts an SQL abstract syntax tree to its string representation.
|
||||
result = ""
|
||||
ra(n, result, 0)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2014 Dominik Picheta
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -15,12 +15,14 @@
|
||||
import strutils
|
||||
|
||||
type
|
||||
TUrl* = tuple[ ## represents a *Uniform Resource Locator* (URL)
|
||||
## any optional component is "" if it does not exist
|
||||
Url* = tuple[ ## represents a *Uniform Resource Locator* (URL)
|
||||
## any optional component is "" if it does not exist
|
||||
scheme, username, password,
|
||||
hostname, port, path, query, anchor: string]
|
||||
|
||||
proc parseUrl*(url: string): TUrl {.deprecated.} =
|
||||
|
||||
{.deprecated: [TUrl: Url].}
|
||||
|
||||
proc parseUrl*(url: string): Url {.deprecated.} =
|
||||
var i = 0
|
||||
|
||||
var scheme, username, password: string = ""
|
||||
@@ -86,7 +88,7 @@ proc parseUrl*(url: string): TUrl {.deprecated.} =
|
||||
|
||||
return (scheme, username, password, hostname, port, path, query, anchor)
|
||||
|
||||
proc `$`*(u: TUrl): string {.deprecated.} =
|
||||
proc `$`*(u: Url): string {.deprecated.} =
|
||||
## turns the URL `u` into its string representation.
|
||||
result = ""
|
||||
if u.scheme.len > 0:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -35,7 +35,7 @@ proc parseHex*(s: string, number: var int, start = 0): int {.
|
||||
## can use this feature to *chain* calls, though the result int will quickly
|
||||
## overflow. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## var value = 0
|
||||
## discard parseHex("0x38", value)
|
||||
## assert value == 56
|
||||
@@ -332,7 +332,7 @@ proc parseFloat*(s: string, number: var float, start = 0): int {.
|
||||
number = bf
|
||||
|
||||
type
|
||||
TInterpolatedKind* = enum ## describes for `interpolatedFragments`
|
||||
InterpolatedKind* = enum ## describes for `interpolatedFragments`
|
||||
## which part of the interpolated string is
|
||||
## yielded; for example in "str$$$var${expr}"
|
||||
ikStr, ## ``str`` part of the interpolated string
|
||||
@@ -340,7 +340,9 @@ type
|
||||
ikVar, ## ``var`` part of the interpolated string
|
||||
ikExpr ## ``expr`` part of the interpolated string
|
||||
|
||||
iterator interpolatedFragments*(s: string): tuple[kind: TInterpolatedKind,
|
||||
{.deprecated: [TInterpolatedKind: InterpolatedKind].}
|
||||
|
||||
iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
|
||||
value: string] =
|
||||
## Tokenizes the string `s` into substrings for interpolation purposes.
|
||||
##
|
||||
@@ -360,7 +362,7 @@ iterator interpolatedFragments*(s: string): tuple[kind: TInterpolatedKind,
|
||||
## (ikString, " ")
|
||||
## (ikDollar, "$")
|
||||
var i = 0
|
||||
var kind: TInterpolatedKind
|
||||
var kind: InterpolatedKind
|
||||
while true:
|
||||
var j = i
|
||||
if s[j] == '$':
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2010 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -56,7 +56,7 @@ import
|
||||
# xmlElementCloseEnd, ## ``/>``
|
||||
|
||||
type
|
||||
TXmlEventKind* = enum ## enumation of all events that may occur when parsing
|
||||
XmlEventKind* = enum ## enumation of all events that may occur when parsing
|
||||
xmlError, ## an error ocurred during parsing
|
||||
xmlEof, ## end of file reached
|
||||
xmlCharData, ## character data
|
||||
@@ -72,7 +72,7 @@ type
|
||||
xmlEntity, ## &entity;
|
||||
xmlSpecial ## ``<! ... data ... >``
|
||||
|
||||
TXmlError* = enum ## enumeration that lists all errors that can occur
|
||||
XmlErrorKind* = enum ## enumeration that lists all errors that can occur
|
||||
errNone, ## no error
|
||||
errEndOfCDataExpected, ## ``]]>`` expected
|
||||
errNameExpected, ## name expected
|
||||
@@ -83,23 +83,26 @@ type
|
||||
errQuoteExpected, ## ``"`` or ``'`` expected
|
||||
errEndOfCommentExpected ## ``-->`` expected
|
||||
|
||||
TParserState = enum
|
||||
ParserState = enum
|
||||
stateStart, stateNormal, stateAttr, stateEmptyElementTag, stateError
|
||||
|
||||
TXmlParseOption* = enum ## options for the XML parser
|
||||
XmlParseOption* = enum ## options for the XML parser
|
||||
reportWhitespace, ## report whitespace
|
||||
reportComments ## report comments
|
||||
|
||||
TXmlParser* = object of TBaseLexer ## the parser object.
|
||||
XmlParser* = object of TBaseLexer ## the parser object.
|
||||
a, b, c: string
|
||||
kind: TXmlEventKind
|
||||
err: TXmlError
|
||||
state: TParserState
|
||||
kind: XmlEventKind
|
||||
err: XmlErrorKind
|
||||
state: ParserState
|
||||
filename: string
|
||||
options: set[TXmlParseOption]
|
||||
|
||||
options: set[XmlParseOption]
|
||||
|
||||
{.deprecated: [TXmlParser: XmlParser, TXmlParseOptions: XmlParseOptions,
|
||||
TXmlError: XmlErrorKind, TXmlEventKind: XmlEventKind].}
|
||||
|
||||
const
|
||||
errorMessages: array [TXmlError, string] = [
|
||||
errorMessages: array[XmlError, string] = [
|
||||
"no error",
|
||||
"']]>' expected",
|
||||
"name expected",
|
||||
@@ -111,8 +114,8 @@ const
|
||||
"'-->' expected"
|
||||
]
|
||||
|
||||
proc open*(my: var TXmlParser, input: PStream, filename: string,
|
||||
options: set[TXmlParseOption] = {}) =
|
||||
proc open*(my: var XmlParser, input: Stream, filename: string,
|
||||
options: set[XmlParseOption] = {}) =
|
||||
## initializes the parser with an input stream. `Filename` is only used
|
||||
## for nice error messages. The parser's behaviour can be controlled by
|
||||
## the `options` parameter: If `options` contains ``reportWhitespace``
|
||||
@@ -127,7 +130,7 @@ proc open*(my: var TXmlParser, input: PStream, filename: string,
|
||||
my.b = ""
|
||||
my.options = options
|
||||
|
||||
proc close*(my: var TXmlParser) {.inline.} =
|
||||
proc close*(my: var XmlParser) {.inline.} =
|
||||
## closes the parser `my` and its associated input stream.
|
||||
lexbase.close(my)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -32,7 +32,7 @@ const
|
||||
## can be captured. More subpatterns cannot be captured!
|
||||
|
||||
type
|
||||
TPegKind = enum
|
||||
PegKind = enum
|
||||
pkEmpty,
|
||||
pkAny, ## any character (.)
|
||||
pkAnyRune, ## any Unicode character (_)
|
||||
@@ -67,16 +67,16 @@ type
|
||||
pkRule, ## a <- b
|
||||
pkList, ## a, b
|
||||
pkStartAnchor ## ^ --> Internal DSL: startAnchor()
|
||||
TNonTerminalFlag = enum
|
||||
NonTerminalFlag = enum
|
||||
ntDeclared, ntUsed
|
||||
TNonTerminal {.final.} = object ## represents a non terminal symbol
|
||||
NonTerminalObj = object ## represents a non terminal symbol
|
||||
name: string ## the name of the symbol
|
||||
line: int ## line the symbol has been declared/used in
|
||||
col: int ## column the symbol has been declared/used in
|
||||
flags: set[TNonTerminalFlag] ## the nonterminal's flags
|
||||
flags: set[NonTerminalFlag] ## the nonterminal's flags
|
||||
rule: TNode ## the rule that the symbol refers to
|
||||
TNode {.final, shallow.} = object
|
||||
case kind: TPegKind
|
||||
TNode {.shallow.} = object
|
||||
case kind: PegKind
|
||||
of pkEmpty..pkWhitespace: nil
|
||||
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string
|
||||
of pkChar, pkGreedyRepChar: ch: char
|
||||
@@ -84,11 +84,13 @@ type
|
||||
of pkNonTerminal: nt: PNonTerminal
|
||||
of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns]
|
||||
else: sons: seq[TNode]
|
||||
PNonTerminal* = ref TNonTerminal
|
||||
NonTerminal* = ref NonTerminalObj
|
||||
|
||||
TPeg* = TNode ## type that represents a PEG
|
||||
Peg* = TNode ## type that represents a PEG
|
||||
|
||||
proc term*(t: string): TPeg {.nosideEffect, rtl, extern: "npegs$1Str".} =
|
||||
{.deprecated: [TPeg: Peg].}
|
||||
|
||||
proc term*(t: string): Peg {.nosideEffect, rtl, extern: "npegs$1Str".} =
|
||||
## constructs a PEG from a terminal string
|
||||
if t.len != 1:
|
||||
result.kind = pkTerminal
|
||||
@@ -97,35 +99,35 @@ proc term*(t: string): TPeg {.nosideEffect, rtl, extern: "npegs$1Str".} =
|
||||
result.kind = pkChar
|
||||
result.ch = t[0]
|
||||
|
||||
proc termIgnoreCase*(t: string): TPeg {.
|
||||
proc termIgnoreCase*(t: string): Peg {.
|
||||
nosideEffect, rtl, extern: "npegs$1".} =
|
||||
## constructs a PEG from a terminal string; ignore case for matching
|
||||
result.kind = pkTerminalIgnoreCase
|
||||
result.term = t
|
||||
|
||||
proc termIgnoreStyle*(t: string): TPeg {.
|
||||
proc termIgnoreStyle*(t: string): Peg {.
|
||||
nosideEffect, rtl, extern: "npegs$1".} =
|
||||
## constructs a PEG from a terminal string; ignore style for matching
|
||||
result.kind = pkTerminalIgnoreStyle
|
||||
result.term = t
|
||||
|
||||
proc term*(t: char): TPeg {.nosideEffect, rtl, extern: "npegs$1Char".} =
|
||||
proc term*(t: char): Peg {.nosideEffect, rtl, extern: "npegs$1Char".} =
|
||||
## constructs a PEG from a terminal char
|
||||
assert t != '\0'
|
||||
result.kind = pkChar
|
||||
result.ch = t
|
||||
|
||||
proc charSet*(s: set[char]): TPeg {.nosideEffect, rtl, extern: "npegs$1".} =
|
||||
proc charSet*(s: set[char]): Peg {.nosideEffect, rtl, extern: "npegs$1".} =
|
||||
## constructs a PEG from a character set `s`
|
||||
assert '\0' notin s
|
||||
result.kind = pkCharChoice
|
||||
new(result.charChoice)
|
||||
result.charChoice[] = s
|
||||
|
||||
proc len(a: TPeg): int {.inline.} = return a.sons.len
|
||||
proc add(d: var TPeg, s: TPeg) {.inline.} = add(d.sons, s)
|
||||
proc len(a: Peg): int {.inline.} = return a.sons.len
|
||||
proc add(d: var Peg, s: Peg) {.inline.} = add(d.sons, s)
|
||||
|
||||
proc addChoice(dest: var TPeg, elem: TPeg) =
|
||||
proc addChoice(dest: var Peg, elem: Peg) =
|
||||
var L = dest.len-1
|
||||
if L >= 0 and dest.sons[L].kind == pkCharChoice:
|
||||
# caution! Do not introduce false aliasing here!
|
||||
@@ -137,7 +139,7 @@ proc addChoice(dest: var TPeg, elem: TPeg) =
|
||||
else: add(dest, elem)
|
||||
else: add(dest, elem)
|
||||
|
||||
template multipleOp(k: TPegKind, localOpt: expr) =
|
||||
template multipleOp(k: PegKind, localOpt: expr) =
|
||||
result.kind = k
|
||||
result.sons = @[]
|
||||
for x in items(a):
|
||||
@@ -149,12 +151,12 @@ template multipleOp(k: TPegKind, localOpt: expr) =
|
||||
if result.len == 1:
|
||||
result = result.sons[0]
|
||||
|
||||
proc `/`*(a: varargs[TPeg]): TPeg {.
|
||||
proc `/`*(a: varargs[Peg]): Peg {.
|
||||
nosideEffect, rtl, extern: "npegsOrderedChoice".} =
|
||||
## constructs an ordered choice with the PEGs in `a`
|
||||
multipleOp(pkOrderedChoice, addChoice)
|
||||
|
||||
proc addSequence(dest: var TPeg, elem: TPeg) =
|
||||
proc addSequence(dest: var Peg, elem: Peg) =
|
||||
var L = dest.len-1
|
||||
if L >= 0 and dest.sons[L].kind == pkTerminal:
|
||||
# caution! Do not introduce false aliasing here!
|
||||
@@ -166,12 +168,12 @@ proc addSequence(dest: var TPeg, elem: TPeg) =
|
||||
else: add(dest, elem)
|
||||
else: add(dest, elem)
|
||||
|
||||
proc sequence*(a: varargs[TPeg]): TPeg {.
|
||||
proc sequence*(a: varargs[Peg]): Peg {.
|
||||
nosideEffect, rtl, extern: "npegs$1".} =
|
||||
## constructs a sequence with all the PEGs from `a`
|
||||
multipleOp(pkSequence, addSequence)
|
||||
|
||||
proc `?`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsOptional".} =
|
||||
proc `?`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsOptional".} =
|
||||
## constructs an optional for the PEG `a`
|
||||
if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar,
|
||||
pkGreedyRepSet}:
|
||||
@@ -182,7 +184,7 @@ proc `?`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsOptional".} =
|
||||
result.kind = pkOption
|
||||
result.sons = @[a]
|
||||
|
||||
proc `*`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsGreedyRep".} =
|
||||
proc `*`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsGreedyRep".} =
|
||||
## constructs a "greedy repetition" for the PEG `a`
|
||||
case a.kind
|
||||
of pkGreedyRep, pkGreedyRepChar, pkGreedyRepSet, pkGreedyAny, pkOption:
|
||||
@@ -200,111 +202,99 @@ proc `*`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsGreedyRep".} =
|
||||
result.kind = pkGreedyRep
|
||||
result.sons = @[a]
|
||||
|
||||
proc `!*`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsSearch".} =
|
||||
proc `!*`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsSearch".} =
|
||||
## constructs a "search" for the PEG `a`
|
||||
result.kind = pkSearch
|
||||
result.sons = @[a]
|
||||
|
||||
proc `!*\`*(a: TPeg): TPeg {.noSideEffect, rtl,
|
||||
proc `!*\`*(a: Peg): Peg {.noSideEffect, rtl,
|
||||
extern: "npgegsCapturedSearch".} =
|
||||
## constructs a "captured search" for the PEG `a`
|
||||
result.kind = pkCapturedSearch
|
||||
result.sons = @[a]
|
||||
|
||||
when false:
|
||||
proc contains(a: TPeg, k: TPegKind): bool =
|
||||
if a.kind == k: return true
|
||||
case a.kind
|
||||
of pkEmpty, pkAny, pkAnyRune, pkGreedyAny, pkNewLine, pkTerminal,
|
||||
pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, pkGreedyRepChar,
|
||||
pkCharChoice, pkGreedyRepSet: nil
|
||||
of pkNonTerminal: return true
|
||||
else:
|
||||
for i in 0..a.sons.len-1:
|
||||
if contains(a.sons[i], k): return true
|
||||
|
||||
proc `+`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsGreedyPosRep".} =
|
||||
proc `+`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsGreedyPosRep".} =
|
||||
## constructs a "greedy positive repetition" with the PEG `a`
|
||||
return sequence(a, *a)
|
||||
|
||||
proc `&`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsAndPredicate".} =
|
||||
proc `&`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsAndPredicate".} =
|
||||
## constructs an "and predicate" with the PEG `a`
|
||||
result.kind = pkAndPredicate
|
||||
result.sons = @[a]
|
||||
|
||||
proc `!`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsNotPredicate".} =
|
||||
proc `!`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsNotPredicate".} =
|
||||
## constructs a "not predicate" with the PEG `a`
|
||||
result.kind = pkNotPredicate
|
||||
result.sons = @[a]
|
||||
|
||||
proc any*: TPeg {.inline.} =
|
||||
proc any*: Peg {.inline.} =
|
||||
## constructs the PEG `any character`:idx: (``.``)
|
||||
result.kind = pkAny
|
||||
|
||||
proc anyRune*: TPeg {.inline.} =
|
||||
proc anyRune*: Peg {.inline.} =
|
||||
## constructs the PEG `any rune`:idx: (``_``)
|
||||
result.kind = pkAnyRune
|
||||
|
||||
proc newLine*: TPeg {.inline.} =
|
||||
proc newLine*: Peg {.inline.} =
|
||||
## constructs the PEG `newline`:idx: (``\n``)
|
||||
result.kind = pkNewline
|
||||
|
||||
proc unicodeLetter*: TPeg {.inline.} =
|
||||
proc unicodeLetter*: Peg {.inline.} =
|
||||
## constructs the PEG ``\letter`` which matches any Unicode letter.
|
||||
result.kind = pkLetter
|
||||
|
||||
proc unicodeLower*: TPeg {.inline.} =
|
||||
proc unicodeLower*: Peg {.inline.} =
|
||||
## constructs the PEG ``\lower`` which matches any Unicode lowercase letter.
|
||||
result.kind = pkLower
|
||||
|
||||
proc unicodeUpper*: TPeg {.inline.} =
|
||||
proc unicodeUpper*: Peg {.inline.} =
|
||||
## constructs the PEG ``\upper`` which matches any Unicode uppercase letter.
|
||||
result.kind = pkUpper
|
||||
|
||||
proc unicodeTitle*: TPeg {.inline.} =
|
||||
proc unicodeTitle*: Peg {.inline.} =
|
||||
## constructs the PEG ``\title`` which matches any Unicode title letter.
|
||||
result.kind = pkTitle
|
||||
|
||||
proc unicodeWhitespace*: TPeg {.inline.} =
|
||||
proc unicodeWhitespace*: Peg {.inline.} =
|
||||
## constructs the PEG ``\white`` which matches any Unicode
|
||||
## whitespace character.
|
||||
result.kind = pkWhitespace
|
||||
|
||||
proc startAnchor*: TPeg {.inline.} =
|
||||
proc startAnchor*: Peg {.inline.} =
|
||||
## constructs the PEG ``^`` which matches the start of the input.
|
||||
result.kind = pkStartAnchor
|
||||
|
||||
proc endAnchor*: TPeg {.inline.} =
|
||||
proc endAnchor*: Peg {.inline.} =
|
||||
## constructs the PEG ``$`` which matches the end of the input.
|
||||
result = !any()
|
||||
|
||||
proc capture*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsCapture".} =
|
||||
proc capture*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsCapture".} =
|
||||
## constructs a capture with the PEG `a`
|
||||
result.kind = pkCapture
|
||||
result.sons = @[a]
|
||||
|
||||
proc backref*(index: range[1..MaxSubPatterns]): TPeg {.
|
||||
proc backref*(index: range[1..MaxSubPatterns]): Peg {.
|
||||
nosideEffect, rtl, extern: "npegs$1".} =
|
||||
## constructs a back reference of the given `index`. `index` starts counting
|
||||
## from 1.
|
||||
result.kind = pkBackRef
|
||||
result.index = index-1
|
||||
|
||||
proc backrefIgnoreCase*(index: range[1..MaxSubPatterns]): TPeg {.
|
||||
proc backrefIgnoreCase*(index: range[1..MaxSubPatterns]): Peg {.
|
||||
nosideEffect, rtl, extern: "npegs$1".} =
|
||||
## constructs a back reference of the given `index`. `index` starts counting
|
||||
## from 1. Ignores case for matching.
|
||||
result.kind = pkBackRefIgnoreCase
|
||||
result.index = index-1
|
||||
|
||||
proc backrefIgnoreStyle*(index: range[1..MaxSubPatterns]): TPeg {.
|
||||
proc backrefIgnoreStyle*(index: range[1..MaxSubPatterns]): Peg {.
|
||||
nosideEffect, rtl, extern: "npegs$1".}=
|
||||
## constructs a back reference of the given `index`. `index` starts counting
|
||||
## from 1. Ignores style for matching.
|
||||
result.kind = pkBackRefIgnoreStyle
|
||||
result.index = index-1
|
||||
|
||||
proc spaceCost(n: TPeg): int =
|
||||
proc spaceCost(n: Peg): int =
|
||||
case n.kind
|
||||
of pkEmpty: discard
|
||||
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar,
|
||||
@@ -319,7 +309,7 @@ proc spaceCost(n: TPeg): int =
|
||||
inc(result, spaceCost(n.sons[i]))
|
||||
if result >= InlineThreshold: break
|
||||
|
||||
proc nonterminal*(n: PNonTerminal): TPeg {.
|
||||
proc nonterminal*(n: PNonTerminal): Peg {.
|
||||
nosideEffect, rtl, extern: "npegs$1".} =
|
||||
## constructs a PEG that consists of the nonterminal symbol
|
||||
assert n != nil
|
||||
@@ -415,7 +405,7 @@ proc charSetEsc(cc: set[char]): string =
|
||||
else:
|
||||
result = '[' & charSetEscAux(cc) & ']'
|
||||
|
||||
proc toStrAux(r: TPeg, res: var string) =
|
||||
proc toStrAux(r: Peg, res: var string) =
|
||||
case r.kind
|
||||
of pkEmpty: add(res, "()")
|
||||
of pkAny: add(res, '.')
|
||||
@@ -501,7 +491,7 @@ proc toStrAux(r: TPeg, res: var string) =
|
||||
of pkStartAnchor:
|
||||
add(res, '^')
|
||||
|
||||
proc `$` *(r: TPeg): string {.nosideEffect, rtl, extern: "npegsToString".} =
|
||||
proc `$` *(r: Peg): string {.nosideEffect, rtl, extern: "npegsToString".} =
|
||||
## converts a PEG to its string representation
|
||||
result = ""
|
||||
toStrAux(r, result)
|
||||
@@ -509,12 +499,14 @@ proc `$` *(r: TPeg): string {.nosideEffect, rtl, extern: "npegsToString".} =
|
||||
# --------------------- core engine -------------------------------------------
|
||||
|
||||
type
|
||||
TCaptures* {.final.} = object ## contains the captured substrings.
|
||||
Captures* = object ## contains the captured substrings.
|
||||
matches: array[0..maxSubpatterns-1, tuple[first, last: int]]
|
||||
ml: int
|
||||
origStart: int
|
||||
|
||||
proc bounds*(c: TCaptures,
|
||||
{.deprecated: [TCaptures: Captures].}
|
||||
|
||||
proc bounds*(c: Captures,
|
||||
i: range[0..maxSubpatterns-1]): tuple[first, last: int] =
|
||||
## returns the bounds ``[first..last]`` of the `i`'th capture.
|
||||
result = c.matches[i]
|
||||
@@ -533,7 +525,7 @@ when not useUnicode:
|
||||
proc isTitle(a: char): bool {.inline.} = return false
|
||||
proc isWhiteSpace(a: char): bool {.inline.} = return a in {' ', '\9'..'\13'}
|
||||
|
||||
proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {.
|
||||
proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
|
||||
nosideEffect, rtl, extern: "npegs$1".} =
|
||||
## low-level matching proc that implements the PEG interpreter. Use this
|
||||
## for maximum efficiency (every other PEG operation ends up calling this
|
||||
@@ -734,7 +726,7 @@ proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {.
|
||||
of pkBackRef..pkBackRefIgnoreStyle:
|
||||
if p.index >= c.ml: return -1
|
||||
var (a, b) = c.matches[p.index]
|
||||
var n: TPeg
|
||||
var n: Peg
|
||||
n.kind = succ(pkTerminal, ord(p.kind)-ord(pkBackRef))
|
||||
n.term = s.substr(a, b)
|
||||
result = rawMatch(s, n, start, c)
|
||||
@@ -747,51 +739,51 @@ template fillMatches(s, caps, c: expr) =
|
||||
for k in 0..c.ml-1:
|
||||
caps[k] = substr(s, c.matches[k][0], c.matches[k][1])
|
||||
|
||||
proc match*(s: string, pattern: TPeg, matches: var openArray[string],
|
||||
proc match*(s: string, pattern: Peg, matches: var openArray[string],
|
||||
start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} =
|
||||
## returns ``true`` if ``s[start..]`` matches the ``pattern`` and
|
||||
## the captured substrings in the array ``matches``. If it does not
|
||||
## match, nothing is written into ``matches`` and ``false`` is
|
||||
## returned.
|
||||
var c: TCaptures
|
||||
var c: Captures
|
||||
c.origStart = start
|
||||
result = rawMatch(s, pattern, start, c) == len(s) - start
|
||||
if result: fillMatches(s, matches, c)
|
||||
|
||||
proc match*(s: string, pattern: TPeg,
|
||||
proc match*(s: string, pattern: Peg,
|
||||
start = 0): bool {.nosideEffect, rtl, extern: "npegs$1".} =
|
||||
## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``.
|
||||
var c: TCaptures
|
||||
var c: Captures
|
||||
c.origStart = start
|
||||
result = rawMatch(s, pattern, start, c) == len(s)-start
|
||||
|
||||
proc matchLen*(s: string, pattern: TPeg, matches: var openArray[string],
|
||||
proc matchLen*(s: string, pattern: Peg, matches: var openArray[string],
|
||||
start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
|
||||
## the same as ``match``, but it returns the length of the match,
|
||||
## if there is no match, -1 is returned. Note that a match length
|
||||
## of zero can happen. It's possible that a suffix of `s` remains
|
||||
## that does not belong to the match.
|
||||
var c: TCaptures
|
||||
var c: Captures
|
||||
c.origStart = start
|
||||
result = rawMatch(s, pattern, start, c)
|
||||
if result >= 0: fillMatches(s, matches, c)
|
||||
|
||||
proc matchLen*(s: string, pattern: TPeg,
|
||||
proc matchLen*(s: string, pattern: Peg,
|
||||
start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} =
|
||||
## the same as ``match``, but it returns the length of the match,
|
||||
## if there is no match, -1 is returned. Note that a match length
|
||||
## of zero can happen. It's possible that a suffix of `s` remains
|
||||
## that does not belong to the match.
|
||||
var c: TCaptures
|
||||
var c: Captures
|
||||
c.origStart = start
|
||||
result = rawMatch(s, pattern, start, c)
|
||||
|
||||
proc find*(s: string, pattern: TPeg, matches: var openArray[string],
|
||||
proc find*(s: string, pattern: Peg, matches: var openArray[string],
|
||||
start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
|
||||
## returns the starting position of ``pattern`` in ``s`` and the captured
|
||||
## substrings in the array ``matches``. If it does not match, nothing
|
||||
## is written into ``matches`` and -1 is returned.
|
||||
var c: TCaptures
|
||||
var c: Captures
|
||||
c.origStart = start
|
||||
for i in start .. s.len-1:
|
||||
c.ml = 0
|
||||
@@ -801,14 +793,14 @@ proc find*(s: string, pattern: TPeg, matches: var openArray[string],
|
||||
return -1
|
||||
# could also use the pattern here: (!P .)* P
|
||||
|
||||
proc findBounds*(s: string, pattern: TPeg, matches: var openArray[string],
|
||||
proc findBounds*(s: string, pattern: Peg, matches: var openArray[string],
|
||||
start = 0): tuple[first, last: int] {.
|
||||
nosideEffect, rtl, extern: "npegs$1Capture".} =
|
||||
## returns the starting position and end position of ``pattern`` in ``s``
|
||||
## and the captured
|
||||
## substrings in the array ``matches``. If it does not match, nothing
|
||||
## is written into ``matches`` and (-1,0) is returned.
|
||||
var c: TCaptures
|
||||
var c: Captures
|
||||
c.origStart = start
|
||||
for i in start .. s.len-1:
|
||||
c.ml = 0
|
||||
@@ -818,19 +810,19 @@ proc findBounds*(s: string, pattern: TPeg, matches: var openArray[string],
|
||||
return (i, i+L-1)
|
||||
return (-1, 0)
|
||||
|
||||
proc find*(s: string, pattern: TPeg,
|
||||
proc find*(s: string, pattern: Peg,
|
||||
start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} =
|
||||
## returns the starting position of ``pattern`` in ``s``. If it does not
|
||||
## match, -1 is returned.
|
||||
var c: TCaptures
|
||||
var c: Captures
|
||||
c.origStart = start
|
||||
for i in start .. s.len-1:
|
||||
if rawMatch(s, pattern, i, c) >= 0: return i
|
||||
return -1
|
||||
|
||||
iterator findAll*(s: string, pattern: TPeg, start = 0): string =
|
||||
iterator findAll*(s: string, pattern: Peg, start = 0): string =
|
||||
## yields all matching *substrings* of `s` that match `pattern`.
|
||||
var c: TCaptures
|
||||
var c: Captures
|
||||
c.origStart = start
|
||||
var i = start
|
||||
while i < s.len:
|
||||
@@ -842,7 +834,7 @@ iterator findAll*(s: string, pattern: TPeg, start = 0): string =
|
||||
yield substr(s, i, i+L-1)
|
||||
inc(i, L)
|
||||
|
||||
proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {.
|
||||
proc findAll*(s: string, pattern: Peg, start = 0): seq[string] {.
|
||||
nosideEffect, rtl, extern: "npegs$1".} =
|
||||
## returns all matching *substrings* of `s` that match `pattern`.
|
||||
## If it does not match, @[] is returned.
|
||||
@@ -851,11 +843,11 @@ proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {.
|
||||
when not defined(nimhygiene):
|
||||
{.pragma: inject.}
|
||||
|
||||
template `=~`*(s: string, pattern: TPeg): bool =
|
||||
template `=~`*(s: string, pattern: Peg): bool =
|
||||
## This calls ``match`` with an implicit declared ``matches`` array that
|
||||
## can be used in the scope of the ``=~`` call:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## if line =~ peg"\s* {\w+} \s* '=' \s* {\w+}":
|
||||
## # matches a key=value pair:
|
||||
@@ -876,46 +868,46 @@ template `=~`*(s: string, pattern: TPeg): bool =
|
||||
|
||||
# ------------------------- more string handling ------------------------------
|
||||
|
||||
proc contains*(s: string, pattern: TPeg, start = 0): bool {.
|
||||
proc contains*(s: string, pattern: Peg, start = 0): bool {.
|
||||
nosideEffect, rtl, extern: "npegs$1".} =
|
||||
## same as ``find(s, pattern, start) >= 0``
|
||||
return find(s, pattern, start) >= 0
|
||||
|
||||
proc contains*(s: string, pattern: TPeg, matches: var openArray[string],
|
||||
proc contains*(s: string, pattern: Peg, matches: var openArray[string],
|
||||
start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} =
|
||||
## same as ``find(s, pattern, matches, start) >= 0``
|
||||
return find(s, pattern, matches, start) >= 0
|
||||
|
||||
proc startsWith*(s: string, prefix: TPeg, start = 0): bool {.
|
||||
proc startsWith*(s: string, prefix: Peg, start = 0): bool {.
|
||||
nosideEffect, rtl, extern: "npegs$1".} =
|
||||
## returns true if `s` starts with the pattern `prefix`
|
||||
result = matchLen(s, prefix, start) >= 0
|
||||
|
||||
proc endsWith*(s: string, suffix: TPeg, start = 0): bool {.
|
||||
proc endsWith*(s: string, suffix: Peg, start = 0): bool {.
|
||||
nosideEffect, rtl, extern: "npegs$1".} =
|
||||
## returns true if `s` ends with the pattern `prefix`
|
||||
var c: TCaptures
|
||||
var c: Captures
|
||||
c.origStart = start
|
||||
for i in start .. s.len-1:
|
||||
if rawMatch(s, suffix, i, c) == s.len - i: return true
|
||||
|
||||
proc replacef*(s: string, sub: TPeg, by: string): string {.
|
||||
proc replacef*(s: string, sub: Peg, by: string): string {.
|
||||
nosideEffect, rtl, extern: "npegs$1".} =
|
||||
## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by`
|
||||
## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## "var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2")
|
||||
##
|
||||
## Results in:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## "var1<-keykey; val2<-key2key2"
|
||||
result = ""
|
||||
var i = 0
|
||||
var caps: array[0..maxSubpatterns-1, string]
|
||||
var c: TCaptures
|
||||
var c: Captures
|
||||
while i < s.len:
|
||||
c.ml = 0
|
||||
var x = rawMatch(s, sub, i, c)
|
||||
@@ -928,13 +920,13 @@ proc replacef*(s: string, sub: TPeg, by: string): string {.
|
||||
inc(i, x)
|
||||
add(result, substr(s, i))
|
||||
|
||||
proc replace*(s: string, sub: TPeg, by = ""): string {.
|
||||
proc replace*(s: string, sub: Peg, by = ""): string {.
|
||||
nosideEffect, rtl, extern: "npegs$1".} =
|
||||
## Replaces `sub` in `s` by the string `by`. Captures cannot be accessed
|
||||
## in `by`.
|
||||
result = ""
|
||||
var i = 0
|
||||
var c: TCaptures
|
||||
var c: Captures
|
||||
while i < s.len:
|
||||
var x = rawMatch(s, sub, i, c)
|
||||
if x <= 0:
|
||||
@@ -946,13 +938,13 @@ proc replace*(s: string, sub: TPeg, by = ""): string {.
|
||||
add(result, substr(s, i))
|
||||
|
||||
proc parallelReplace*(s: string, subs: varargs[
|
||||
tuple[pattern: TPeg, repl: string]]): string {.
|
||||
tuple[pattern: Peg, repl: string]]): string {.
|
||||
nosideEffect, rtl, extern: "npegs$1".} =
|
||||
## Returns a modified copy of `s` with the substitutions in `subs`
|
||||
## applied in parallel.
|
||||
result = ""
|
||||
var i = 0
|
||||
var c: TCaptures
|
||||
var c: Captures
|
||||
var caps: array[0..maxSubpatterns-1, string]
|
||||
while i < s.len:
|
||||
block searchSubs:
|
||||
@@ -970,7 +962,7 @@ proc parallelReplace*(s: string, subs: varargs[
|
||||
add(result, substr(s, i))
|
||||
|
||||
proc transformFile*(infile, outfile: string,
|
||||
subs: varargs[tuple[pattern: TPeg, repl: string]]) {.
|
||||
subs: varargs[tuple[pattern: Peg, repl: string]]) {.
|
||||
rtl, extern: "npegs$1".} =
|
||||
## reads in the file `infile`, performs a parallel replacement (calls
|
||||
## `parallelReplace`) and writes back to `outfile`. Raises ``EIO`` if an
|
||||
@@ -978,25 +970,25 @@ proc transformFile*(infile, outfile: string,
|
||||
var x = readFile(infile).string
|
||||
writeFile(outfile, x.parallelReplace(subs))
|
||||
|
||||
iterator split*(s: string, sep: TPeg): string =
|
||||
iterator split*(s: string, sep: Peg): string =
|
||||
## Splits the string `s` into substrings.
|
||||
##
|
||||
## Substrings are separated by the PEG `sep`.
|
||||
## Examples:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## for word in split("00232this02939is39an22example111", peg"\d+"):
|
||||
## writeln(stdout, word)
|
||||
##
|
||||
## Results in:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## "this"
|
||||
## "is"
|
||||
## "an"
|
||||
## "example"
|
||||
##
|
||||
var c: TCaptures
|
||||
var c: Captures
|
||||
var
|
||||
first = 0
|
||||
last = 0
|
||||
@@ -1013,7 +1005,7 @@ iterator split*(s: string, sep: TPeg): string =
|
||||
if first < last:
|
||||
yield substr(s, first, last-1)
|
||||
|
||||
proc split*(s: string, sep: TPeg): seq[string] {.
|
||||
proc split*(s: string, sep: Peg): seq[string] {.
|
||||
nosideEffect, rtl, extern: "npegs$1".} =
|
||||
## Splits the string `s` into substrings.
|
||||
accumulateResult(split(s, sep))
|
||||
@@ -1060,7 +1052,7 @@ type
|
||||
charset: set[char] ## if kind == tkCharSet
|
||||
index: int ## if kind == tkBackref
|
||||
|
||||
TPegLexer {.inheritable.} = object ## the lexer object.
|
||||
PegLexer {.inheritable.} = object ## the lexer object.
|
||||
bufpos: int ## the current position within the buffer
|
||||
buf: cstring ## the buffer itself
|
||||
LineNumber: int ## the current line number
|
||||
@@ -1076,20 +1068,20 @@ const
|
||||
"@", "built-in", "escaped", "$", "$", "^"
|
||||
]
|
||||
|
||||
proc handleCR(L: var TPegLexer, pos: int): int =
|
||||
proc handleCR(L: var PegLexer, pos: int): int =
|
||||
assert(L.buf[pos] == '\c')
|
||||
inc(L.linenumber)
|
||||
result = pos+1
|
||||
if L.buf[result] == '\L': inc(result)
|
||||
L.lineStart = result
|
||||
|
||||
proc handleLF(L: var TPegLexer, pos: int): int =
|
||||
proc handleLF(L: var PegLexer, pos: int): int =
|
||||
assert(L.buf[pos] == '\L')
|
||||
inc(L.linenumber)
|
||||
result = pos+1
|
||||
L.lineStart = result
|
||||
|
||||
proc init(L: var TPegLexer, input, filename: string, line = 1, col = 0) =
|
||||
proc init(L: var PegLexer, input, filename: string, line = 1, col = 0) =
|
||||
L.buf = input
|
||||
L.bufpos = 0
|
||||
L.lineNumber = line
|
||||
@@ -1097,18 +1089,18 @@ proc init(L: var TPegLexer, input, filename: string, line = 1, col = 0) =
|
||||
L.lineStart = 0
|
||||
L.filename = filename
|
||||
|
||||
proc getColumn(L: TPegLexer): int {.inline.} =
|
||||
proc getColumn(L: PegLexer): int {.inline.} =
|
||||
result = abs(L.bufpos - L.lineStart) + L.colOffset
|
||||
|
||||
proc getLine(L: TPegLexer): int {.inline.} =
|
||||
proc getLine(L: PegLexer): int {.inline.} =
|
||||
result = L.linenumber
|
||||
|
||||
proc errorStr(L: TPegLexer, msg: string, line = -1, col = -1): string =
|
||||
proc errorStr(L: PegLexer, msg: string, line = -1, col = -1): string =
|
||||
var line = if line < 0: getLine(L) else: line
|
||||
var col = if col < 0: getColumn(L) else: col
|
||||
result = "$1($2, $3) Error: $4" % [L.filename, $line, $col, msg]
|
||||
|
||||
proc handleHexChar(c: var TPegLexer, xi: var int) =
|
||||
proc handleHexChar(c: var PegLexer, xi: var int) =
|
||||
case c.buf[c.bufpos]
|
||||
of '0'..'9':
|
||||
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0'))
|
||||
@@ -1121,7 +1113,7 @@ proc handleHexChar(c: var TPegLexer, xi: var int) =
|
||||
inc(c.bufpos)
|
||||
else: discard
|
||||
|
||||
proc getEscapedChar(c: var TPegLexer, tok: var TToken) =
|
||||
proc getEscapedChar(c: var PegLexer, tok: var TToken) =
|
||||
inc(c.bufpos)
|
||||
case c.buf[c.bufpos]
|
||||
of 'r', 'R', 'c', 'C':
|
||||
@@ -1173,7 +1165,7 @@ proc getEscapedChar(c: var TPegLexer, tok: var TToken) =
|
||||
add(tok.literal, c.buf[c.bufpos])
|
||||
inc(c.bufpos)
|
||||
|
||||
proc skip(c: var TPegLexer) =
|
||||
proc skip(c: var PegLexer) =
|
||||
var pos = c.bufpos
|
||||
var buf = c.buf
|
||||
while true:
|
||||
@@ -1192,7 +1184,7 @@ proc skip(c: var TPegLexer) =
|
||||
break # EndOfFile also leaves the loop
|
||||
c.bufpos = pos
|
||||
|
||||
proc getString(c: var TPegLexer, tok: var TToken) =
|
||||
proc getString(c: var PegLexer, tok: var TToken) =
|
||||
tok.kind = tkStringLit
|
||||
var pos = c.bufPos + 1
|
||||
var buf = c.buf
|
||||
@@ -1214,7 +1206,7 @@ proc getString(c: var TPegLexer, tok: var TToken) =
|
||||
inc(pos)
|
||||
c.bufpos = pos
|
||||
|
||||
proc getDollar(c: var TPegLexer, tok: var TToken) =
|
||||
proc getDollar(c: var PegLexer, tok: var TToken) =
|
||||
var pos = c.bufPos + 1
|
||||
var buf = c.buf
|
||||
if buf[pos] in {'0'..'9'}:
|
||||
@@ -1227,7 +1219,7 @@ proc getDollar(c: var TPegLexer, tok: var TToken) =
|
||||
tok.kind = tkDollar
|
||||
c.bufpos = pos
|
||||
|
||||
proc getCharSet(c: var TPegLexer, tok: var TToken) =
|
||||
proc getCharSet(c: var PegLexer, tok: var TToken) =
|
||||
tok.kind = tkCharSet
|
||||
tok.charset = {}
|
||||
var pos = c.bufPos + 1
|
||||
@@ -1278,7 +1270,7 @@ proc getCharSet(c: var TPegLexer, tok: var TToken) =
|
||||
c.bufpos = pos
|
||||
if caret: tok.charset = {'\1'..'\xFF'} - tok.charset
|
||||
|
||||
proc getSymbol(c: var TPegLexer, tok: var TToken) =
|
||||
proc getSymbol(c: var PegLexer, tok: var TToken) =
|
||||
var pos = c.bufpos
|
||||
var buf = c.buf
|
||||
while true:
|
||||
@@ -1288,7 +1280,7 @@ proc getSymbol(c: var TPegLexer, tok: var TToken) =
|
||||
c.bufpos = pos
|
||||
tok.kind = tkIdentifier
|
||||
|
||||
proc getBuiltin(c: var TPegLexer, tok: var TToken) =
|
||||
proc getBuiltin(c: var PegLexer, tok: var TToken) =
|
||||
if c.buf[c.bufpos+1] in strutils.Letters:
|
||||
inc(c.bufpos)
|
||||
getSymbol(c, tok)
|
||||
@@ -1297,7 +1289,7 @@ proc getBuiltin(c: var TPegLexer, tok: var TToken) =
|
||||
tok.kind = tkEscaped
|
||||
getEscapedChar(c, tok) # may set tok.kind to tkInvalid
|
||||
|
||||
proc getTok(c: var TPegLexer, tok: var TToken) =
|
||||
proc getTok(c: var PegLexer, tok: var TToken) =
|
||||
tok.kind = tkInvalid
|
||||
tok.modifier = modNone
|
||||
setLen(tok.literal, 0)
|
||||
@@ -1403,7 +1395,7 @@ proc getTok(c: var TPegLexer, tok: var TToken) =
|
||||
add(tok.literal, c.buf[c.bufpos])
|
||||
inc(c.bufpos)
|
||||
|
||||
proc arrowIsNextTok(c: TPegLexer): bool =
|
||||
proc arrowIsNextTok(c: PegLexer): bool =
|
||||
# the only look ahead we need
|
||||
var pos = c.bufpos
|
||||
while c.buf[pos] in {'\t', ' '}: inc(pos)
|
||||
@@ -1414,31 +1406,31 @@ proc arrowIsNextTok(c: TPegLexer): bool =
|
||||
type
|
||||
EInvalidPeg* = object of EInvalidValue ## raised if an invalid
|
||||
## PEG has been detected
|
||||
TPegParser = object of TPegLexer ## the PEG parser object
|
||||
PegParser = object of PegLexer ## the PEG parser object
|
||||
tok: TToken
|
||||
nonterms: seq[PNonTerminal]
|
||||
modifier: TModifier
|
||||
captures: int
|
||||
identIsVerbatim: bool
|
||||
skip: TPeg
|
||||
skip: Peg
|
||||
|
||||
proc pegError(p: TPegParser, msg: string, line = -1, col = -1) =
|
||||
proc pegError(p: PegParser, msg: string, line = -1, col = -1) =
|
||||
var e: ref EInvalidPeg
|
||||
new(e)
|
||||
e.msg = errorStr(p, msg, line, col)
|
||||
raise e
|
||||
|
||||
proc getTok(p: var TPegParser) =
|
||||
proc getTok(p: var PegParser) =
|
||||
getTok(p, p.tok)
|
||||
if p.tok.kind == tkInvalid: pegError(p, "invalid token")
|
||||
|
||||
proc eat(p: var TPegParser, kind: TTokKind) =
|
||||
proc eat(p: var PegParser, kind: TTokKind) =
|
||||
if p.tok.kind == kind: getTok(p)
|
||||
else: pegError(p, tokKindToStr[kind] & " expected")
|
||||
|
||||
proc parseExpr(p: var TPegParser): TPeg
|
||||
proc parseExpr(p: var PegParser): Peg
|
||||
|
||||
proc getNonTerminal(p: var TPegParser, name: string): PNonTerminal =
|
||||
proc getNonTerminal(p: var PegParser, name: string): PNonTerminal =
|
||||
for i in 0..high(p.nonterms):
|
||||
result = p.nonterms[i]
|
||||
if cmpIgnoreStyle(result.name, name) == 0: return
|
||||
@@ -1446,19 +1438,19 @@ proc getNonTerminal(p: var TPegParser, name: string): PNonTerminal =
|
||||
result = newNonTerminal(name, getLine(p), getColumn(p))
|
||||
add(p.nonterms, result)
|
||||
|
||||
proc modifiedTerm(s: string, m: TModifier): TPeg =
|
||||
proc modifiedTerm(s: string, m: TModifier): Peg =
|
||||
case m
|
||||
of modNone, modVerbatim: result = term(s)
|
||||
of modIgnoreCase: result = termIgnoreCase(s)
|
||||
of modIgnoreStyle: result = termIgnoreStyle(s)
|
||||
|
||||
proc modifiedBackref(s: int, m: TModifier): TPeg =
|
||||
proc modifiedBackref(s: int, m: TModifier): Peg =
|
||||
case m
|
||||
of modNone, modVerbatim: result = backref(s)
|
||||
of modIgnoreCase: result = backrefIgnoreCase(s)
|
||||
of modIgnoreStyle: result = backrefIgnoreStyle(s)
|
||||
|
||||
proc builtin(p: var TPegParser): TPeg =
|
||||
proc builtin(p: var PegParser): Peg =
|
||||
# do not use "y", "skip" or "i" as these would be ambiguous
|
||||
case p.tok.literal
|
||||
of "n": result = newLine()
|
||||
@@ -1478,11 +1470,11 @@ proc builtin(p: var TPegParser): TPeg =
|
||||
of "white": result = unicodeWhitespace()
|
||||
else: pegError(p, "unknown built-in: " & p.tok.literal)
|
||||
|
||||
proc token(terminal: TPeg, p: TPegParser): TPeg =
|
||||
proc token(terminal: Peg, p: PegParser): Peg =
|
||||
if p.skip.kind == pkEmpty: result = terminal
|
||||
else: result = sequence(p.skip, terminal)
|
||||
|
||||
proc primary(p: var TPegParser): TPeg =
|
||||
proc primary(p: var PegParser): Peg =
|
||||
case p.tok.kind
|
||||
of tkAmp:
|
||||
getTok(p)
|
||||
@@ -1571,7 +1563,7 @@ proc primary(p: var TPegParser): TPeg =
|
||||
getTok(p)
|
||||
else: break
|
||||
|
||||
proc seqExpr(p: var TPegParser): TPeg =
|
||||
proc seqExpr(p: var PegParser): Peg =
|
||||
result = primary(p)
|
||||
while true:
|
||||
case p.tok.kind
|
||||
@@ -1585,13 +1577,13 @@ proc seqExpr(p: var TPegParser): TPeg =
|
||||
else: break
|
||||
else: break
|
||||
|
||||
proc parseExpr(p: var TPegParser): TPeg =
|
||||
proc parseExpr(p: var PegParser): Peg =
|
||||
result = seqExpr(p)
|
||||
while p.tok.kind == tkBar:
|
||||
getTok(p)
|
||||
result = result / seqExpr(p)
|
||||
|
||||
proc parseRule(p: var TPegParser): PNonTerminal =
|
||||
proc parseRule(p: var PegParser): PNonTerminal =
|
||||
if p.tok.kind == tkIdentifier and arrowIsNextTok(p):
|
||||
result = getNonTerminal(p, p.tok.literal)
|
||||
if ntDeclared in result.flags:
|
||||
@@ -1605,7 +1597,7 @@ proc parseRule(p: var TPegParser): PNonTerminal =
|
||||
else:
|
||||
pegError(p, "rule expected, but found: " & p.tok.literal)
|
||||
|
||||
proc rawParse(p: var TPegParser): TPeg =
|
||||
proc rawParse(p: var PegParser): Peg =
|
||||
## parses a rule or a PEG expression
|
||||
while p.tok.kind == tkBuiltin:
|
||||
case p.tok.literal
|
||||
@@ -1635,12 +1627,12 @@ proc rawParse(p: var TPegParser): TPeg =
|
||||
elif ntUsed notin nt.flags and i > 0:
|
||||
pegError(p, "unused rule: " & nt.name, nt.line, nt.col)
|
||||
|
||||
proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): TPeg =
|
||||
## constructs a TPeg object from `pattern`. `filename`, `line`, `col` are
|
||||
proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): Peg =
|
||||
## constructs a Peg object from `pattern`. `filename`, `line`, `col` are
|
||||
## used for error messages, but they only provide start offsets. `parsePeg`
|
||||
## keeps track of line and column numbers within `pattern`.
|
||||
var p: TPegParser
|
||||
init(TPegLexer(p), pattern, filename, line, col)
|
||||
var p: PegParser
|
||||
init(PegLexer(p), pattern, filename, line, col)
|
||||
p.tok.kind = tkInvalid
|
||||
p.tok.modifier = modNone
|
||||
p.tok.literal = ""
|
||||
@@ -1650,8 +1642,8 @@ proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): TPeg =
|
||||
getTok(p)
|
||||
result = rawParse(p)
|
||||
|
||||
proc peg*(pattern: string): TPeg =
|
||||
## constructs a TPeg object from the `pattern`. The short name has been
|
||||
proc peg*(pattern: string): Peg =
|
||||
## constructs a Peg object from the `pattern`. The short name has been
|
||||
## chosen to encourage its use as a raw string modifier::
|
||||
##
|
||||
## peg"{\ident} \s* '=' \s* {.*}"
|
||||
@@ -1704,7 +1696,7 @@ when isMainModule:
|
||||
expr.rule = sequence(capture(ident), *sequence(
|
||||
nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr)))
|
||||
|
||||
var c: TCaptures
|
||||
var c: Captures
|
||||
var s = "a+b + c +d+e+f"
|
||||
assert rawMatch(s, expr.rule, 0, c) == len(s)
|
||||
var a = ""
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2013 Robert Persson
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -12,10 +12,11 @@ import strutils
|
||||
import numeric
|
||||
|
||||
type
|
||||
TPoly* = object
|
||||
cofs:seq[float]
|
||||
Poly* = object
|
||||
cofs:seq[float]
|
||||
|
||||
{.deprecated: [TPoly: Poly].}
|
||||
|
||||
|
||||
proc degree*(p:TPoly):int=
|
||||
## Returns the degree of the polynomial,
|
||||
## that is the number of coefficients-1
|
||||
@@ -104,7 +105,7 @@ proc `$` *(p:TPoly):string =
|
||||
result="0"
|
||||
|
||||
|
||||
proc derivative*(p:TPoly):TPoly=
|
||||
proc derivative*(p: TPoly): TPoly=
|
||||
## Returns a new polynomial, which is the derivative of `p`
|
||||
newSeq[float](result.cofs,p.degree)
|
||||
for idx in 0..high(result.cofs):
|
||||
@@ -310,10 +311,10 @@ proc getRangeForRoots(p:TPoly):tuple[xmin,xmax:float]=
|
||||
var bound1,bound2:float
|
||||
|
||||
for i in countup(0,deg):
|
||||
var c=abs(p.cofs[i]/d)
|
||||
bound1=max(bound1,c+1.0)
|
||||
bound2=bound2+c
|
||||
|
||||
var c=abs(p.cofs[i]/d)
|
||||
bound1=max(bound1,c+1.0)
|
||||
bound2=bound2+c
|
||||
|
||||
bound2=max(1.0,bound2)
|
||||
result.xmax=min(bound1,bound2)
|
||||
result.xmin= -result.xmax
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2014 Dominik Picheta
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -28,7 +28,7 @@ else:
|
||||
export fcntl, F_GETFL, O_NONBLOCK, F_SETFL, EAGAIN, EWOULDBLOCK, MSG_NOSIGNAL,
|
||||
EINTR, EINPROGRESS, ECONNRESET, EPIPE, ENETRESET
|
||||
|
||||
export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen,
|
||||
export SocketHandle, Sockaddr_in, Addrinfo, INADDR_ANY, SockAddr, SockLen,
|
||||
inet_ntoa, recv, `==`, connect, send, accept, recvfrom, sendto
|
||||
|
||||
export
|
||||
@@ -40,22 +40,22 @@ export
|
||||
MSG_PEEK
|
||||
|
||||
type
|
||||
TPort* = distinct uint16 ## port type
|
||||
Port* = distinct uint16 ## port type
|
||||
|
||||
TDomain* = enum ## domain, which specifies the protocol family of the
|
||||
Domain* = enum ## domain, which specifies the protocol family of the
|
||||
## created socket. Other domains than those that are listed
|
||||
## here are unsupported.
|
||||
AF_UNIX, ## for local socket (using a file). Unsupported on Windows.
|
||||
AF_INET = 2, ## for network protocol IPv4 or
|
||||
AF_INET6 = 23 ## for network protocol IPv6.
|
||||
|
||||
TType* = enum ## second argument to `socket` proc
|
||||
SockType* = enum ## second argument to `socket` proc
|
||||
SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets
|
||||
SOCK_DGRAM = 2, ## datagram service or Datagram Sockets
|
||||
SOCK_RAW = 3, ## raw protocols atop the network layer.
|
||||
SOCK_SEQPACKET = 5 ## reliable sequenced packet service
|
||||
|
||||
TProtocol* = enum ## third argument to `socket` proc
|
||||
Protocol* = enum ## third argument to `socket` proc
|
||||
IPPROTO_TCP = 6, ## Transmission control protocol.
|
||||
IPPROTO_UDP = 17, ## User datagram protocol.
|
||||
IPPROTO_IP, ## Internet protocol. Unsupported on Windows.
|
||||
@@ -63,19 +63,22 @@ type
|
||||
IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows.
|
||||
IPPROTO_ICMP ## Control message protocol. Unsupported on Windows.
|
||||
|
||||
TServent* {.pure, final.} = object ## information about a service
|
||||
Servent* = object ## information about a service
|
||||
name*: string
|
||||
aliases*: seq[string]
|
||||
port*: TPort
|
||||
port*: Port
|
||||
proto*: string
|
||||
|
||||
Thostent* {.pure, final.} = object ## information about a given host
|
||||
Hostent* = object ## information about a given host
|
||||
name*: string
|
||||
aliases*: seq[string]
|
||||
addrtype*: TDomain
|
||||
addrtype*: Domain
|
||||
length*: int
|
||||
addrList*: seq[string]
|
||||
|
||||
{.deprecated: [TPort: Port, TDomain: Domain, TType: SockType,
|
||||
TProtocol: Protocol, TServent: Servent, THostent: Hostent].}
|
||||
|
||||
when useWinVersion:
|
||||
let
|
||||
osInvalidSocket* = winlean.INVALID_SOCKET
|
||||
@@ -86,37 +89,37 @@ when useWinVersion:
|
||||
FIONBIO* = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or
|
||||
(102 shl 8) or 126
|
||||
|
||||
proc ioctlsocket*(s: TSocketHandle, cmd: clong,
|
||||
proc ioctlsocket*(s: SocketHandle, cmd: clong,
|
||||
argptr: ptr clong): cint {.
|
||||
stdcall, importc: "ioctlsocket", dynlib: "ws2_32.dll".}
|
||||
else:
|
||||
let
|
||||
osInvalidSocket* = posix.INVALID_SOCKET
|
||||
|
||||
proc `==`*(a, b: TPort): bool {.borrow.}
|
||||
proc `==`*(a, b: Port): bool {.borrow.}
|
||||
## ``==`` for ports.
|
||||
|
||||
proc `$`*(p: TPort): string {.borrow.}
|
||||
proc `$`*(p: Port): string {.borrow.}
|
||||
## returns the port number as a string
|
||||
|
||||
proc toInt*(domain: TDomain): cint
|
||||
proc toInt*(domain: Domain): cint
|
||||
## Converts the TDomain enum to a platform-dependent ``cint``.
|
||||
|
||||
proc toInt*(typ: TType): cint
|
||||
proc toInt*(typ: SockType): cint
|
||||
## Converts the TType enum to a platform-dependent ``cint``.
|
||||
|
||||
proc toInt*(p: TProtocol): cint
|
||||
proc toInt*(p: Protocol): cint
|
||||
## Converts the TProtocol enum to a platform-dependent ``cint``.
|
||||
|
||||
when not useWinVersion:
|
||||
proc toInt(domain: TDomain): cint =
|
||||
proc toInt(domain: Domain): cint =
|
||||
case domain
|
||||
of AF_UNIX: result = posix.AF_UNIX
|
||||
of AF_INET: result = posix.AF_INET
|
||||
of AF_INET6: result = posix.AF_INET6
|
||||
else: discard
|
||||
|
||||
proc toInt(typ: TType): cint =
|
||||
proc toInt(typ: SockType): cint =
|
||||
case typ
|
||||
of SOCK_STREAM: result = posix.SOCK_STREAM
|
||||
of SOCK_DGRAM: result = posix.SOCK_DGRAM
|
||||
@@ -124,7 +127,7 @@ when not useWinVersion:
|
||||
of SOCK_RAW: result = posix.SOCK_RAW
|
||||
else: discard
|
||||
|
||||
proc toInt(p: TProtocol): cint =
|
||||
proc toInt(p: Protocol): cint =
|
||||
case p
|
||||
of IPPROTO_TCP: result = posix.IPPROTO_TCP
|
||||
of IPPROTO_UDP: result = posix.IPPROTO_UDP
|
||||
@@ -135,22 +138,22 @@ when not useWinVersion:
|
||||
else: discard
|
||||
|
||||
else:
|
||||
proc toInt(domain: TDomain): cint =
|
||||
proc toInt(domain: Domain): cint =
|
||||
result = toU16(ord(domain))
|
||||
|
||||
proc toInt(typ: TType): cint =
|
||||
proc toInt(typ: SockType): cint =
|
||||
result = cint(ord(typ))
|
||||
|
||||
proc toInt(p: TProtocol): cint =
|
||||
proc toInt(p: Protocol): cint =
|
||||
result = cint(ord(p))
|
||||
|
||||
|
||||
proc newRawSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
|
||||
protocol: TProtocol = IPPROTO_TCP): TSocketHandle =
|
||||
proc newRawSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
|
||||
protocol: Protocol = IPPROTO_TCP): SocketHandle =
|
||||
## Creates a new socket; returns `InvalidSocket` if an error occurs.
|
||||
socket(toInt(domain), toInt(typ), toInt(protocol))
|
||||
|
||||
proc close*(socket: TSocketHandle) =
|
||||
proc close*(socket: SocketHandle) =
|
||||
## closes a socket.
|
||||
when useWinVersion:
|
||||
discard winlean.closeSocket(socket)
|
||||
@@ -159,10 +162,10 @@ proc close*(socket: TSocketHandle) =
|
||||
# TODO: These values should not be discarded. An EOS should be raised.
|
||||
# http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times
|
||||
|
||||
proc bindAddr*(socket: TSocketHandle, name: ptr TSockAddr, namelen: TSockLen): cint =
|
||||
proc bindAddr*(socket: SocketHandle, name: ptr SockAddr, namelen: SockLen): cint =
|
||||
result = bindSocket(socket, name, namelen)
|
||||
|
||||
proc listen*(socket: TSocketHandle, backlog = SOMAXCONN): cint {.tags: [FReadIO].} =
|
||||
proc listen*(socket: SocketHandle, backlog = SOMAXCONN): cint {.tags: [FReadIO].} =
|
||||
## Marks ``socket`` as accepting connections.
|
||||
## ``Backlog`` specifies the maximum length of the
|
||||
## queue of pending connections.
|
||||
@@ -171,12 +174,12 @@ proc listen*(socket: TSocketHandle, backlog = SOMAXCONN): cint {.tags: [FReadIO]
|
||||
else:
|
||||
result = posix.listen(socket, cint(backlog))
|
||||
|
||||
proc getAddrInfo*(address: string, port: TPort, af: TDomain = AF_INET, typ: TType = SOCK_STREAM,
|
||||
prot: TProtocol = IPPROTO_TCP): ptr TAddrInfo =
|
||||
proc getAddrInfo*(address: string, port: Port, af: Domain = AF_INET, typ: SockType = SOCK_STREAM,
|
||||
prot: Protocol = IPPROTO_TCP): ptr AddrInfo =
|
||||
##
|
||||
##
|
||||
## **Warning**: The resulting ``ptr TAddrInfo`` must be freed using ``dealloc``!
|
||||
var hints: TAddrInfo
|
||||
var hints: AddrInfo
|
||||
result = nil
|
||||
hints.ai_family = toInt(af)
|
||||
hints.ai_socktype = toInt(typ)
|
||||
@@ -184,11 +187,11 @@ proc getAddrInfo*(address: string, port: TPort, af: TDomain = AF_INET, typ: TTyp
|
||||
var gaiResult = getAddrInfo(address, $port, addr(hints), result)
|
||||
if gaiResult != 0'i32:
|
||||
when useWinVersion:
|
||||
osError(osLastError())
|
||||
raiseOSError(osLastError())
|
||||
else:
|
||||
raise newException(EOS, $gai_strerror(gaiResult))
|
||||
|
||||
proc dealloc*(ai: ptr TAddrInfo) =
|
||||
proc dealloc*(ai: ptr AddrInfo) =
|
||||
freeaddrinfo(ai)
|
||||
|
||||
proc ntohl*(x: int32): int32 =
|
||||
@@ -220,7 +223,7 @@ proc htons*(x: int16): int16 =
|
||||
## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
|
||||
result = rawsockets.ntohs(x)
|
||||
|
||||
proc getServByName*(name, proto: string): TServent {.tags: [FReadIO].} =
|
||||
proc getServByName*(name, proto: string): Servent {.tags: [FReadIO].} =
|
||||
## Searches the database from the beginning and finds the first entry for
|
||||
## which the service name specified by ``name`` matches the s_name member
|
||||
## and the protocol name specified by ``proto`` matches the s_proto member.
|
||||
@@ -233,10 +236,10 @@ proc getServByName*(name, proto: string): TServent {.tags: [FReadIO].} =
|
||||
if s == nil: raise newException(EOS, "Service not found.")
|
||||
result.name = $s.s_name
|
||||
result.aliases = cstringArrayToSeq(s.s_aliases)
|
||||
result.port = TPort(s.s_port)
|
||||
result.port = Port(s.s_port)
|
||||
result.proto = $s.s_proto
|
||||
|
||||
proc getServByPort*(port: TPort, proto: string): TServent {.tags: [FReadIO].} =
|
||||
proc getServByPort*(port: Port, proto: string): Servent {.tags: [FReadIO].} =
|
||||
## Searches the database from the beginning and finds the first entry for
|
||||
## which the port specified by ``port`` matches the s_port member and the
|
||||
## protocol name specified by ``proto`` matches the s_proto member.
|
||||
@@ -249,28 +252,28 @@ proc getServByPort*(port: TPort, proto: string): TServent {.tags: [FReadIO].} =
|
||||
if s == nil: raise newException(EOS, "Service not found.")
|
||||
result.name = $s.s_name
|
||||
result.aliases = cstringArrayToSeq(s.s_aliases)
|
||||
result.port = TPort(s.s_port)
|
||||
result.port = Port(s.s_port)
|
||||
result.proto = $s.s_proto
|
||||
|
||||
proc getHostByAddr*(ip: string): Thostent {.tags: [FReadIO].} =
|
||||
proc getHostByAddr*(ip: string): Hostent {.tags: [FReadIO].} =
|
||||
## This function will lookup the hostname of an IP Address.
|
||||
var myaddr: TInAddr
|
||||
var myaddr: InAddr
|
||||
myaddr.s_addr = inet_addr(ip)
|
||||
|
||||
when useWinVersion:
|
||||
var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
|
||||
cint(rawsockets.AF_INET))
|
||||
if s == nil: osError(osLastError())
|
||||
if s == nil: raiseOSError(osLastError())
|
||||
else:
|
||||
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).TSocklen,
|
||||
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).Socklen,
|
||||
cint(posix.AF_INET))
|
||||
if s == nil:
|
||||
raise newException(EOS, $hstrerror(h_errno))
|
||||
|
||||
result.name = $s.h_name
|
||||
result.aliases = cstringArrayToSeq(s.h_aliases)
|
||||
when useWinVersion:
|
||||
result.addrtype = TDomain(s.h_addrtype)
|
||||
when useWinVersion:
|
||||
result.addrtype = Domain(s.h_addrtype)
|
||||
else:
|
||||
if s.h_addrtype == posix.AF_INET:
|
||||
result.addrtype = AF_INET
|
||||
@@ -281,17 +284,17 @@ proc getHostByAddr*(ip: string): Thostent {.tags: [FReadIO].} =
|
||||
result.addrList = cstringArrayToSeq(s.h_addr_list)
|
||||
result.length = int(s.h_length)
|
||||
|
||||
proc getHostByName*(name: string): Thostent {.tags: [FReadIO].} =
|
||||
proc getHostByName*(name: string): Hostent {.tags: [FReadIO].} =
|
||||
## This function will lookup the IP address of a hostname.
|
||||
when useWinVersion:
|
||||
var s = winlean.gethostbyname(name)
|
||||
else:
|
||||
var s = posix.gethostbyname(name)
|
||||
if s == nil: osError(osLastError())
|
||||
if s == nil: raiseOSError(osLastError())
|
||||
result.name = $s.h_name
|
||||
result.aliases = cstringArrayToSeq(s.h_aliases)
|
||||
when useWinVersion:
|
||||
result.addrtype = TDomain(s.h_addrtype)
|
||||
when useWinVersion:
|
||||
result.addrtype = Domain(s.h_addrtype)
|
||||
else:
|
||||
if s.h_addrtype == posix.AF_INET:
|
||||
result.addrtype = AF_INET
|
||||
@@ -302,69 +305,69 @@ proc getHostByName*(name: string): Thostent {.tags: [FReadIO].} =
|
||||
result.addrList = cstringArrayToSeq(s.h_addr_list)
|
||||
result.length = int(s.h_length)
|
||||
|
||||
proc getSockName*(socket: TSocketHandle): TPort =
|
||||
proc getSockName*(socket: SocketHandle): Port =
|
||||
## returns the socket's associated port number.
|
||||
var name: Tsockaddr_in
|
||||
var name: Sockaddr_in
|
||||
when useWinVersion:
|
||||
name.sin_family = int16(ord(AF_INET))
|
||||
else:
|
||||
name.sin_family = posix.AF_INET
|
||||
#name.sin_port = htons(cint16(port))
|
||||
#name.sin_addr.s_addr = htonl(INADDR_ANY)
|
||||
var namelen = sizeof(name).TSocklen
|
||||
if getsockname(socket, cast[ptr TSockAddr](addr(name)),
|
||||
var namelen = sizeof(name).Socklen
|
||||
if getsockname(socket, cast[ptr SockAddr](addr(name)),
|
||||
addr(namelen)) == -1'i32:
|
||||
osError(osLastError())
|
||||
result = TPort(rawsockets.ntohs(name.sin_port))
|
||||
raiseOSError(osLastError())
|
||||
result = Port(rawsockets.ntohs(name.sin_port))
|
||||
|
||||
proc getSockOptInt*(socket: TSocketHandle, level, optname: int): int {.
|
||||
proc getSockOptInt*(socket: SocketHandle, level, optname: int): int {.
|
||||
tags: [FReadIO].} =
|
||||
## getsockopt for integer options.
|
||||
var res: cint
|
||||
var size = sizeof(res).TSocklen
|
||||
var size = sizeof(res).Socklen
|
||||
if getsockopt(socket, cint(level), cint(optname),
|
||||
addr(res), addr(size)) < 0'i32:
|
||||
osError(osLastError())
|
||||
raiseOSError(osLastError())
|
||||
result = int(res)
|
||||
|
||||
proc setSockOptInt*(socket: TSocketHandle, level, optname, optval: int) {.
|
||||
proc setSockOptInt*(socket: SocketHandle, level, optname, optval: int) {.
|
||||
tags: [FWriteIO].} =
|
||||
## setsockopt for integer options.
|
||||
var value = cint(optval)
|
||||
if setsockopt(socket, cint(level), cint(optname), addr(value),
|
||||
sizeof(value).TSocklen) < 0'i32:
|
||||
osError(osLastError())
|
||||
sizeof(value).Socklen) < 0'i32:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc setBlocking*(s: TSocketHandle, blocking: bool) =
|
||||
proc setBlocking*(s: SocketHandle, blocking: bool) =
|
||||
## Sets blocking mode on socket.
|
||||
##
|
||||
## Raises EOS on error.
|
||||
when useWinVersion:
|
||||
var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
|
||||
if ioctlsocket(s, FIONBIO, addr(mode)) == -1:
|
||||
osError(osLastError())
|
||||
raiseOSError(osLastError())
|
||||
else: # BSD sockets
|
||||
var x: int = fcntl(s, F_GETFL, 0)
|
||||
if x == -1:
|
||||
osError(osLastError())
|
||||
raiseOSError(osLastError())
|
||||
else:
|
||||
var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
|
||||
if fcntl(s, F_SETFL, mode) == -1:
|
||||
osError(osLastError())
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc timeValFromMilliseconds(timeout = 500): Ttimeval =
|
||||
proc timeValFromMilliseconds(timeout = 500): Timeval =
|
||||
if timeout != -1:
|
||||
var seconds = timeout div 1000
|
||||
result.tv_sec = seconds.int32
|
||||
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
|
||||
|
||||
proc createFdSet(fd: var TFdSet, s: seq[TSocketHandle], m: var int) =
|
||||
proc createFdSet(fd: var FdSet, s: seq[SocketHandle], m: var int) =
|
||||
FD_ZERO(fd)
|
||||
for i in items(s):
|
||||
m = max(m, int(i))
|
||||
FD_SET(i, fd)
|
||||
|
||||
proc pruneSocketSet(s: var seq[TSocketHandle], fd: var TFdSet) =
|
||||
proc pruneSocketSet(s: var seq[SocketHandle], fd: var FdSet) =
|
||||
var i = 0
|
||||
var L = s.len
|
||||
while i < L:
|
||||
@@ -375,7 +378,7 @@ proc pruneSocketSet(s: var seq[TSocketHandle], fd: var TFdSet) =
|
||||
inc(i)
|
||||
setLen(s, L)
|
||||
|
||||
proc select*(readfds: var seq[TSocketHandle], timeout = 500): int =
|
||||
proc select*(readfds: var seq[SocketHandle], timeout = 500): int =
|
||||
## Traditional select function. This function will return the number of
|
||||
## sockets that are ready to be read from, written to, or which have errors.
|
||||
## If there are none; 0 is returned.
|
||||
@@ -396,7 +399,7 @@ proc select*(readfds: var seq[TSocketHandle], timeout = 500): int =
|
||||
|
||||
pruneSocketSet(readfds, (rd))
|
||||
|
||||
proc selectWrite*(writefds: var seq[TSocketHandle],
|
||||
proc selectWrite*(writefds: var seq[SocketHandle],
|
||||
timeout = 500): int {.tags: [FReadIO].} =
|
||||
## When a socket in ``writefds`` is ready to be written to then a non-zero
|
||||
## value will be returned specifying the count of the sockets which can be
|
||||
@@ -405,7 +408,7 @@ proc selectWrite*(writefds: var seq[TSocketHandle],
|
||||
##
|
||||
## ``timeout`` is specified in miliseconds and ``-1`` can be specified for
|
||||
## an unlimited time.
|
||||
var tv {.noInit.}: Ttimeval = timeValFromMilliseconds(timeout)
|
||||
var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
|
||||
|
||||
var wr: TFdSet
|
||||
var m = 0
|
||||
@@ -419,5 +422,5 @@ proc selectWrite*(writefds: var seq[TSocketHandle],
|
||||
pruneSocketSet(writefds, (wr))
|
||||
|
||||
when defined(Windows):
|
||||
var wsa: TWSADATA
|
||||
if WSAStartup(0x0101'i16, addr wsa) != 0: osError(osLastError())
|
||||
var wsa: WSADATA
|
||||
if WSAStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError())
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Dominik Picheta
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -20,36 +20,40 @@ const
|
||||
redisNil* = "\0\0"
|
||||
|
||||
type
|
||||
PPipeline = ref object
|
||||
Pipeline = ref object
|
||||
enabled: bool
|
||||
buffer: string
|
||||
expected: int ## number of replies expected if pipelined
|
||||
|
||||
type
|
||||
TSendMode = enum
|
||||
SendMode = enum
|
||||
normal, pipelined, multiple
|
||||
|
||||
type
|
||||
TRedis* {.pure, final.} = object
|
||||
Redis* = object
|
||||
socket: TSocket
|
||||
connected: bool
|
||||
pipeline: PPipeline
|
||||
pipeline: Pipeline
|
||||
|
||||
TRedisStatus* = string
|
||||
TRedisInteger* = biggestInt
|
||||
TRedisString* = string ## Bulk reply
|
||||
TRedisList* = seq[TRedisString] ## Multi-bulk reply
|
||||
RedisStatus* = string
|
||||
RedisInteger* = BiggestInt
|
||||
RedisString* = string ## Bulk reply
|
||||
RedisList* = seq[RedisString] ## Multi-bulk reply
|
||||
|
||||
EInvalidReply* = object of ESynch ## Invalid reply from redis
|
||||
ERedis* = object of ESynch ## Error in redis
|
||||
ReplyError* = object of IOError ## Invalid reply from redis
|
||||
RedisError* = object of IOError ## Error in redis
|
||||
|
||||
proc newPipeline(): PPipeline =
|
||||
{.deprecated: [TSendMode: SendMode, TRedis: Redis, TRedisStatus: RedisStatus,
|
||||
TRedisInteger: RedisInteger, TRedisString: RedisString,
|
||||
TRedisList: RedistList, EInvalidReply: ReplyError, ERedis: RedisError].}
|
||||
|
||||
proc newPipeline(): Pipeline =
|
||||
new(result)
|
||||
result.buffer = ""
|
||||
result.enabled = false
|
||||
result.expected = 0
|
||||
|
||||
proc open*(host = "localhost", port = 6379.TPort): TRedis =
|
||||
proc open*(host = "localhost", port = 6379.Port): Redis =
|
||||
## Opens a connection to the redis server.
|
||||
result.socket = socket(buffered = false)
|
||||
if result.socket == InvalidSocket:
|
||||
@@ -58,24 +62,24 @@ proc open*(host = "localhost", port = 6379.TPort): TRedis =
|
||||
result.pipeline = newPipeline()
|
||||
|
||||
proc raiseInvalidReply(expected, got: char) =
|
||||
raise newException(EInvalidReply,
|
||||
raise newException(ReplyError,
|
||||
"Expected '$1' at the beginning of a status reply got '$2'" %
|
||||
[$expected, $got])
|
||||
|
||||
proc raiseNoOK(status: string, pipelineEnabled:bool) =
|
||||
proc raiseNoOK(status: string, pipelineEnabled: bool) =
|
||||
if pipelineEnabled and not (status == "QUEUED" or status == "PIPELINED"):
|
||||
raise newException(EInvalidReply, "Expected \"QUEUED\" or \"PIPELINED\" got \"$1\"" % status)
|
||||
elif not pipelineEnabled and status != "OK":
|
||||
raise newException(EInvalidReply, "Expected \"OK\" got \"$1\"" % status)
|
||||
|
||||
template readSocket(r: TRedis, dummyVal:expr): stmt =
|
||||
var line {.inject.} :TaintedString = ""
|
||||
template readSocket(r: Redis, dummyVal:expr): stmt =
|
||||
var line {.inject.}: TaintedString = ""
|
||||
if r.pipeline.enabled:
|
||||
return dummyVal
|
||||
else:
|
||||
readLine(r.socket, line)
|
||||
|
||||
proc parseStatus(r: TRedis, line: string = ""): TRedisStatus =
|
||||
proc parseStatus(r: Redis, line: string = ""): RedisStatus =
|
||||
if r.pipeline.enabled:
|
||||
return "PIPELINED"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2011 Philippe Lhoste
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2010 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -30,13 +30,15 @@ var
|
||||
cacheEnabled = false
|
||||
|
||||
type
|
||||
PRope* = ref TRope ## empty rope is represented by nil
|
||||
TRope {.acyclic, final, pure.} = object
|
||||
left, right: PRope
|
||||
Rope* = ref RopeObj ## empty rope is represented by nil
|
||||
RopeObj {.acyclic.} = object
|
||||
left, right: Rope
|
||||
length: int
|
||||
data: string # != nil if a leaf
|
||||
|
||||
proc isConc(r: PRope): bool {.inline.} = return isNil(r.data)
|
||||
{.deprecated: [PRope: Rope].}
|
||||
|
||||
proc isConc(r: Rope): bool {.inline.} = return isNil(r.data)
|
||||
|
||||
# Note that the left and right pointers are not needed for leafs.
|
||||
# Leaves have relatively high memory overhead (~30 bytes on a 32
|
||||
@@ -46,25 +48,25 @@ proc isConc(r: PRope): bool {.inline.} = return isNil(r.data)
|
||||
# performance. But for the caching tree we use the leaf's left and right
|
||||
# pointers.
|
||||
|
||||
proc len*(a: PRope): int {.rtl, extern: "nro$1".} =
|
||||
proc len*(a: Rope): int {.rtl, extern: "nro$1".} =
|
||||
## the rope's length
|
||||
if a == nil: result = 0
|
||||
else: result = a.length
|
||||
|
||||
proc newRope(): PRope = new(result)
|
||||
proc newRope(data: string): PRope =
|
||||
proc newRope(): Rope = new(result)
|
||||
proc newRope(data: string): Rope =
|
||||
new(result)
|
||||
result.length = len(data)
|
||||
result.data = data
|
||||
|
||||
var
|
||||
cache {.threadvar.}: PRope # the root of the cache tree
|
||||
N {.threadvar.}: PRope # dummy rope needed for splay algorithm
|
||||
cache {.threadvar.}: Rope # the root of the cache tree
|
||||
N {.threadvar.}: Rope # dummy rope needed for splay algorithm
|
||||
|
||||
when countCacheMisses:
|
||||
var misses, hits: int
|
||||
|
||||
proc splay(s: string, tree: PRope, cmpres: var int): PRope =
|
||||
proc splay(s: string, tree: Rope, cmpres: var int): Rope =
|
||||
var c: int
|
||||
var t = tree
|
||||
N.left = nil
|
||||
@@ -102,7 +104,7 @@ proc splay(s: string, tree: PRope, cmpres: var int): PRope =
|
||||
t.right = N.left
|
||||
result = t
|
||||
|
||||
proc insertInCache(s: string, tree: PRope): PRope =
|
||||
proc insertInCache(s: string, tree: Rope): Rope =
|
||||
var t = tree
|
||||
if t == nil:
|
||||
result = newRope(s)
|
||||
@@ -128,7 +130,7 @@ proc insertInCache(s: string, tree: PRope): PRope =
|
||||
result.left = t
|
||||
t.right = nil
|
||||
|
||||
proc rope*(s: string): PRope {.rtl, extern: "nro$1Str".} =
|
||||
proc rope*(s: string): Rope {.rtl, extern: "nro$1Str".} =
|
||||
## Converts a string to a rope.
|
||||
if s.len == 0:
|
||||
result = nil
|
||||
@@ -138,11 +140,11 @@ proc rope*(s: string): PRope {.rtl, extern: "nro$1Str".} =
|
||||
else:
|
||||
result = newRope(s)
|
||||
|
||||
proc rope*(i: BiggestInt): PRope {.rtl, extern: "nro$1BiggestInt".} =
|
||||
proc rope*(i: BiggestInt): Rope {.rtl, extern: "nro$1BiggestInt".} =
|
||||
## Converts an int to a rope.
|
||||
result = rope($i)
|
||||
|
||||
proc rope*(f: BiggestFloat): PRope {.rtl, extern: "nro$1BiggestFloat".} =
|
||||
proc rope*(f: BiggestFloat): Rope {.rtl, extern: "nro$1BiggestFloat".} =
|
||||
## Converts a float to a rope.
|
||||
result = rope($f)
|
||||
|
||||
@@ -156,7 +158,7 @@ proc disableCache*() {.rtl, extern: "nro$1".} =
|
||||
cache = nil
|
||||
cacheEnabled = false
|
||||
|
||||
proc `&`*(a, b: PRope): PRope {.rtl, extern: "nroConcRopeRope".} =
|
||||
proc `&`*(a, b: Rope): Rope {.rtl, extern: "nroConcRopeRope".} =
|
||||
## the concatenation operator for ropes.
|
||||
if a == nil:
|
||||
result = b
|
||||
@@ -176,27 +178,27 @@ proc `&`*(a, b: PRope): PRope {.rtl, extern: "nroConcRopeRope".} =
|
||||
result.left = a
|
||||
result.right = b
|
||||
|
||||
proc `&`*(a: PRope, b: string): PRope {.rtl, extern: "nroConcRopeStr".} =
|
||||
proc `&`*(a: Rope, b: string): Rope {.rtl, extern: "nroConcRopeStr".} =
|
||||
## the concatenation operator for ropes.
|
||||
result = a & rope(b)
|
||||
|
||||
proc `&`*(a: string, b: PRope): PRope {.rtl, extern: "nroConcStrRope".} =
|
||||
proc `&`*(a: string, b: Rope): Rope {.rtl, extern: "nroConcStrRope".} =
|
||||
## the concatenation operator for ropes.
|
||||
result = rope(a) & b
|
||||
|
||||
proc `&`*(a: openarray[PRope]): PRope {.rtl, extern: "nroConcOpenArray".} =
|
||||
proc `&`*(a: openarray[Rope]): Rope {.rtl, extern: "nroConcOpenArray".} =
|
||||
## the concatenation operator for an openarray of ropes.
|
||||
for i in countup(0, high(a)): result = result & a[i]
|
||||
|
||||
proc add*(a: var PRope, b: PRope) {.rtl, extern: "nro$1Rope".} =
|
||||
proc add*(a: var Rope, b: Rope) {.rtl, extern: "nro$1Rope".} =
|
||||
## adds `b` to the rope `a`.
|
||||
a = a & b
|
||||
|
||||
proc add*(a: var PRope, b: string) {.rtl, extern: "nro$1Str".} =
|
||||
proc add*(a: var Rope, b: string) {.rtl, extern: "nro$1Str".} =
|
||||
## adds `b` to the rope `a`.
|
||||
a = a & b
|
||||
|
||||
proc `[]`*(r: PRope, i: int): char {.rtl, extern: "nroCharAt".} =
|
||||
proc `[]`*(r: Rope, i: int): char {.rtl, extern: "nroCharAt".} =
|
||||
## returns the character at position `i` in the rope `r`. This is quite
|
||||
## expensive! Worst-case: O(n). If ``i >= r.len``, ``\0`` is returned.
|
||||
var x = r
|
||||
@@ -213,7 +215,7 @@ proc `[]`*(r: PRope, i: int): char {.rtl, extern: "nroCharAt".} =
|
||||
x = x.right
|
||||
dec(j, x.len)
|
||||
|
||||
iterator leaves*(r: PRope): string =
|
||||
iterator leaves*(r: Rope): string =
|
||||
## iterates over any leaf string in the rope `r`.
|
||||
if r != nil:
|
||||
var stack = @[r]
|
||||
@@ -226,16 +228,16 @@ iterator leaves*(r: PRope): string =
|
||||
assert(it.data != nil)
|
||||
yield it.data
|
||||
|
||||
iterator items*(r: PRope): char =
|
||||
iterator items*(r: Rope): char =
|
||||
## iterates over any character in the rope `r`.
|
||||
for s in leaves(r):
|
||||
for c in items(s): yield c
|
||||
|
||||
proc write*(f: TFile, r: PRope) {.rtl, extern: "nro$1".} =
|
||||
proc write*(f: TFile, r: Rope) {.rtl, extern: "nro$1".} =
|
||||
## writes a rope to a file.
|
||||
for s in leaves(r): write(f, s)
|
||||
|
||||
proc `$`*(r: PRope): string {.rtl, extern: "nroToString".}=
|
||||
proc `$`*(r: Rope): string {.rtl, extern: "nroToString".}=
|
||||
## converts a rope back to a string.
|
||||
result = newString(r.len)
|
||||
setLen(result, 0)
|
||||
@@ -245,11 +247,11 @@ when false:
|
||||
# Format string caching seems reasonable: All leaves can be shared and format
|
||||
# string parsing has to be done only once. A compiled format string is stored
|
||||
# as a rope. A negative length is used for the index into the args array.
|
||||
proc compiledArg(idx: int): PRope =
|
||||
proc compiledArg(idx: int): Rope =
|
||||
new(result)
|
||||
result.length = -idx
|
||||
|
||||
proc compileFrmt(frmt: string): PRope =
|
||||
proc compileFrmt(frmt: string): Rope =
|
||||
var i = 0
|
||||
var length = len(frmt)
|
||||
result = nil
|
||||
@@ -289,7 +291,7 @@ when false:
|
||||
if i - 1 >= start:
|
||||
add(result, substr(frmt, start, i-1))
|
||||
|
||||
proc `%`*(frmt: string, args: openarray[PRope]): PRope {.
|
||||
proc `%`*(frmt: string, args: openarray[Rope]): Rope {.
|
||||
rtl, extern: "nroFormat".} =
|
||||
## `%` substitution operator for ropes. Does not support the ``$identifier``
|
||||
## nor ``${identifier}`` notations.
|
||||
@@ -332,12 +334,12 @@ proc `%`*(frmt: string, args: openarray[PRope]): PRope {.
|
||||
if i - 1 >= start:
|
||||
add(result, substr(frmt, start, i - 1))
|
||||
|
||||
proc addf*(c: var PRope, frmt: string, args: openarray[PRope]) {.
|
||||
proc addf*(c: var Rope, frmt: string, args: openarray[Rope]) {.
|
||||
rtl, extern: "nro$1".} =
|
||||
## shortcut for ``add(c, frmt % args)``.
|
||||
add(c, frmt % args)
|
||||
|
||||
proc equalsFile*(r: PRope, f: TFile): bool {.rtl, extern: "nro$1File".} =
|
||||
proc equalsFile*(r: Rope, f: TFile): bool {.rtl, extern: "nro$1File".} =
|
||||
## returns true if the contents of the file `f` equal `r`.
|
||||
var bufSize = 1024 # reasonable start value
|
||||
var buf = alloc(BufSize)
|
||||
@@ -352,7 +354,7 @@ proc equalsFile*(r: PRope, f: TFile): bool {.rtl, extern: "nro$1File".} =
|
||||
result = readBuffer(f, buf, 1) == 0 # really at the end of file?
|
||||
dealloc(buf)
|
||||
|
||||
proc equalsFile*(r: PRope, f: string): bool {.rtl, extern: "nro$1Str".} =
|
||||
proc equalsFile*(r: Rope, f: string): bool {.rtl, extern: "nro$1Str".} =
|
||||
## returns true if the contents of the file `f` equal `r`. If `f` does not
|
||||
## exist, false is returned.
|
||||
var bin: TFile
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2013 Andreas Rumpf, Dominik Picheta
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -9,13 +9,13 @@
|
||||
|
||||
## This module implements helper procs for SCGI applications. Example:
|
||||
##
|
||||
## .. code-block:: Nimrod
|
||||
## .. code-block:: Nim
|
||||
##
|
||||
## import strtabs, sockets, scgi
|
||||
##
|
||||
## var counter = 0
|
||||
## proc handleRequest(client: TSocket, input: string,
|
||||
## headers: PStringTable): bool {.procvar.} =
|
||||
## proc handleRequest(client: Socket, input: string,
|
||||
## headers: StringTableRef): bool {.procvar.} =
|
||||
## inc(counter)
|
||||
## client.writeStatusOkTextContent()
|
||||
## client.send("Hello for the $#th time." % $counter & "\c\L")
|
||||
@@ -31,11 +31,11 @@ include "system/inclrtl"
|
||||
import sockets, strutils, os, strtabs, asyncio
|
||||
|
||||
type
|
||||
EScgi* = object of EIO ## the exception that is raised, if a SCGI error occurs
|
||||
ScgiError* = object of IOError ## the exception that is raised, if a SCGI error occurs
|
||||
|
||||
proc scgiError*(msg: string) {.noreturn.} =
|
||||
## raises an EScgi exception with message `msg`.
|
||||
var e: ref EScgi
|
||||
proc raiseScgiError*(msg: string) {.noreturn.} =
|
||||
## raises an ScgiError exception with message `msg`.
|
||||
var e: ref ScgiError
|
||||
new(e)
|
||||
e.msg = msg
|
||||
raise e
|
||||
@@ -45,7 +45,7 @@ proc parseWord(inp: string, outp: var string, start: int): int =
|
||||
while inp[result] != '\0': inc(result)
|
||||
outp = substr(inp, start, result-1)
|
||||
|
||||
proc parseHeaders(s: string, L: int): PStringTable =
|
||||
proc parseHeaders(s: string, L: int): StringTableRef =
|
||||
result = newStringTable()
|
||||
var i = 0
|
||||
while i < L:
|
||||
@@ -54,73 +54,77 @@ proc parseHeaders(s: string, L: int): PStringTable =
|
||||
i = parseWord(s, val, i)+1
|
||||
result[key] = val
|
||||
if s[i] == ',': inc(i)
|
||||
else: scgiError("',' after netstring expected")
|
||||
else: raiseScgiError("',' after netstring expected")
|
||||
|
||||
proc recvChar(s: TSocket): char =
|
||||
proc recvChar(s: Socket): char =
|
||||
var c: char
|
||||
if recv(s, addr(c), sizeof(c)) == sizeof(c):
|
||||
result = c
|
||||
|
||||
type
|
||||
TScgiState* = object of TObject ## SCGI state object
|
||||
server: TSocket
|
||||
ScgiState* = object of RootObj ## SCGI state object
|
||||
server: Socket
|
||||
bufLen: int
|
||||
client*: TSocket ## the client socket to send data to
|
||||
headers*: PStringTable ## the parsed headers
|
||||
client*: Socket ## the client socket to send data to
|
||||
headers*: StringTableRef ## the parsed headers
|
||||
input*: string ## the input buffer
|
||||
|
||||
|
||||
# Async
|
||||
|
||||
TClientMode = enum
|
||||
ClientMode = enum
|
||||
ClientReadChar, ClientReadHeaders, ClientReadContent
|
||||
|
||||
PAsyncClient = ref object
|
||||
c: PAsyncSocket
|
||||
mode: TClientMode
|
||||
AsyncClient = ref object
|
||||
c: AsyncSocket
|
||||
mode: ClientMode
|
||||
dataLen: int
|
||||
headers: PStringTable ## the parsed headers
|
||||
input: string ## the input buffer
|
||||
|
||||
TAsyncScgiState = object
|
||||
handleRequest: proc (client: PAsyncSocket,
|
||||
input: string, headers: PStringTable) {.closure,gcsafe.}
|
||||
asyncServer: PAsyncSocket
|
||||
disp: PDispatcher
|
||||
PAsyncScgiState* = ref TAsyncScgiState
|
||||
|
||||
proc recvBuffer(s: var TScgiState, L: int) =
|
||||
AsyncScgiStateObj = object
|
||||
handleRequest: proc (client: AsyncSocket,
|
||||
input: string,
|
||||
headers: StringTableRef) {.closure, gcsafe.}
|
||||
asyncServer: AsyncSocket
|
||||
disp: Dispatcher
|
||||
AsyncScgiState* = ref AsyncScgiStateObj
|
||||
|
||||
{.deprected: [EScgi: ScgiError, TScgiState: ScgiState,
|
||||
PAsyncScgiState: AsyncScgiState, scgiError: raiseScgiError].}
|
||||
|
||||
proc recvBuffer(s: var ScgiState, L: int) =
|
||||
if L > s.bufLen:
|
||||
s.bufLen = L
|
||||
s.input = newString(L)
|
||||
if L > 0 and recv(s.client, cstring(s.input), L) != L:
|
||||
scgiError("could not read all data")
|
||||
raiseScgiError("could not read all data")
|
||||
setLen(s.input, L)
|
||||
|
||||
proc open*(s: var TScgiState, port = TPort(4000), address = "127.0.0.1",
|
||||
reuseAddr = False) =
|
||||
proc open*(s: var ScgiState, port = Port(4000), address = "127.0.0.1",
|
||||
reuseAddr = false) =
|
||||
## opens a connection.
|
||||
s.bufLen = 4000
|
||||
s.input = newString(s.buflen) # will be reused
|
||||
|
||||
s.server = socket()
|
||||
if s.server == InvalidSocket: osError(osLastError())
|
||||
if s.server == InvalidSocket: raiseOSError(osLastError())
|
||||
new(s.client) # Initialise s.client for `next`
|
||||
if s.server == InvalidSocket: scgiError("could not open socket")
|
||||
if s.server == InvalidSocket: raiseScgiError("could not open socket")
|
||||
#s.server.connect(connectionName, port)
|
||||
if reuseAddr:
|
||||
s.server.setSockOpt(OptReuseAddr, True)
|
||||
bindAddr(s.server, port, address)
|
||||
listen(s.server)
|
||||
|
||||
proc close*(s: var TScgiState) =
|
||||
proc close*(s: var ScgiState) =
|
||||
## closes the connection.
|
||||
s.server.close()
|
||||
|
||||
proc next*(s: var TScgistate, timeout: int = -1): bool =
|
||||
proc next*(s: var ScgiState, timeout: int = -1): bool =
|
||||
## proceed to the first/next request. Waits ``timeout`` miliseconds for a
|
||||
## request, if ``timeout`` is `-1` then this function will never time out.
|
||||
## Returns `True` if a new request has been processed.
|
||||
## Returns `true` if a new request has been processed.
|
||||
var rsocks = @[s.server]
|
||||
if select(rsocks, timeout) == 1 and rsocks.len == 1:
|
||||
new(s.client)
|
||||
@@ -132,17 +136,17 @@ proc next*(s: var TScgistate, timeout: int = -1): bool =
|
||||
s.client.close()
|
||||
return false
|
||||
if d notin strutils.digits:
|
||||
if d != ':': scgiError("':' after length expected")
|
||||
if d != ':': raiseScgiError("':' after length expected")
|
||||
break
|
||||
L = L * 10 + ord(d) - ord('0')
|
||||
recvBuffer(s, L+1)
|
||||
s.headers = parseHeaders(s.input, L)
|
||||
if s.headers["SCGI"] != "1": scgiError("SCGI Version 1 expected")
|
||||
if s.headers["SCGI"] != "1": raiseScgiError("SCGI Version 1 expected")
|
||||
L = parseInt(s.headers["CONTENT_LENGTH"])
|
||||
recvBuffer(s, L)
|
||||
return True
|
||||
|
||||
proc writeStatusOkTextContent*(c: TSocket, contentType = "text/html") =
|
||||
proc writeStatusOkTextContent*(c: Socket, contentType = "text/html") =
|
||||
## sends the following string to the socket `c`::
|
||||
##
|
||||
## Status: 200 OK\r\LContent-Type: text/html\r\L\r\L
|
||||
@@ -151,11 +155,11 @@ proc writeStatusOkTextContent*(c: TSocket, contentType = "text/html") =
|
||||
c.send("Status: 200 OK\r\L" &
|
||||
"Content-Type: $1\r\L\r\L" % contentType)
|
||||
|
||||
proc run*(handleRequest: proc (client: TSocket, input: string,
|
||||
headers: PStringTable): bool {.nimcall,gcsafe.},
|
||||
port = TPort(4000)) =
|
||||
proc run*(handleRequest: proc (client: Socket, input: string,
|
||||
headers: StringTableRef): bool {.nimcall,gcsafe.},
|
||||
port = Port(4000)) =
|
||||
## encapsulates the SCGI object and main loop.
|
||||
var s: TScgiState
|
||||
var s: ScgiState
|
||||
s.open(port)
|
||||
var stop = false
|
||||
while not stop:
|
||||
@@ -166,11 +170,11 @@ proc run*(handleRequest: proc (client: TSocket, input: string,
|
||||
|
||||
# -- AsyncIO start
|
||||
|
||||
proc recvBufferAsync(client: PAsyncClient, L: int): TReadLineResult =
|
||||
proc recvBufferAsync(client: AsyncClient, L: int): ReadLineResult =
|
||||
result = ReadPartialLine
|
||||
var data = ""
|
||||
if L < 1:
|
||||
scgiError("Cannot read negative or zero length: " & $L)
|
||||
raiseScgiError("Cannot read negative or zero length: " & $L)
|
||||
let ret = recvAsync(client.c, data, L)
|
||||
if ret == 0 and data == "":
|
||||
client.c.close()
|
||||
@@ -181,16 +185,16 @@ proc recvBufferAsync(client: PAsyncClient, L: int): TReadLineResult =
|
||||
if ret == L:
|
||||
return ReadFullLine
|
||||
|
||||
proc checkCloseSocket(client: PAsyncClient) =
|
||||
proc checkCloseSocket(client: AsyncClient) =
|
||||
if not client.c.isClosed:
|
||||
if client.c.isSendDataBuffered:
|
||||
client.c.setHandleWrite do (s: PAsyncSocket):
|
||||
client.c.setHandleWrite do (s: AsyncSocket):
|
||||
if not s.isClosed and not s.isSendDataBuffered:
|
||||
s.close()
|
||||
s.delHandleWrite()
|
||||
else: client.c.close()
|
||||
|
||||
proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) =
|
||||
proc handleClientRead(client: AsyncClient, s: AsyncScgiState) =
|
||||
case client.mode
|
||||
of ClientReadChar:
|
||||
while true:
|
||||
@@ -203,7 +207,7 @@ proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) =
|
||||
if ret == -1:
|
||||
return # No more data available
|
||||
if d[0] notin strutils.digits:
|
||||
if d[0] != ':': scgiError("':' after length expected")
|
||||
if d[0] != ':': raiseScgiError("':' after length expected")
|
||||
break
|
||||
client.dataLen = client.dataLen * 10 + ord(d[0]) - ord('0')
|
||||
client.mode = ClientReadHeaders
|
||||
@@ -213,7 +217,7 @@ proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) =
|
||||
case ret
|
||||
of ReadFullLine:
|
||||
client.headers = parseHeaders(client.input, client.input.len-1)
|
||||
if client.headers["SCGI"] != "1": scgiError("SCGI Version 1 expected")
|
||||
if client.headers["SCGI"] != "1": raiseScgiError("SCGI Version 1 expected")
|
||||
client.input = "" # For next part
|
||||
|
||||
let contentLen = parseInt(client.headers["CONTENT_LENGTH"])
|
||||
@@ -236,50 +240,50 @@ proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) =
|
||||
s.handleRequest(client.c, client.input, client.headers)
|
||||
checkCloseSocket(client)
|
||||
|
||||
proc handleAccept(sock: PAsyncSocket, s: PAsyncScgiState) =
|
||||
var client: PAsyncSocket
|
||||
proc handleAccept(sock: AsyncSocket, s: AsyncScgiState) =
|
||||
var client: AsyncSocket
|
||||
new(client)
|
||||
accept(s.asyncServer, client)
|
||||
var asyncClient = PAsyncClient(c: client, mode: ClientReadChar, dataLen: 0,
|
||||
var asyncClient = AsyncClient(c: client, mode: ClientReadChar, dataLen: 0,
|
||||
headers: newStringTable(), input: "")
|
||||
client.handleRead =
|
||||
proc (sock: PAsyncSocket) =
|
||||
proc (sock: AsyncSocket) =
|
||||
handleClientRead(asyncClient, s)
|
||||
s.disp.register(client)
|
||||
|
||||
proc open*(handleRequest: proc (client: PAsyncSocket,
|
||||
input: string, headers: PStringTable) {.
|
||||
proc open*(handleRequest: proc (client: AsyncSocket,
|
||||
input: string, headers: StringTableRef) {.
|
||||
closure, gcsafe.},
|
||||
port = TPort(4000), address = "127.0.0.1",
|
||||
reuseAddr = false): PAsyncScgiState =
|
||||
## Creates an ``PAsyncScgiState`` object which serves as a SCGI server.
|
||||
port = Port(4000), address = "127.0.0.1",
|
||||
reuseAddr = false): AsyncScgiState =
|
||||
## Creates an ``AsyncScgiState`` object which serves as a SCGI server.
|
||||
##
|
||||
## After the execution of ``handleRequest`` the client socket will be closed
|
||||
## automatically unless it has already been closed.
|
||||
var cres: PAsyncScgiState
|
||||
var cres: AsyncScgiState
|
||||
new(cres)
|
||||
cres.asyncServer = AsyncSocket()
|
||||
cres.asyncServer.handleAccept = proc (s: PAsyncSocket) = handleAccept(s, cres)
|
||||
cres.asyncServer.handleAccept = proc (s: AsyncSocket) = handleAccept(s, cres)
|
||||
if reuseAddr:
|
||||
cres.asyncServer.setSockOpt(OptReuseAddr, True)
|
||||
cres.asyncServer.setSockOpt(OptReuseAddr, true)
|
||||
bindAddr(cres.asyncServer, port, address)
|
||||
listen(cres.asyncServer)
|
||||
cres.handleRequest = handleRequest
|
||||
result = cres
|
||||
|
||||
proc register*(d: PDispatcher, s: PAsyncScgiState): PDelegate {.discardable.} =
|
||||
proc register*(d: Dispatcher, s: AsyncScgiState): Delegate {.discardable.} =
|
||||
## Registers ``s`` with dispatcher ``d``.
|
||||
result = d.register(s.asyncServer)
|
||||
s.disp = d
|
||||
|
||||
proc close*(s: PAsyncScgiState) =
|
||||
## Closes the ``PAsyncScgiState``.
|
||||
proc close*(s: AsyncScgiState) =
|
||||
## Closes the ``AsyncScgiState``.
|
||||
s.asyncServer.close()
|
||||
|
||||
when false:
|
||||
var counter = 0
|
||||
proc handleRequest(client: TSocket, input: string,
|
||||
headers: PStringTable): bool {.procvar.} =
|
||||
proc handleRequest(client: Socket, input: string,
|
||||
headers: StringTableRef): bool {.procvar.} =
|
||||
inc(counter)
|
||||
client.writeStatusOkTextContent()
|
||||
client.send("Hello for the $#th time." % $counter & "\c\L")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2014 Dominik Picheta
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -18,57 +18,57 @@ elif defined(windows):
|
||||
else:
|
||||
import posix
|
||||
|
||||
proc hash*(x: TSocketHandle): THash {.borrow.}
|
||||
proc `$`*(x: TSocketHandle): string {.borrow.}
|
||||
proc hash*(x: SocketHandle): THash {.borrow.}
|
||||
proc `$`*(x: SocketHandle): string {.borrow.}
|
||||
|
||||
type
|
||||
TEvent* = enum
|
||||
Event* = enum
|
||||
EvRead, EvWrite
|
||||
|
||||
PSelectorKey* = ref object
|
||||
fd*: TSocketHandle
|
||||
events*: set[TEvent] ## The events which ``fd`` listens for.
|
||||
data*: PObject ## User object.
|
||||
SelectorKey* = ref object
|
||||
fd*: SocketHandle
|
||||
events*: set[Event] ## The events which ``fd`` listens for.
|
||||
data*: RootRef ## User object.
|
||||
|
||||
TReadyInfo* = tuple[key: PSelectorKey, events: set[TEvent]]
|
||||
ReadyInfo* = tuple[key: SelectorKey, events: set[Event]]
|
||||
|
||||
when defined(nimdoc):
|
||||
type
|
||||
PSelector* = ref object
|
||||
Selector* = ref object
|
||||
## An object which holds file descripters to be checked for read/write
|
||||
## status.
|
||||
fds: TTable[TSocketHandle, PSelectorKey]
|
||||
fds: Table[SocketHandle, SelectorKey]
|
||||
|
||||
proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent],
|
||||
data: PObject): PSelectorKey {.discardable.} =
|
||||
proc register*(s: Selector, fd: SocketHandle, events: set[Event],
|
||||
data: RootRef): SelectorKey {.discardable.} =
|
||||
## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent
|
||||
## ``events``.
|
||||
|
||||
proc update*(s: PSelector, fd: TSocketHandle,
|
||||
events: set[TEvent]): PSelectorKey {.discardable.} =
|
||||
proc update*(s: Selector, fd: SocketHandle,
|
||||
events: set[Event]): SelectorKey {.discardable.} =
|
||||
## Updates the events which ``fd`` wants notifications for.
|
||||
|
||||
proc select*(s: PSelector, timeout: int): seq[TReadyInfo] =
|
||||
proc select*(s: Selector, timeout: int): seq[ReadyInfo] =
|
||||
## The ``events`` field of the returned ``key`` contains the original events
|
||||
## for which the ``fd`` was bound. This is contrary to the ``events`` field
|
||||
## of the ``TReadyInfo`` tuple which determines which events are ready
|
||||
## on the ``fd``.
|
||||
|
||||
proc contains*(s: PSelector, fd: TSocketHandle): bool =
|
||||
proc contains*(s: Selector, fd: SocketHandle): bool =
|
||||
## Determines whether selector contains a file descriptor.
|
||||
|
||||
proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey =
|
||||
proc `[]`*(s: Selector, fd: SocketHandle): SelectorKey =
|
||||
## Retrieves the selector key for ``fd``.
|
||||
|
||||
|
||||
elif defined(linux):
|
||||
type
|
||||
PSelector* = ref object
|
||||
Selector* = ref object
|
||||
epollFD: cint
|
||||
events: array[64, epoll_event]
|
||||
fds: TTable[TSocketHandle, PSelectorKey]
|
||||
fds: Table[SocketHandle, SelectorKey]
|
||||
|
||||
proc createEventStruct(events: set[TEvent], fd: TSocketHandle): epoll_event =
|
||||
proc createEventStruct(events: set[Event], fd: SocketHandle): epoll_event =
|
||||
if EvRead in events:
|
||||
result.events = EPOLLIN
|
||||
if EvWrite in events:
|
||||
@@ -76,22 +76,22 @@ elif defined(linux):
|
||||
result.events = result.events or EPOLLRDHUP
|
||||
result.data.fd = fd.cint
|
||||
|
||||
proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent],
|
||||
data: PObject): PSelectorKey {.discardable.} =
|
||||
proc register*(s: Selector, fd: SocketHandle, events: set[Event],
|
||||
data: RootRef): SelectorKey {.discardable.} =
|
||||
## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent
|
||||
## ``events``.
|
||||
var event = createEventStruct(events, fd)
|
||||
if events != {}:
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
|
||||
OSError(OSLastError())
|
||||
raiseOSError(osLastError())
|
||||
|
||||
var key = PSelectorKey(fd: fd, events: events, data: data)
|
||||
var key = SelectorKey(fd: fd, events: events, data: data)
|
||||
|
||||
s.fds[fd] = key
|
||||
result = key
|
||||
|
||||
proc update*(s: PSelector, fd: TSocketHandle,
|
||||
events: set[TEvent]): PSelectorKey {.discardable.} =
|
||||
proc update*(s: Selector, fd: SocketHandle,
|
||||
events: set[Event]): SelectorKey {.discardable.} =
|
||||
## Updates the events which ``fd`` wants notifications for.
|
||||
if s.fds[fd].events != events:
|
||||
if events == {}:
|
||||
@@ -101,7 +101,7 @@ elif defined(linux):
|
||||
# because its got fds which are waiting for no events and
|
||||
# are therefore constantly ready. (leading to 100% CPU usage).
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
|
||||
OSError(OSLastError())
|
||||
raiseOSError(osLastError())
|
||||
s.fds[fd].events = events
|
||||
else:
|
||||
var event = createEventStruct(events, fd)
|
||||
@@ -109,36 +109,36 @@ elif defined(linux):
|
||||
# This fd is idle. It's not a member of this epoll instance and must
|
||||
# be re-registered.
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
|
||||
OSError(OSLastError())
|
||||
raiseOSError(osLastError())
|
||||
else:
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
|
||||
OSError(OSLastError())
|
||||
raiseOSError(osLastError())
|
||||
s.fds[fd].events = events
|
||||
|
||||
result = s.fds[fd]
|
||||
|
||||
proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} =
|
||||
proc unregister*(s: Selector, fd: SocketHandle): SelectorKey {.discardable.} =
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
|
||||
let err = OSLastError()
|
||||
let err = osLastError()
|
||||
if err.cint notin {ENOENT, EBADF}: # TODO: Why do we sometimes get an EBADF? Is this normal?
|
||||
OSError(err)
|
||||
raiseOSError(err)
|
||||
result = s.fds[fd]
|
||||
s.fds.del(fd)
|
||||
|
||||
proc close*(s: PSelector) =
|
||||
if s.epollFD.close() != 0: OSError(OSLastError())
|
||||
proc close*(s: Selector) =
|
||||
if s.epollFD.close() != 0: raiseOSError(osLastError())
|
||||
dealloc(addr s.events) # TODO: Test this
|
||||
|
||||
proc epollHasFd(s: PSelector, fd: TSocketHandle): bool =
|
||||
proc epollHasFd(s: Selector, fd: SocketHandle): bool =
|
||||
result = true
|
||||
var event = createEventStruct(s.fds[fd].events, fd)
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
|
||||
let err = osLastError()
|
||||
if err.cint in {ENOENT, EBADF}:
|
||||
return false
|
||||
OSError(OSLastError())
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc select*(s: PSelector, timeout: int): seq[TReadyInfo] =
|
||||
proc select*(s: Selector, timeout: int): seq[ReadyInfo] =
|
||||
##
|
||||
## The ``events`` field of the returned ``key`` contains the original events
|
||||
## for which the ``fd`` was bound. This is contrary to the ``events`` field
|
||||
@@ -146,12 +146,12 @@ elif defined(linux):
|
||||
## on the ``fd``.
|
||||
result = @[]
|
||||
let evNum = epoll_wait(s.epollFD, addr s.events[0], 64.cint, timeout.cint)
|
||||
if evNum < 0: OSError(OSLastError())
|
||||
if evNum < 0: raiseOSError(osLastError())
|
||||
if evNum == 0: return @[]
|
||||
for i in 0 .. <evNum:
|
||||
let fd = s.events[i].data.fd.TSocketHandle
|
||||
let fd = s.events[i].data.fd.SocketHandle
|
||||
|
||||
var evSet: set[TEvent] = {}
|
||||
var evSet: set[Event] = {}
|
||||
if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
|
||||
if (s.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
|
||||
let selectorKey = s.fds[fd]
|
||||
@@ -160,15 +160,15 @@ elif defined(linux):
|
||||
|
||||
#echo("Epoll: ", result[i].key.fd, " ", result[i].events, " ", result[i].key.events)
|
||||
|
||||
proc newSelector*(): PSelector =
|
||||
proc newSelector*(): Selector =
|
||||
new result
|
||||
result.epollFD = epoll_create(64)
|
||||
#result.events = cast[array[64, epoll_event]](alloc0(sizeof(epoll_event)*64))
|
||||
result.fds = initTable[TSocketHandle, PSelectorKey]()
|
||||
result.fds = initTable[SocketHandle, SelectorKey]()
|
||||
if result.epollFD < 0:
|
||||
OSError(OSLastError())
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc contains*(s: PSelector, fd: TSocketHandle): bool =
|
||||
proc contains*(s: Selector, fd: SocketHandle): bool =
|
||||
## Determines whether selector contains a file descriptor.
|
||||
if s.fds.hasKey(fd):
|
||||
# Ensure the underlying epoll instance still contains this fd.
|
||||
@@ -179,46 +179,46 @@ elif defined(linux):
|
||||
else:
|
||||
return false
|
||||
|
||||
proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey =
|
||||
proc `[]`*(s: Selector, fd: SocketHandle): SelectorKey =
|
||||
## Retrieves the selector key for ``fd``.
|
||||
return s.fds[fd]
|
||||
|
||||
elif not defined(nimdoc):
|
||||
# TODO: kqueue for bsd/mac os x.
|
||||
type
|
||||
PSelector* = ref object
|
||||
fds: TTable[TSocketHandle, PSelectorKey]
|
||||
Selector* = ref object
|
||||
fds: Table[SocketHandle, SelectorKey]
|
||||
|
||||
proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent],
|
||||
data: PObject): PSelectorKey {.discardable.} =
|
||||
proc register*(s: Selector, fd: SocketHandle, events: set[Event],
|
||||
data: RootRef): SelectorKey {.discardable.} =
|
||||
if s.fds.hasKey(fd):
|
||||
raise newException(EInvalidValue, "File descriptor already exists.")
|
||||
var sk = PSelectorKey(fd: fd, events: events, data: data)
|
||||
raise newException(ValueError, "File descriptor already exists.")
|
||||
var sk = SelectorKey(fd: fd, events: events, data: data)
|
||||
s.fds[fd] = sk
|
||||
result = sk
|
||||
|
||||
proc update*(s: PSelector, fd: TSocketHandle,
|
||||
events: set[TEvent]): PSelectorKey {.discardable.} =
|
||||
proc update*(s: Selector, fd: SocketHandle,
|
||||
events: set[Event]): SelectorKey {.discardable.} =
|
||||
## Updates the events which ``fd`` wants notifications for.
|
||||
if not s.fds.hasKey(fd):
|
||||
raise newException(EInvalidValue, "File descriptor not found.")
|
||||
raise newException(ValueError, "File descriptor not found.")
|
||||
|
||||
s.fds[fd].events = events
|
||||
result = s.fds[fd]
|
||||
|
||||
proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} =
|
||||
proc unregister*(s: Selector, fd: SocketHandle): SelectorKey {.discardable.} =
|
||||
result = s.fds[fd]
|
||||
s.fds.del(fd)
|
||||
|
||||
proc close*(s: PSelector) = nil
|
||||
proc close*(s: Selector) = discard
|
||||
|
||||
proc timeValFromMilliseconds(timeout: int): TTimeVal =
|
||||
proc timeValFromMilliseconds(timeout: int): TimeVal =
|
||||
if timeout != -1:
|
||||
var seconds = timeout div 1000
|
||||
result.tv_sec = seconds.int32
|
||||
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
|
||||
|
||||
proc createFdSet(rd, wr: var TFdSet, fds: TTable[TSocketHandle, PSelectorKey],
|
||||
proc createFdSet(rd, wr: var FdSet, fds: Table[SocketHandle, SelectorKey],
|
||||
m: var int) =
|
||||
FD_ZERO(rd); FD_ZERO(wr)
|
||||
for k, v in pairs(fds):
|
||||
@@ -229,22 +229,22 @@ elif not defined(nimdoc):
|
||||
m = max(m, int(k))
|
||||
FD_SET(k, wr)
|
||||
|
||||
proc getReadyFDs(rd, wr: var TFdSet, fds: TTable[TSocketHandle, PSelectorKey]):
|
||||
seq[TReadyInfo] =
|
||||
proc getReadyFDs(rd, wr: var FdSet, fds: Table[SocketHandle, SelectorKey]):
|
||||
seq[ReadyInfo] =
|
||||
result = @[]
|
||||
for k, v in pairs(fds):
|
||||
var events: set[TEvent] = {}
|
||||
var events: set[Event] = {}
|
||||
if FD_ISSET(k, rd) != 0'i32:
|
||||
events = events + {EvRead}
|
||||
if FD_ISSET(k, wr) != 0'i32:
|
||||
events = events + {EvWrite}
|
||||
result.add((v, events))
|
||||
|
||||
proc select(fds: TTable[TSocketHandle, PSelectorKey], timeout = 500):
|
||||
seq[TReadyInfo] =
|
||||
var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout)
|
||||
proc select(fds: Table[SocketHandle, SelectorKey], timeout = 500):
|
||||
seq[ReadyInfo] =
|
||||
var tv {.noInit.}: TimeVal = timeValFromMilliseconds(timeout)
|
||||
|
||||
var rd, wr: TFdSet
|
||||
var rd, wr: FdSet
|
||||
var m = 0
|
||||
createFdSet(rd, wr, fds, m)
|
||||
|
||||
@@ -255,26 +255,26 @@ elif not defined(nimdoc):
|
||||
retCode = int(select(cint(m+1), addr(rd), addr(wr), nil, nil))
|
||||
|
||||
if retCode < 0:
|
||||
OSError(OSLastError())
|
||||
raiseOSError(osLastError())
|
||||
elif retCode == 0:
|
||||
return @[]
|
||||
else:
|
||||
return getReadyFDs(rd, wr, fds)
|
||||
|
||||
proc select*(s: PSelector, timeout: int): seq[TReadyInfo] =
|
||||
proc select*(s: Selector, timeout: int): seq[ReadyInfo] =
|
||||
result = select(s.fds, timeout)
|
||||
|
||||
proc newSelector*(): PSelector =
|
||||
proc newSelector*(): Selector =
|
||||
new result
|
||||
result.fds = initTable[TSocketHandle, PSelectorKey]()
|
||||
result.fds = initTable[SocketHandle, SelectorKey]()
|
||||
|
||||
proc contains*(s: PSelector, fd: TSocketHandle): bool =
|
||||
proc contains*(s: Selector, fd: SocketHandle): bool =
|
||||
return s.fds.hasKey(fd)
|
||||
|
||||
proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey =
|
||||
proc `[]`*(s: Selector, fd: SocketHandle): SelectorKey =
|
||||
return s.fds[fd]
|
||||
|
||||
proc contains*(s: PSelector, key: PSelectorKey): bool =
|
||||
proc contains*(s: Selector, key: SelectorKey): bool =
|
||||
## Determines whether selector contains this selector key. More accurate
|
||||
## than checking if the file descriptor is in the selector because it
|
||||
## ensures that the keys are equal. File descriptors may not always be
|
||||
@@ -282,20 +282,24 @@ proc contains*(s: PSelector, key: PSelectorKey): bool =
|
||||
## the new one may have the same value.
|
||||
return key.fd in s and s.fds[key.fd] == key
|
||||
|
||||
{.deprecated: [TEvent: Event, PSelectorKey: SelectorKey,
|
||||
TReadyInfo: ReadyInfo, PSelector: Selector].}
|
||||
|
||||
|
||||
when isMainModule and not defined(nimdoc):
|
||||
# Select()
|
||||
import sockets
|
||||
type
|
||||
PSockWrapper = ref object of PObject
|
||||
sock: TSocket
|
||||
SockWrapper = ref object of RootObj
|
||||
sock: Socket
|
||||
|
||||
var sock = socket()
|
||||
if sock == sockets.InvalidSocket: osError(osLastError())
|
||||
if sock == sockets.InvalidSocket: raiseOSError(osLastError())
|
||||
#sock.setBlocking(false)
|
||||
sock.connect("irc.freenode.net", TPort(6667))
|
||||
sock.connect("irc.freenode.net", Port(6667))
|
||||
|
||||
var selector = newSelector()
|
||||
var data = PSockWrapper(sock: sock)
|
||||
var data = SockWrapper(sock: sock)
|
||||
let key = selector.register(sock.getFD, {EvWrite}, data)
|
||||
var i = 0
|
||||
while true:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Dominik Picheta
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -16,11 +16,11 @@
|
||||
## Example gmail use:
|
||||
##
|
||||
##
|
||||
## .. code-block:: Nimrod
|
||||
## var msg = createMessage("Hello from Nimrod's SMTP",
|
||||
## .. code-block:: Nim
|
||||
## var msg = createMessage("Hello from Nim's SMTP",
|
||||
## "Hello!.\n Is this awesome or what?",
|
||||
## @["foo@gmail.com"])
|
||||
## var smtp = connect("smtp.gmail.com", 465, True, True)
|
||||
## var smtp = connect("smtp.gmail.com", 465, true, true)
|
||||
## smtp.auth("username", "password")
|
||||
## smtp.sendmail("username@gmail.com", @["foo@gmail.com"], $msg)
|
||||
##
|
||||
@@ -31,18 +31,20 @@
|
||||
import sockets, strutils, strtabs, base64, os
|
||||
|
||||
type
|
||||
TSMTP* {.final.} = object
|
||||
sock: TSocket
|
||||
debug: Bool
|
||||
Smtp* = object
|
||||
sock: Socket
|
||||
debug: bool
|
||||
|
||||
TMessage* {.final.} = object
|
||||
Message* = object
|
||||
msgTo: seq[string]
|
||||
msgCc: seq[string]
|
||||
msgSubject: string
|
||||
msgOtherHeaders: PStringTable
|
||||
msgOtherHeaders: StringTableRef
|
||||
msgBody: string
|
||||
|
||||
EInvalidReply* = object of EIO
|
||||
ReplyError* = object of IOError
|
||||
|
||||
{.deprecated: [EInvalidReply: ReplyError, TMessage: Message, TSMTP: Smtp].}
|
||||
|
||||
proc debugSend(smtp: TSMTP, cmd: string) =
|
||||
if smtp.debug:
|
||||
@@ -170,7 +172,7 @@ when isMainModule:
|
||||
#smtp.sendmail("root@localhost", @["dominik@localhost"], $msg)
|
||||
|
||||
#echo(decode("a17sm3701420wbe.12"))
|
||||
var msg = createMessage("Hello from Nimrod's SMTP!",
|
||||
var msg = createMessage("Hello from Nim's SMTP!",
|
||||
"Hello!!!!.\n Is this awesome or what?",
|
||||
@["someone@yahoo.com", "someone@gmail.com"])
|
||||
echo(msg)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2013 Andreas Rumpf, Dominik Picheta
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -14,8 +14,8 @@
|
||||
## the ``socket`` function to `false`. Be aware that some functions may not yet
|
||||
## support buffered sockets (mainly the recvFrom function).
|
||||
##
|
||||
## Most procedures raise EOS on error, but some may return ``-1`` or a boolean
|
||||
## ``false``.
|
||||
## Most procedures raise OSError on error, but some may return ``-1`` or a
|
||||
## boolean ``false``.
|
||||
##
|
||||
## SSL is supported through the OpenSSL library. This support can be activated
|
||||
## by compiling with the ``-d:ssl`` switch. When an SSL socket is used it will
|
||||
@@ -47,25 +47,29 @@ else:
|
||||
|
||||
when defined(ssl):
|
||||
type
|
||||
ESSL* = object of ESynch
|
||||
SSLError* = object of Exception
|
||||
|
||||
TSSLCVerifyMode* = enum
|
||||
SSLCVerifyMode* = enum
|
||||
CVerifyNone, CVerifyPeer
|
||||
|
||||
TSSLProtVersion* = enum
|
||||
SSLProtVersion* = enum
|
||||
protSSLv2, protSSLv3, protTLSv1, protSSLv23
|
||||
|
||||
PSSLContext* = distinct PSSLCTX
|
||||
SSLContext* = distinct PSSLCTX
|
||||
|
||||
TSSLAcceptResult* = enum
|
||||
SSLAcceptResult* = enum
|
||||
AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess
|
||||
|
||||
{.deprecated: [ESSL: SSLError, TSSLCVerifyMode: SSLCVerifyMode,
|
||||
TSSLProtVersion: SSLProtVersion, PSSLContext: SSLContext,
|
||||
TSSLAcceptResult: SSLAcceptResult].}
|
||||
|
||||
const
|
||||
BufferSize*: int = 4000 ## size of a buffered socket's buffer
|
||||
|
||||
type
|
||||
TSocketImpl = object ## socket type
|
||||
fd: TSocketHandle
|
||||
fd: SocketHandle
|
||||
case isBuffered: bool # determines whether this socket is buffered.
|
||||
of true:
|
||||
buffer: array[0..BufferSize, char]
|
||||
@@ -76,31 +80,31 @@ type
|
||||
case isSsl: bool
|
||||
of true:
|
||||
sslHandle: PSSL
|
||||
sslContext: PSSLContext
|
||||
sslContext: SSLContext
|
||||
sslNoHandshake: bool # True if needs handshake.
|
||||
sslHasPeekChar: bool
|
||||
sslPeekChar: char
|
||||
of false: nil
|
||||
nonblocking: bool
|
||||
|
||||
TSocket* = ref TSocketImpl
|
||||
Socket* = ref TSocketImpl
|
||||
|
||||
TPort* = distinct uint16 ## port type
|
||||
Port* = distinct uint16 ## port type
|
||||
|
||||
TDomain* = enum ## domain, which specifies the protocol family of the
|
||||
Domain* = enum ## domain, which specifies the protocol family of the
|
||||
## created socket. Other domains than those that are listed
|
||||
## here are unsupported.
|
||||
AF_UNIX, ## for local socket (using a file). Unsupported on Windows.
|
||||
AF_INET = 2, ## for network protocol IPv4 or
|
||||
AF_INET6 = 23 ## for network protocol IPv6.
|
||||
|
||||
TType* = enum ## second argument to `socket` proc
|
||||
SockType* = enum ## second argument to `socket` proc
|
||||
SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets
|
||||
SOCK_DGRAM = 2, ## datagram service or Datagram Sockets
|
||||
SOCK_RAW = 3, ## raw protocols atop the network layer.
|
||||
SOCK_SEQPACKET = 5 ## reliable sequenced packet service
|
||||
|
||||
TProtocol* = enum ## third argument to `socket` proc
|
||||
Protocol* = enum ## third argument to `socket` proc
|
||||
IPPROTO_TCP = 6, ## Transmission control protocol.
|
||||
IPPROTO_UDP = 17, ## User datagram protocol.
|
||||
IPPROTO_IP, ## Internet protocol. Unsupported on Windows.
|
||||
@@ -108,33 +112,39 @@ type
|
||||
IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows.
|
||||
IPPROTO_ICMP ## Control message protocol. Unsupported on Windows.
|
||||
|
||||
TServent* {.pure, final.} = object ## information about a service
|
||||
Servent* = object ## information about a service
|
||||
name*: string
|
||||
aliases*: seq[string]
|
||||
port*: TPort
|
||||
port*: Port
|
||||
proto*: string
|
||||
|
||||
Thostent* {.pure, final.} = object ## information about a given host
|
||||
Hostent* = object ## information about a given host
|
||||
name*: string
|
||||
aliases*: seq[string]
|
||||
addrtype*: TDomain
|
||||
addrtype*: Domain
|
||||
length*: int
|
||||
addrList*: seq[string]
|
||||
|
||||
TSOBool* = enum ## Boolean socket options.
|
||||
SOBool* = enum ## Boolean socket options.
|
||||
OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive,
|
||||
OptOOBInline, OptReuseAddr
|
||||
|
||||
TRecvLineResult* = enum ## result for recvLineAsync
|
||||
RecvLineResult* = enum ## result for recvLineAsync
|
||||
RecvFullLine, RecvPartialLine, RecvDisconnected, RecvFail
|
||||
|
||||
TReadLineResult* = enum ## result for readLineAsync
|
||||
ReadLineResult* = enum ## result for readLineAsync
|
||||
ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone
|
||||
|
||||
ETimeout* = object of Exception
|
||||
TimeoutError* = object of Exception
|
||||
|
||||
{.deprecated: [TSocket: Socket, TType: SockType, TPort: Port, TDomain: Domain,
|
||||
TProtocol: Protocol, TServent: Servent, THostent: Hostent,
|
||||
TSOBool: SOBool, TRecvLineResult: RecvLineResult,
|
||||
TReadLineResult: ReadLineResult, ETimeout: TimeoutError].}
|
||||
|
||||
|
||||
let
|
||||
invalidSocket*: TSocket = nil ## invalid socket
|
||||
invalidSocket*: Socket = nil ## invalid socket
|
||||
|
||||
when defined(windows):
|
||||
let
|
||||
@@ -143,7 +153,7 @@ else:
|
||||
let
|
||||
osInvalidSocket = posix.INVALID_SOCKET
|
||||
|
||||
proc newTSocket(fd: TSocketHandle, isBuff: bool): TSocket =
|
||||
proc newTSocket(fd: SocketHandle, isBuff: bool): Socket =
|
||||
if fd == osInvalidSocket:
|
||||
return nil
|
||||
new(result)
|
||||
@@ -153,10 +163,10 @@ proc newTSocket(fd: TSocketHandle, isBuff: bool): TSocket =
|
||||
result.currPos = 0
|
||||
result.nonblocking = false
|
||||
|
||||
proc `==`*(a, b: TPort): bool {.borrow.}
|
||||
proc `==`*(a, b: Port): bool {.borrow.}
|
||||
## ``==`` for ports.
|
||||
|
||||
proc `$`*(p: TPort): string {.borrow.}
|
||||
proc `$`*(p: Port): string {.borrow.}
|
||||
## returns the port number as a string
|
||||
|
||||
proc ntohl*(x: int32): int32 =
|
||||
@@ -189,14 +199,14 @@ proc htons*(x: int16): int16 =
|
||||
result = sockets.ntohs(x)
|
||||
|
||||
when defined(Posix):
|
||||
proc toInt(domain: TDomain): cint =
|
||||
proc toInt(domain: Domain): cint =
|
||||
case domain
|
||||
of AF_UNIX: result = posix.AF_UNIX
|
||||
of AF_INET: result = posix.AF_INET
|
||||
of AF_INET6: result = posix.AF_INET6
|
||||
else: discard
|
||||
|
||||
proc toInt(typ: TType): cint =
|
||||
proc toInt(typ: SockType): cint =
|
||||
case typ
|
||||
of SOCK_STREAM: result = posix.SOCK_STREAM
|
||||
of SOCK_DGRAM: result = posix.SOCK_DGRAM
|
||||
@@ -204,7 +214,7 @@ when defined(Posix):
|
||||
of SOCK_RAW: result = posix.SOCK_RAW
|
||||
else: discard
|
||||
|
||||
proc toInt(p: TProtocol): cint =
|
||||
proc toInt(p: Protocol): cint =
|
||||
case p
|
||||
of IPPROTO_TCP: result = posix.IPPROTO_TCP
|
||||
of IPPROTO_UDP: result = posix.IPPROTO_UDP
|
||||
@@ -215,13 +225,13 @@ when defined(Posix):
|
||||
else: discard
|
||||
|
||||
else:
|
||||
proc toInt(domain: TDomain): cint =
|
||||
proc toInt(domain: Domain): cint =
|
||||
result = toU16(ord(domain))
|
||||
|
||||
proc toInt(typ: TType): cint =
|
||||
proc toInt(typ: SockType): cint =
|
||||
result = cint(ord(typ))
|
||||
|
||||
proc toInt(p: TProtocol): cint =
|
||||
proc toInt(p: Protocol): cint =
|
||||
result = cint(ord(p))
|
||||
|
||||
proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
|
||||
@@ -336,7 +346,7 @@ when defined(ssl):
|
||||
if SSLSetFd(socket.sslHandle, socket.fd) != 1:
|
||||
SSLError()
|
||||
|
||||
proc socketError*(socket: TSocket, err: int = -1, async = false) =
|
||||
proc raiseSocketError*(socket: TSocket, err: int = -1, async = false) =
|
||||
## Raises proper errors based on return values of ``recv`` functions.
|
||||
##
|
||||
## If ``async`` is ``True`` no error will be thrown in the case when the
|
||||
@@ -370,18 +380,18 @@ proc socketError*(socket: TSocket, err: int = -1, async = false) =
|
||||
when defined(windows):
|
||||
if lastError.int32 == WSAEWOULDBLOCK:
|
||||
return
|
||||
else: osError(lastError)
|
||||
else: raiseOSError(lastError)
|
||||
else:
|
||||
if lastError.int32 == EAGAIN or lastError.int32 == EWOULDBLOCK:
|
||||
return
|
||||
else: osError(lastError)
|
||||
else: osError(lastError)
|
||||
else: raiseOSError(lastError)
|
||||
else: raiseOSError(lastError)
|
||||
|
||||
proc listen*(socket: TSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
|
||||
## Marks ``socket`` as accepting connections.
|
||||
## ``Backlog`` specifies the maximum length of the
|
||||
## queue of pending connections.
|
||||
if listen(socket.fd, cint(backlog)) < 0'i32: osError(osLastError())
|
||||
if listen(socket.fd, cint(backlog)) < 0'i32: raiseOSError(osLastError())
|
||||
|
||||
proc invalidIp4(s: string) {.noreturn, noinline.} =
|
||||
raise newException(ValueError, "invalid ip4 address: " & s)
|
||||
@@ -420,7 +430,7 @@ template gaiNim(a, p, h, list: expr): stmt =
|
||||
var gaiResult = getaddrinfo(a, $p, addr(h), list)
|
||||
if gaiResult != 0'i32:
|
||||
when defined(windows):
|
||||
osError(osLastError())
|
||||
raiseOSError(osLastError())
|
||||
else:
|
||||
raise newException(OSError, $gai_strerror(gaiResult))
|
||||
|
||||
@@ -440,7 +450,7 @@ proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {.
|
||||
name.sin_addr.s_addr = sockets.htonl(INADDR_ANY)
|
||||
if bindSocket(socket.fd, cast[ptr TSockAddr](addr(name)),
|
||||
sizeof(name).TSocklen) < 0'i32:
|
||||
osError(osLastError())
|
||||
raiseOSError(osLastError())
|
||||
else:
|
||||
var hints: Taddrinfo
|
||||
var aiList: ptr Taddrinfo = nil
|
||||
@@ -449,7 +459,7 @@ proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {.
|
||||
hints.ai_protocol = toInt(IPPROTO_TCP)
|
||||
gaiNim(address, port, hints, aiList)
|
||||
if bindSocket(socket.fd, aiList.ai_addr, aiList.ai_addrlen.TSocklen) < 0'i32:
|
||||
osError(osLastError())
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc getSockName*(socket: TSocket): TPort =
|
||||
## returns the socket's associated port number.
|
||||
@@ -463,7 +473,7 @@ proc getSockName*(socket: TSocket): TPort =
|
||||
var namelen = sizeof(name).TSocklen
|
||||
if getsockname(socket.fd, cast[ptr TSockAddr](addr(name)),
|
||||
addr(namelen)) == -1'i32:
|
||||
osError(osLastError())
|
||||
raiseOSError(osLastError())
|
||||
result = TPort(sockets.ntohs(name.sin_port))
|
||||
|
||||
template acceptAddrPlain(noClientRet, successRet: expr,
|
||||
@@ -484,7 +494,7 @@ template acceptAddrPlain(noClientRet, successRet: expr,
|
||||
return
|
||||
else:
|
||||
return noClientRet
|
||||
else: osError(err)
|
||||
else: raiseOSError(err)
|
||||
else:
|
||||
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
|
||||
client = invalidSocket
|
||||
@@ -493,7 +503,7 @@ template acceptAddrPlain(noClientRet, successRet: expr,
|
||||
return
|
||||
else:
|
||||
return noClientRet
|
||||
else: osError(err)
|
||||
else: raiseOSError(err)
|
||||
else:
|
||||
client.fd = sock
|
||||
client.isBuffered = server.isBuffered
|
||||
@@ -680,7 +690,7 @@ proc getHostByAddr*(ip: string): Thostent {.tags: [ReadIOEffect].} =
|
||||
when defined(windows):
|
||||
var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
|
||||
cint(sockets.AF_INET))
|
||||
if s == nil: osError(osLastError())
|
||||
if s == nil: raiseOSError(osLastError())
|
||||
else:
|
||||
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).TSocklen,
|
||||
cint(posix.AF_INET))
|
||||
@@ -707,7 +717,7 @@ proc getHostByName*(name: string): Thostent {.tags: [ReadIOEffect].} =
|
||||
var s = winlean.gethostbyname(name)
|
||||
else:
|
||||
var s = posix.gethostbyname(name)
|
||||
if s == nil: osError(osLastError())
|
||||
if s == nil: raiseOSError(osLastError())
|
||||
result.name = $s.h_name
|
||||
result.aliases = cstringArrayToSeq(s.h_aliases)
|
||||
when defined(windows):
|
||||
@@ -729,7 +739,7 @@ proc getSockOptInt*(socket: TSocket, level, optname: int): int {.
|
||||
var size = sizeof(res).TSocklen
|
||||
if getsockopt(socket.fd, cint(level), cint(optname),
|
||||
addr(res), addr(size)) < 0'i32:
|
||||
osError(osLastError())
|
||||
raiseOSError(osLastError())
|
||||
result = int(res)
|
||||
|
||||
proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {.
|
||||
@@ -738,7 +748,7 @@ proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {.
|
||||
var value = cint(optval)
|
||||
if setsockopt(socket.fd, cint(level), cint(optname), addr(value),
|
||||
sizeof(value).TSocklen) < 0'i32:
|
||||
osError(osLastError())
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc toCInt(opt: TSOBool): cint =
|
||||
case opt
|
||||
@@ -757,7 +767,7 @@ proc getSockOpt*(socket: TSocket, opt: TSOBool, level = SOL_SOCKET): bool {.
|
||||
var size = sizeof(res).TSocklen
|
||||
if getsockopt(socket.fd, cint(level), toCInt(opt),
|
||||
addr(res), addr(size)) < 0'i32:
|
||||
osError(osLastError())
|
||||
raiseOSError(osLastError())
|
||||
result = res != 0
|
||||
|
||||
proc setSockOpt*(socket: TSocket, opt: TSOBool, value: bool, level = SOL_SOCKET) {.
|
||||
@@ -766,7 +776,7 @@ proc setSockOpt*(socket: TSocket, opt: TSOBool, value: bool, level = SOL_SOCKET)
|
||||
var valuei = cint(if value: 1 else: 0)
|
||||
if setsockopt(socket.fd, cint(level), toCInt(opt), addr(valuei),
|
||||
sizeof(valuei).TSocklen) < 0'i32:
|
||||
osError(osLastError())
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc connect*(socket: TSocket, address: string, port = TPort(0),
|
||||
af: TDomain = AF_INET) {.tags: [ReadIOEffect].} =
|
||||
@@ -784,7 +794,7 @@ proc connect*(socket: TSocket, address: string, port = TPort(0),
|
||||
gaiNim(address, port, hints, aiList)
|
||||
# try all possibilities:
|
||||
var success = false
|
||||
var lastError: TOSErrorCode
|
||||
var lastError: OSErrorCode
|
||||
var it = aiList
|
||||
while it != nil:
|
||||
if connect(socket.fd, it.ai_addr, it.ai_addrlen.TSocklen) == 0'i32:
|
||||
@@ -794,7 +804,7 @@ proc connect*(socket: TSocket, address: string, port = TPort(0),
|
||||
it = it.ai_next
|
||||
|
||||
freeaddrinfo(aiList)
|
||||
if not success: osError(lastError)
|
||||
if not success: raiseOSError(lastError)
|
||||
|
||||
when defined(ssl):
|
||||
if socket.isSSL:
|
||||
@@ -847,7 +857,7 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0),
|
||||
gaiNim(name, port, hints, aiList)
|
||||
# try all possibilities:
|
||||
var success = false
|
||||
var lastError: TOSErrorCode
|
||||
var lastError: OSErrorCode
|
||||
var it = aiList
|
||||
while it != nil:
|
||||
var ret = connect(socket.fd, it.ai_addr, it.ai_addrlen.TSocklen)
|
||||
@@ -869,7 +879,7 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0),
|
||||
it = it.ai_next
|
||||
|
||||
freeaddrinfo(aiList)
|
||||
if not success: osError(lastError)
|
||||
if not success: raiseOSError(lastError)
|
||||
when defined(ssl):
|
||||
if socket.isSSL:
|
||||
socket.sslNoHandshake = true
|
||||
@@ -1133,7 +1143,7 @@ proc waitFor(socket: TSocket, waited: var float, timeout, size: int,
|
||||
result = min(result, size)
|
||||
else:
|
||||
if timeout - int(waited * 1000.0) < 1:
|
||||
raise newException(ETimeout, "Call to '" & funcName & "' timed out.")
|
||||
raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
|
||||
|
||||
when defined(ssl):
|
||||
if socket.isSSL:
|
||||
@@ -1147,9 +1157,9 @@ proc waitFor(socket: TSocket, waited: var float, timeout, size: int,
|
||||
var s = @[socket]
|
||||
var startTime = epochTime()
|
||||
let selRet = select(s, timeout - int(waited * 1000.0))
|
||||
if selRet < 0: osError(osLastError())
|
||||
if selRet < 0: raiseOSError(osLastError())
|
||||
if selRet != 1:
|
||||
raise newException(ETimeout, "Call to '" & funcName & "' timed out.")
|
||||
raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
|
||||
waited += (epochTime() - startTime)
|
||||
|
||||
proc recv*(socket: TSocket, data: pointer, size: int, timeout: int): int {.
|
||||
@@ -1185,7 +1195,7 @@ proc recv*(socket: TSocket, data: var string, size: int, timeout = -1): int =
|
||||
result = recv(socket, cstring(data), size, timeout)
|
||||
if result < 0:
|
||||
data.setLen(0)
|
||||
socket.socketError(result)
|
||||
socket.raiseSocketError(result)
|
||||
data.setLen(result)
|
||||
|
||||
proc recvAsync*(socket: TSocket, data: var string, size: int): int =
|
||||
@@ -1199,7 +1209,7 @@ proc recvAsync*(socket: TSocket, data: var string, size: int): int =
|
||||
result = recv(socket, cstring(data), size)
|
||||
if result < 0:
|
||||
data.setLen(0)
|
||||
socket.socketError(async = true)
|
||||
socket.raiseSocketError(async = true)
|
||||
result = -1
|
||||
data.setLen(result)
|
||||
|
||||
@@ -1296,14 +1306,14 @@ proc readLine*(socket: TSocket, line: var TaintedString, timeout = -1) {.
|
||||
var c: char
|
||||
discard waitFor(socket, waited, timeout, 1, "readLine")
|
||||
var n = recv(socket, addr(c), 1)
|
||||
if n < 0: socket.socketError()
|
||||
if n < 0: socket.raiseSocketError()
|
||||
elif n == 0: return
|
||||
if c == '\r':
|
||||
discard waitFor(socket, waited, timeout, 1, "readLine")
|
||||
n = peekChar(socket, c)
|
||||
if n > 0 and c == '\L':
|
||||
discard recv(socket, addr(c), 1)
|
||||
elif n <= 0: socket.socketError()
|
||||
elif n <= 0: socket.raiseSocketError()
|
||||
addNLIfEmpty()
|
||||
return
|
||||
elif c == '\L':
|
||||
@@ -1357,7 +1367,7 @@ proc readLineAsync*(socket: TSocket,
|
||||
setLen(line.string, 0)
|
||||
|
||||
template errorOrNone =
|
||||
socket.socketError(async = true)
|
||||
socket.raiseSocketError(async = true)
|
||||
return ReadNone
|
||||
|
||||
while true:
|
||||
@@ -1390,7 +1400,7 @@ proc recv*(socket: TSocket): TaintedString {.tags: [ReadIOEffect], deprecated.}
|
||||
var pos = 0
|
||||
while true:
|
||||
var bytesRead = recv(socket, addr(string(result)[pos]), bufSize-1)
|
||||
if bytesRead == -1: osError(osLastError())
|
||||
if bytesRead == -1: raiseOSError(osLastError())
|
||||
setLen(result.string, pos + bytesRead)
|
||||
if bytesRead != bufSize-1: break
|
||||
# increase capacity:
|
||||
@@ -1463,11 +1473,11 @@ proc recvAsync*(socket: TSocket, s: var TaintedString): bool {.
|
||||
when defined(windows):
|
||||
if err.int32 == WSAEWOULDBLOCK:
|
||||
return false
|
||||
else: osError(err)
|
||||
else: raiseOSError(err)
|
||||
else:
|
||||
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
|
||||
return false
|
||||
else: osError(err)
|
||||
else: raiseOSError(err)
|
||||
|
||||
setLen(s.string, pos + bytesRead)
|
||||
if bytesRead != bufSize-1: break
|
||||
@@ -1516,11 +1526,11 @@ proc recvFromAsync*(socket: TSocket, data: var string, length: int,
|
||||
when defined(windows):
|
||||
if err.int32 == WSAEWOULDBLOCK:
|
||||
return false
|
||||
else: osError(err)
|
||||
else: raiseOSError(err)
|
||||
else:
|
||||
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
|
||||
return false
|
||||
else: osError(err)
|
||||
else: raiseOSError(err)
|
||||
|
||||
proc skip*(socket: TSocket) {.tags: [ReadIOEffect], deprecated.} =
|
||||
## skips all the data that is pending for the socket
|
||||
@@ -1570,7 +1580,7 @@ proc send*(socket: TSocket, data: string) {.tags: [WriteIOEffect].} =
|
||||
if socket.isSSL:
|
||||
SSLError()
|
||||
|
||||
osError(osLastError())
|
||||
raiseOSError(osLastError())
|
||||
|
||||
if sent != data.len:
|
||||
raise newException(OSError, "Could not send all data.")
|
||||
@@ -1607,11 +1617,11 @@ proc sendAsync*(socket: TSocket, data: string): int {.tags: [WriteIOEffect].} =
|
||||
when defined(windows):
|
||||
if err.int32 == WSAEINPROGRESS:
|
||||
return 0
|
||||
else: osError(err)
|
||||
else: raiseOSError(err)
|
||||
else:
|
||||
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
|
||||
return 0
|
||||
else: osError(err)
|
||||
else: raiseOSError(err)
|
||||
|
||||
|
||||
proc trySend*(socket: TSocket, data: string): bool {.tags: [WriteIOEffect].} =
|
||||
@@ -1667,15 +1677,15 @@ proc setBlocking(s: TSocket, blocking: bool) =
|
||||
when defined(Windows):
|
||||
var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
|
||||
if ioctlsocket(s.fd, FIONBIO, addr(mode)) == -1:
|
||||
osError(osLastError())
|
||||
raiseOSError(osLastError())
|
||||
else: # BSD sockets
|
||||
var x: int = fcntl(s.fd, F_GETFL, 0)
|
||||
if x == -1:
|
||||
osError(osLastError())
|
||||
raiseOSError(osLastError())
|
||||
else:
|
||||
var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
|
||||
if fcntl(s.fd, F_SETFL, mode) == -1:
|
||||
osError(osLastError())
|
||||
raiseOSError(osLastError())
|
||||
s.nonblocking = not blocking
|
||||
|
||||
discard """ proc setReuseAddr*(s: TSocket) =
|
||||
@@ -1715,6 +1725,6 @@ proc isBlocking*(socket: TSocket): bool = not socket.nonblocking
|
||||
|
||||
when defined(Windows):
|
||||
var wsa: TWSAData
|
||||
if wsaStartup(0x0101'i16, addr wsa) != 0: osError(osLastError())
|
||||
if wsaStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError())
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -8,8 +8,8 @@
|
||||
#
|
||||
|
||||
## This module provides a stream interface and two implementations thereof:
|
||||
## the `PFileStream` and the `PStringStream` which implement the stream
|
||||
## interface for Nimrod file objects (`TFile`) and strings. Other modules
|
||||
## the `FileStream` and the `StringStream` which implement the stream
|
||||
## interface for Nimrod file objects (`File`) and strings. Other modules
|
||||
## may provide other implementations for this standard stream interface.
|
||||
|
||||
include "system/inclrtl"
|
||||
@@ -19,81 +19,83 @@ proc newEIO(msg: string): ref IOError =
|
||||
result.msg = msg
|
||||
|
||||
type
|
||||
PStream* = ref TStream
|
||||
TStream* = object of RootObj ## Stream interface that supports
|
||||
## writing or reading. Note that these fields
|
||||
## here shouldn't be used directly. They are
|
||||
## accessible so that a stream implementation
|
||||
## can override them.
|
||||
closeImpl*: proc (s: PStream) {.nimcall, tags: [], gcsafe.}
|
||||
atEndImpl*: proc (s: PStream): bool {.nimcall, tags: [], gcsafe.}
|
||||
setPositionImpl*: proc (s: PStream, pos: int) {.nimcall, tags: [], gcsafe.}
|
||||
getPositionImpl*: proc (s: PStream): int {.nimcall, tags: [], gcsafe.}
|
||||
readDataImpl*: proc (s: PStream, buffer: pointer,
|
||||
Stream* = ref StreamObj
|
||||
StreamObj* = object of RootObj ## Stream interface that supports
|
||||
## writing or reading. Note that these fields
|
||||
## here shouldn't be used directly. They are
|
||||
## accessible so that a stream implementation
|
||||
## can override them.
|
||||
closeImpl*: proc (s: Stream) {.nimcall, tags: [], gcsafe.}
|
||||
atEndImpl*: proc (s: Stream): bool {.nimcall, tags: [], gcsafe.}
|
||||
setPositionImpl*: proc (s: Stream, pos: int) {.nimcall, tags: [], gcsafe.}
|
||||
getPositionImpl*: proc (s: Stream): int {.nimcall, tags: [], gcsafe.}
|
||||
readDataImpl*: proc (s: Stream, buffer: pointer,
|
||||
bufLen: int): int {.nimcall, tags: [ReadIOEffect], gcsafe.}
|
||||
writeDataImpl*: proc (s: PStream, buffer: pointer, bufLen: int) {.nimcall,
|
||||
writeDataImpl*: proc (s: Stream, buffer: pointer, bufLen: int) {.nimcall,
|
||||
tags: [WriteIOEffect], gcsafe.}
|
||||
flushImpl*: proc (s: PStream) {.nimcall, tags: [WriteIOEffect], gcsafe.}
|
||||
flushImpl*: proc (s: Stream) {.nimcall, tags: [WriteIOEffect], gcsafe.}
|
||||
|
||||
proc flush*(s: PStream) =
|
||||
{.deprecated: [PStream: Stream, TStream: StreamObj].}
|
||||
|
||||
proc flush*(s: Stream) =
|
||||
## flushes the buffers that the stream `s` might use.
|
||||
if not isNil(s.flushImpl): s.flushImpl(s)
|
||||
|
||||
proc close*(s: PStream) =
|
||||
proc close*(s: Stream) =
|
||||
## closes the stream `s`.
|
||||
if not isNil(s.closeImpl): s.closeImpl(s)
|
||||
|
||||
proc close*(s, unused: PStream) {.deprecated.} =
|
||||
proc close*(s, unused: Stream) {.deprecated.} =
|
||||
## closes the stream `s`.
|
||||
s.closeImpl(s)
|
||||
|
||||
proc atEnd*(s: PStream): bool =
|
||||
proc atEnd*(s: Stream): bool =
|
||||
## checks if more data can be read from `f`. Returns true if all data has
|
||||
## been read.
|
||||
result = s.atEndImpl(s)
|
||||
|
||||
proc atEnd*(s, unused: PStream): bool {.deprecated.} =
|
||||
proc atEnd*(s, unused: Stream): bool {.deprecated.} =
|
||||
## checks if more data can be read from `f`. Returns true if all data has
|
||||
## been read.
|
||||
result = s.atEndImpl(s)
|
||||
|
||||
proc setPosition*(s: PStream, pos: int) =
|
||||
proc setPosition*(s: Stream, pos: int) =
|
||||
## sets the position `pos` of the stream `s`.
|
||||
s.setPositionImpl(s, pos)
|
||||
|
||||
proc setPosition*(s, unused: PStream, pos: int) {.deprecated.} =
|
||||
proc setPosition*(s, unused: Stream, pos: int) {.deprecated.} =
|
||||
## sets the position `pos` of the stream `s`.
|
||||
s.setPositionImpl(s, pos)
|
||||
|
||||
proc getPosition*(s: PStream): int =
|
||||
proc getPosition*(s: Stream): int =
|
||||
## retrieves the current position in the stream `s`.
|
||||
result = s.getPositionImpl(s)
|
||||
|
||||
proc getPosition*(s, unused: PStream): int {.deprecated.} =
|
||||
proc getPosition*(s, unused: Stream): int {.deprecated.} =
|
||||
## retrieves the current position in the stream `s`.
|
||||
result = s.getPositionImpl(s)
|
||||
|
||||
proc readData*(s: PStream, buffer: pointer, bufLen: int): int =
|
||||
proc readData*(s: Stream, buffer: pointer, bufLen: int): int =
|
||||
## low level proc that reads data into an untyped `buffer` of `bufLen` size.
|
||||
result = s.readDataImpl(s, buffer, bufLen)
|
||||
|
||||
proc readData*(s, unused: PStream, buffer: pointer,
|
||||
proc readData*(s, unused: Stream, buffer: pointer,
|
||||
bufLen: int): int {.deprecated.} =
|
||||
## low level proc that reads data into an untyped `buffer` of `bufLen` size.
|
||||
result = s.readDataImpl(s, buffer, bufLen)
|
||||
|
||||
proc writeData*(s: PStream, buffer: pointer, bufLen: int) =
|
||||
proc writeData*(s: Stream, buffer: pointer, bufLen: int) =
|
||||
## low level proc that writes an untyped `buffer` of `bufLen` size
|
||||
## to the stream `s`.
|
||||
s.writeDataImpl(s, buffer, bufLen)
|
||||
|
||||
proc writeData*(s, unused: PStream, buffer: pointer,
|
||||
proc writeData*(s, unused: Stream, buffer: pointer,
|
||||
bufLen: int) {.deprecated.} =
|
||||
## low level proc that writes an untyped `buffer` of `bufLen` size
|
||||
## to the stream `s`.
|
||||
s.writeDataImpl(s, buffer, bufLen)
|
||||
|
||||
proc write*[T](s: PStream, x: T) =
|
||||
proc write*[T](s: Stream, x: T) =
|
||||
## generic write procedure. Writes `x` to the stream `s`. Implementation:
|
||||
##
|
||||
## .. code-block:: Nimrod
|
||||
@@ -103,63 +105,63 @@ proc write*[T](s: PStream, x: T) =
|
||||
shallowCopy(y, x)
|
||||
writeData(s, addr(y), sizeof(y))
|
||||
|
||||
proc write*(s: PStream, x: string) =
|
||||
proc write*(s: Stream, x: string) =
|
||||
## writes the string `x` to the the stream `s`. No length field or
|
||||
## terminating zero is written.
|
||||
writeData(s, cstring(x), x.len)
|
||||
|
||||
proc writeln*(s: PStream, args: varargs[string, `$`]) =
|
||||
proc writeln*(s: Stream, args: varargs[string, `$`]) =
|
||||
## writes one or more strings to the the stream `s` followed
|
||||
## by a new line. No length field or terminating zero is written.
|
||||
for str in args: write(s, str)
|
||||
write(s, "\n")
|
||||
|
||||
proc read[T](s: PStream, result: var T) =
|
||||
proc read[T](s: Stream, result: var T) =
|
||||
## generic read procedure. Reads `result` from the stream `s`.
|
||||
if readData(s, addr(result), sizeof(T)) != sizeof(T):
|
||||
raise newEIO("cannot read from stream")
|
||||
|
||||
proc readChar*(s: PStream): char =
|
||||
proc readChar*(s: Stream): char =
|
||||
## reads a char from the stream `s`. Raises `EIO` if an error occured.
|
||||
## Returns '\0' as an EOF marker.
|
||||
if readData(s, addr(result), sizeof(result)) != 1: result = '\0'
|
||||
|
||||
proc readBool*(s: PStream): bool =
|
||||
proc readBool*(s: Stream): bool =
|
||||
## reads a bool from the stream `s`. Raises `EIO` if an error occured.
|
||||
read(s, result)
|
||||
|
||||
proc readInt8*(s: PStream): int8 =
|
||||
proc readInt8*(s: Stream): int8 =
|
||||
## reads an int8 from the stream `s`. Raises `EIO` if an error occured.
|
||||
read(s, result)
|
||||
|
||||
proc readInt16*(s: PStream): int16 =
|
||||
proc readInt16*(s: Stream): int16 =
|
||||
## reads an int16 from the stream `s`. Raises `EIO` if an error occured.
|
||||
read(s, result)
|
||||
|
||||
proc readInt32*(s: PStream): int32 =
|
||||
proc readInt32*(s: Stream): int32 =
|
||||
## reads an int32 from the stream `s`. Raises `EIO` if an error occured.
|
||||
read(s, result)
|
||||
|
||||
proc readInt64*(s: PStream): int64 =
|
||||
proc readInt64*(s: Stream): int64 =
|
||||
## reads an int64 from the stream `s`. Raises `EIO` if an error occured.
|
||||
read(s, result)
|
||||
|
||||
proc readFloat32*(s: PStream): float32 =
|
||||
proc readFloat32*(s: Stream): float32 =
|
||||
## reads a float32 from the stream `s`. Raises `EIO` if an error occured.
|
||||
read(s, result)
|
||||
|
||||
proc readFloat64*(s: PStream): float64 =
|
||||
proc readFloat64*(s: Stream): float64 =
|
||||
## reads a float64 from the stream `s`. Raises `EIO` if an error occured.
|
||||
read(s, result)
|
||||
|
||||
proc readStr*(s: PStream, length: int): TaintedString =
|
||||
proc readStr*(s: Stream, length: int): TaintedString =
|
||||
## reads a string of length `length` from the stream `s`. Raises `EIO` if
|
||||
## an error occured.
|
||||
result = newString(length).TaintedString
|
||||
var L = readData(s, addr(string(result)[0]), length)
|
||||
if L != length: setLen(result.string, L)
|
||||
|
||||
proc readLine*(s: PStream, line: var TaintedString): bool =
|
||||
proc readLine*(s: Stream, line: var TaintedString): bool =
|
||||
## reads a line of text from the stream `s` into `line`. `line` must not be
|
||||
## ``nil``! May throw an IO exception.
|
||||
## A line of text may be delimited by ``CR``, ``LF`` or
|
||||
@@ -179,7 +181,7 @@ proc readLine*(s: PStream, line: var TaintedString): bool =
|
||||
line.string.add(c)
|
||||
result = true
|
||||
|
||||
proc readLine*(s: PStream): TaintedString =
|
||||
proc readLine*(s: Stream): TaintedString =
|
||||
## Reads a line from a stream `s`. Note: This is not very efficient. Raises
|
||||
## `EIO` if an error occured.
|
||||
result = TaintedString""
|
||||
@@ -194,42 +196,44 @@ proc readLine*(s: PStream): TaintedString =
|
||||
result.string.add(c)
|
||||
|
||||
type
|
||||
PStringStream* = ref TStringStream ## a stream that encapsulates a string
|
||||
TStringStream* = object of TStream
|
||||
StringStream* = ref StringStreamObj ## a stream that encapsulates a string
|
||||
StringStreamObj* = object of StreamObj
|
||||
data*: string
|
||||
pos: int
|
||||
|
||||
proc ssAtEnd(s: PStream): bool =
|
||||
var s = PStringStream(s)
|
||||
|
||||
{.deprecated: [PStringStream: StringStream, TStringStream: StringStreamObj].}
|
||||
|
||||
proc ssAtEnd(s: Stream): bool =
|
||||
var s = StringStream(s)
|
||||
return s.pos >= s.data.len
|
||||
|
||||
proc ssSetPosition(s: PStream, pos: int) =
|
||||
var s = PStringStream(s)
|
||||
proc ssSetPosition(s: Stream, pos: int) =
|
||||
var s = StringStream(s)
|
||||
s.pos = clamp(pos, 0, s.data.high)
|
||||
|
||||
proc ssGetPosition(s: PStream): int =
|
||||
var s = PStringStream(s)
|
||||
proc ssGetPosition(s: Stream): int =
|
||||
var s = StringStream(s)
|
||||
return s.pos
|
||||
|
||||
proc ssReadData(s: PStream, buffer: pointer, bufLen: int): int =
|
||||
var s = PStringStream(s)
|
||||
proc ssReadData(s: Stream, buffer: pointer, bufLen: int): int =
|
||||
var s = StringStream(s)
|
||||
result = min(bufLen, s.data.len - s.pos)
|
||||
if result > 0:
|
||||
copyMem(buffer, addr(s.data[s.pos]), result)
|
||||
inc(s.pos, result)
|
||||
|
||||
proc ssWriteData(s: PStream, buffer: pointer, bufLen: int) =
|
||||
var s = PStringStream(s)
|
||||
proc ssWriteData(s: Stream, buffer: pointer, bufLen: int) =
|
||||
var s = StringStream(s)
|
||||
if bufLen > 0:
|
||||
setLen(s.data, s.data.len + bufLen)
|
||||
copyMem(addr(s.data[s.pos]), buffer, bufLen)
|
||||
inc(s.pos, bufLen)
|
||||
|
||||
proc ssClose(s: PStream) =
|
||||
var s = PStringStream(s)
|
||||
proc ssClose(s: Stream) =
|
||||
var s = StringStream(s)
|
||||
s.data = nil
|
||||
|
||||
proc newStringStream*(s: string = ""): PStringStream =
|
||||
proc newStringStream*(s: string = ""): StringStream =
|
||||
## creates a new stream from the string `s`.
|
||||
new(result)
|
||||
result.data = s
|
||||
@@ -244,27 +248,28 @@ proc newStringStream*(s: string = ""): PStringStream =
|
||||
when not defined(js):
|
||||
|
||||
type
|
||||
PFileStream* = ref TFileStream ## a stream that encapsulates a `TFile`
|
||||
TFileStream* = object of TStream
|
||||
FileStream* = ref FileStreamObj ## a stream that encapsulates a `TFile`
|
||||
FileStreamObj* = object of Stream
|
||||
f: File
|
||||
{.deprecated: [PFileStream: FileStream, TFileStream: FileStreamObj].}
|
||||
|
||||
proc fsClose(s: PStream) =
|
||||
if PFileStream(s).f != nil:
|
||||
close(PFileStream(s).f)
|
||||
PFileStream(s).f = nil
|
||||
proc fsFlush(s: PStream) = flushFile(PFileStream(s).f)
|
||||
proc fsAtEnd(s: PStream): bool = return endOfFile(PFileStream(s).f)
|
||||
proc fsSetPosition(s: PStream, pos: int) = setFilePos(PFileStream(s).f, pos)
|
||||
proc fsGetPosition(s: PStream): int = return int(getFilePos(PFileStream(s).f))
|
||||
proc fsClose(s: Stream) =
|
||||
if FileStream(s).f != nil:
|
||||
close(FileStream(s).f)
|
||||
FileStream(s).f = nil
|
||||
proc fsFlush(s: Stream) = flushFile(FileStream(s).f)
|
||||
proc fsAtEnd(s: Stream): bool = return endOfFile(FileStream(s).f)
|
||||
proc fsSetPosition(s: Stream, pos: int) = setFilePos(FileStream(s).f, pos)
|
||||
proc fsGetPosition(s: Stream): int = return int(getFilePos(FileStream(s).f))
|
||||
|
||||
proc fsReadData(s: PStream, buffer: pointer, bufLen: int): int =
|
||||
result = readBuffer(PFileStream(s).f, buffer, bufLen)
|
||||
proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int =
|
||||
result = readBuffer(FileStream(s).f, buffer, bufLen)
|
||||
|
||||
proc fsWriteData(s: PStream, buffer: pointer, bufLen: int) =
|
||||
if writeBuffer(PFileStream(s).f, buffer, bufLen) != bufLen:
|
||||
proc fsWriteData(s: Stream, buffer: pointer, bufLen: int) =
|
||||
if writeBuffer(FileStream(s).f, buffer, bufLen) != bufLen:
|
||||
raise newEIO("cannot write to stream")
|
||||
|
||||
proc newFileStream*(f: File): PFileStream =
|
||||
proc newFileStream*(f: File): FileStream =
|
||||
## creates a new stream from the file `f`.
|
||||
new(result)
|
||||
result.f = f
|
||||
@@ -276,7 +281,7 @@ when not defined(js):
|
||||
result.writeDataImpl = fsWriteData
|
||||
result.flushImpl = fsFlush
|
||||
|
||||
proc newFileStream*(filename: string, mode: FileMode): PFileStream =
|
||||
proc newFileStream*(filename: string, mode: FileMode): FileStream =
|
||||
## creates a new stream from the file named `filename` with the mode `mode`.
|
||||
## If the file cannot be opened, nil is returned. See the `system
|
||||
## <system.html>`_ module for a list of available TFileMode enums.
|
||||
@@ -288,45 +293,47 @@ when true:
|
||||
discard
|
||||
else:
|
||||
type
|
||||
TFileHandle* = cint ## Operating system file handle
|
||||
PFileHandleStream* = ref TFileHandleStream
|
||||
TFileHandleStream* = object of TStream
|
||||
handle*: TFileHandle
|
||||
FileHandleStream* = ref FileHandleStreamObj
|
||||
FileHandleStreamObj* = object of Stream
|
||||
handle*: FileHandle
|
||||
pos: int
|
||||
|
||||
proc newEOS(msg: string): ref EOS =
|
||||
{.deprecated: [PFileHandleStream: FileHandleStream,
|
||||
TFileHandleStream: FileHandleStreamObj].}
|
||||
|
||||
proc newEOS(msg: string): ref OSError =
|
||||
new(result)
|
||||
result.msg = msg
|
||||
|
||||
proc hsGetPosition(s: PFileHandleStream): int =
|
||||
proc hsGetPosition(s: FileHandleStream): int =
|
||||
return s.pos
|
||||
|
||||
when defined(windows):
|
||||
# do not import windows as this increases compile times:
|
||||
nil
|
||||
discard
|
||||
else:
|
||||
import posix
|
||||
|
||||
proc hsSetPosition(s: PFileHandleStream, pos: int) =
|
||||
proc hsSetPosition(s: FileHandleStream, pos: int) =
|
||||
discard lseek(s.handle, pos, SEEK_SET)
|
||||
|
||||
proc hsClose(s: PFileHandleStream) = discard close(s.handle)
|
||||
proc hsAtEnd(s: PFileHandleStream): bool =
|
||||
proc hsClose(s: FileHandleStream) = discard close(s.handle)
|
||||
proc hsAtEnd(s: FileHandleStream): bool =
|
||||
var pos = hsGetPosition(s)
|
||||
var theEnd = lseek(s.handle, 0, SEEK_END)
|
||||
result = pos >= theEnd
|
||||
hsSetPosition(s, pos) # set position back
|
||||
|
||||
proc hsReadData(s: PFileHandleStream, buffer: pointer, bufLen: int): int =
|
||||
proc hsReadData(s: FileHandleStream, buffer: pointer, bufLen: int): int =
|
||||
result = posix.read(s.handle, buffer, bufLen)
|
||||
inc(s.pos, result)
|
||||
|
||||
proc hsWriteData(s: PFileHandleStream, buffer: pointer, bufLen: int) =
|
||||
proc hsWriteData(s: FileHandleStream, buffer: pointer, bufLen: int) =
|
||||
if posix.write(s.handle, buffer, bufLen) != bufLen:
|
||||
raise newEIO("cannot write to stream")
|
||||
inc(s.pos, bufLen)
|
||||
|
||||
proc newFileHandleStream*(handle: TFileHandle): PFileHandleStream =
|
||||
proc newFileHandleStream*(handle: FileHandle): FileHandleStream =
|
||||
new(result)
|
||||
result.handle = handle
|
||||
result.pos = 0
|
||||
@@ -338,9 +345,9 @@ else:
|
||||
result.writeData = hsWriteData
|
||||
|
||||
proc newFileHandleStream*(filename: string,
|
||||
mode: TFileMode): PFileHandleStream =
|
||||
when defined(windows):
|
||||
nil
|
||||
mode: FileMode): FileHandleStream =
|
||||
when defined(windows):
|
||||
discard
|
||||
else:
|
||||
var flags: cint
|
||||
case mode
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -18,20 +18,23 @@ import
|
||||
include "system/inclrtl"
|
||||
|
||||
type
|
||||
TStringTableMode* = enum ## describes the tables operation mode
|
||||
StringTableMode* = enum ## describes the tables operation mode
|
||||
modeCaseSensitive, ## the table is case sensitive
|
||||
modeCaseInsensitive, ## the table is case insensitive
|
||||
modeStyleInsensitive ## the table is style insensitive
|
||||
TKeyValuePair = tuple[key, val: string]
|
||||
TKeyValuePairSeq = seq[TKeyValuePair]
|
||||
TStringTable* = object of RootObj
|
||||
KeyValuePair = tuple[key, val: string]
|
||||
KeyValuePairSeq = seq[KeyValuePair]
|
||||
StringTableObj* = object of RootObj
|
||||
counter: int
|
||||
data: TKeyValuePairSeq
|
||||
mode: TStringTableMode
|
||||
data: KeyValuePairSeq
|
||||
mode: StringTableMode
|
||||
|
||||
PStringTable* = ref TStringTable ## use this type to declare string tables
|
||||
StringTableRef* = ref StringTableObj ## use this type to declare string tables
|
||||
|
||||
proc len*(t: PStringTable): int {.rtl, extern: "nst$1".} =
|
||||
{.deprecated: [TStringTableMode: StringTableMode,
|
||||
TStringTable: StringTableObj, PStringTable: StringTableRef].}
|
||||
|
||||
proc len*(t: StringTableRef): int {.rtl, extern: "nst$1".} =
|
||||
## returns the number of keys in `t`.
|
||||
result = t.counter
|
||||
|
||||
@@ -54,7 +57,7 @@ iterator values*(t: PStringTable): string =
|
||||
yield t.data[h].val
|
||||
|
||||
type
|
||||
TFormatFlag* = enum ## flags for the `%` operator
|
||||
FormatFlag* = enum ## flags for the `%` operator
|
||||
useEnvironment, ## use environment variable if the ``$key``
|
||||
## is not found in the table
|
||||
useEmpty, ## use the empty string as a default, thus it
|
||||
@@ -63,6 +66,8 @@ type
|
||||
useKey ## do not replace ``$key`` if it is not found
|
||||
## in the table (or in the environment)
|
||||
|
||||
{.deprecated: [TFormatFlag: FormatFlag].}
|
||||
|
||||
# implementation
|
||||
|
||||
const
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -23,7 +23,7 @@ import parseutils
|
||||
include "system/inclrtl"
|
||||
|
||||
type
|
||||
TCharSet* = set[char] # for compatibility with Nim
|
||||
TCharSet* {.deprecated.} = set[char] # for compatibility with Nim
|
||||
|
||||
const
|
||||
Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'}
|
||||
@@ -54,7 +54,7 @@ const
|
||||
## make the `find() proc <#find,string,set[char],int>`_ find **invalid**
|
||||
## characters in strings. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## let invalid = AllChars - Digits
|
||||
## doAssert "01234".find(invalid) == -1
|
||||
## doAssert "01A34".find(invalid) == 2
|
||||
@@ -205,7 +205,7 @@ iterator split*(s: string, seps: set[char] = Whitespace): string =
|
||||
## a single split point and leading/trailing separators will be ignored.
|
||||
## The following example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## for word in split(" this is an example "):
|
||||
## writeln(stdout, word)
|
||||
##
|
||||
@@ -219,13 +219,13 @@ iterator split*(s: string, seps: set[char] = Whitespace): string =
|
||||
##
|
||||
## And the following code:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## for word in split(";;this;is;an;;example;;;", {';'}):
|
||||
## writeln(stdout, word)
|
||||
##
|
||||
## ...produces the same output as the first example. The code:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## let date = "2012-11-20T22:08:08.398990"
|
||||
## let separators = {' ', '-', ':', 'T'}
|
||||
## for number in split(date, separators):
|
||||
@@ -258,7 +258,7 @@ iterator split*(s: string, sep: char): string =
|
||||
## characters, this proc will not coalesce groups of the
|
||||
## separator, returning a string for each found character. The code:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## for word in split(";;this;is;an;;example;;;", ';'):
|
||||
## writeln(stdout, word)
|
||||
##
|
||||
@@ -308,13 +308,13 @@ iterator splitLines*(s: string): string =
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## for line in splitLines("\nthis\nis\nan\n\nexample\n"):
|
||||
## writeln(stdout, line)
|
||||
##
|
||||
## Results in:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## ""
|
||||
## "this"
|
||||
## "is"
|
||||
@@ -417,7 +417,7 @@ proc parseInt*(s: string): int {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuParseInt".} =
|
||||
## Parses a decimal integer value contained in `s`.
|
||||
##
|
||||
## If `s` is not a valid integer, `EInvalidValue` is raised.
|
||||
## If `s` is not a valid integer, `ValueError` is raised.
|
||||
var L = parseutils.parseInt(s, result, 0)
|
||||
if L != s.len or L == 0:
|
||||
raise newException(ValueError, "invalid integer: " & s)
|
||||
@@ -426,7 +426,7 @@ proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuParseBiggestInt".} =
|
||||
## Parses a decimal integer value contained in `s`.
|
||||
##
|
||||
## If `s` is not a valid integer, `EInvalidValue` is raised.
|
||||
## If `s` is not a valid integer, `ValueError` is raised.
|
||||
var L = parseutils.parseBiggestInt(s, result, 0)
|
||||
if L != s.len or L == 0:
|
||||
raise newException(ValueError, "invalid integer: " & s)
|
||||
@@ -434,7 +434,7 @@ proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect, procvar,
|
||||
proc parseFloat*(s: string): float {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuParseFloat".} =
|
||||
## Parses a decimal floating point value contained in `s`. If `s` is not
|
||||
## a valid floating point number, `EInvalidValue` is raised. ``NAN``,
|
||||
## a valid floating point number, `ValueError` is raised. ``NAN``,
|
||||
## ``INF``, ``-INF`` are also supported (case insensitive comparison).
|
||||
var L = parseutils.parseFloat(s, result, 0)
|
||||
if L != s.len or L == 0:
|
||||
@@ -444,7 +444,7 @@ proc parseHexInt*(s: string): int {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuParseHexInt".} =
|
||||
## Parses a hexadecimal integer value contained in `s`.
|
||||
##
|
||||
## If `s` is not a valid integer, `EInvalidValue` is raised. `s` can have one
|
||||
## If `s` is not a valid integer, `ValueError` is raised. `s` can have one
|
||||
## of the following optional prefixes: ``0x``, ``0X``, ``#``. Underscores
|
||||
## within `s` are ignored.
|
||||
var i = 0
|
||||
@@ -471,7 +471,7 @@ proc parseBool*(s: string): bool =
|
||||
## If ``s`` is one of the following values: ``y, yes, true, 1, on``, then
|
||||
## returns `true`. If ``s`` is one of the following values: ``n, no, false,
|
||||
## 0, off``, then returns `false`. If ``s`` is something else a
|
||||
## ``EInvalidValue`` exception is raised.
|
||||
## ``ValueError`` exception is raised.
|
||||
case normalize(s)
|
||||
of "y", "yes", "true", "1", "on": result = true
|
||||
of "n", "no", "false", "0", "off": result = false
|
||||
@@ -480,7 +480,7 @@ proc parseBool*(s: string): bool =
|
||||
proc parseEnum*[T: enum](s: string): T =
|
||||
## Parses an enum ``T``.
|
||||
##
|
||||
## Raises ``EInvalidValue`` for an invalid value in `s`. The comparison is
|
||||
## Raises ``ValueError`` for an invalid value in `s`. The comparison is
|
||||
## done in a style insensitive way.
|
||||
for e in low(T)..high(T):
|
||||
if cmpIgnoreStyle(s, $e) == 0:
|
||||
@@ -502,7 +502,7 @@ proc repeatChar*(count: int, c: char = ' '): string {.noSideEffect,
|
||||
## Returns a string of length `count` consisting only of
|
||||
## the character `c`. You can use this proc to left align strings. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## let
|
||||
## width = 15
|
||||
## text1 = "Hello user!"
|
||||
@@ -527,7 +527,7 @@ proc align*(s: string, count: int, padding = ' '): string {.
|
||||
## returned unchanged. If you need to left align a string use the `repeatChar
|
||||
## proc <#repeatChar>`_. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## assert align("abc", 4) == " abc"
|
||||
## assert align("a", 0) == "a"
|
||||
## assert align("1232", 6) == " 1232"
|
||||
@@ -547,13 +547,13 @@ iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[
|
||||
## Substrings are separated by a substring containing only `seps`.
|
||||
## Examples:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## for word in tokenize(" this is an example "):
|
||||
## writeln(stdout, word)
|
||||
##
|
||||
## Results in:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## (" ", true)
|
||||
## ("this", false)
|
||||
## (" ", true)
|
||||
@@ -676,7 +676,7 @@ proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect,
|
||||
##
|
||||
## A shorthand for:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## if dest.len > startLen: add(dest, sep)
|
||||
##
|
||||
## This is often useful for generating some code where the items need to
|
||||
@@ -684,7 +684,7 @@ proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect,
|
||||
## `startLen`. The following example creates a string describing
|
||||
## an array of integers:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## var arr = "["
|
||||
## for x in items([2, 3, 5, 7, 11]):
|
||||
## addSep(arr, startLen=len("["))
|
||||
@@ -692,7 +692,7 @@ proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect,
|
||||
## add(arr, "]")
|
||||
if dest.len > startLen: add(dest, sep)
|
||||
|
||||
proc allCharsInSet*(s: string, theSet: TCharSet): bool =
|
||||
proc allCharsInSet*(s: string, theSet: set[char]): bool =
|
||||
## Returns true iff each character of `s` is in the set `theSet`.
|
||||
for c in items(s):
|
||||
if c notin theSet: return false
|
||||
@@ -739,14 +739,14 @@ proc join*(a: openArray[string]): string {.
|
||||
result = ""
|
||||
|
||||
type
|
||||
TSkipTable = array[char, int]
|
||||
SkipTable = array[char, int]
|
||||
|
||||
proc preprocessSub(sub: string, a: var TSkipTable) =
|
||||
proc preprocessSub(sub: string, a: var SkipTable) =
|
||||
var m = len(sub)
|
||||
for i in 0..0xff: a[chr(i)] = m+1
|
||||
for i in 0..m-1: a[sub[i]] = m-i
|
||||
|
||||
proc findAux(s, sub: string, start: int, a: TSkipTable): int =
|
||||
proc findAux(s, sub: string, start: int, a: SkipTable): int =
|
||||
# Fast "quick search" algorithm:
|
||||
var
|
||||
m = len(sub)
|
||||
@@ -762,7 +762,7 @@ proc findAux(s, sub: string, start: int, a: TSkipTable): int =
|
||||
return -1
|
||||
|
||||
proc find*(s, sub: string, start: int = 0): int {.noSideEffect,
|
||||
rtl, extern: "nsuFindStr", operator: 6.} =
|
||||
rtl, extern: "nsuFindStr".} =
|
||||
## Searches for `sub` in `s` starting at position `start`.
|
||||
##
|
||||
## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
|
||||
@@ -827,9 +827,9 @@ proc contains*(s: string, chars: set[char]): bool {.noSideEffect.} =
|
||||
return find(s, chars) >= 0
|
||||
|
||||
proc replace*(s, sub: string, by = ""): string {.noSideEffect,
|
||||
rtl, extern: "nsuReplaceStr", operator: 1.} =
|
||||
rtl, extern: "nsuReplaceStr".} =
|
||||
## Replaces `sub` in `s` by the string `by`.
|
||||
var a {.noinit.}: TSkipTable
|
||||
var a {.noinit.}: SkipTable
|
||||
result = ""
|
||||
preprocessSub(sub, a)
|
||||
var i = 0
|
||||
@@ -862,7 +862,7 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
|
||||
## (comparable to ``\\w`` in regular expressions), otherwise it is not
|
||||
## replaced.
|
||||
const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
|
||||
var a {.noinit.}: TSkipTable
|
||||
var a {.noinit.}: SkipTable
|
||||
result = ""
|
||||
preprocessSub(sub, a)
|
||||
var i = 0
|
||||
@@ -899,7 +899,7 @@ proc parseOctInt*(s: string): int {.noSideEffect,
|
||||
rtl, extern: "nsuParseOctInt".} =
|
||||
## Parses an octal integer value contained in `s`.
|
||||
##
|
||||
## If `s` is not a valid integer, `EInvalidValue` is raised. `s` can have one
|
||||
## If `s` is not a valid integer, `ValueError` is raised. `s` can have one
|
||||
## of the following optional prefixes: ``0o``, ``0O``. Underscores within
|
||||
## `s` are ignored.
|
||||
var i = 0
|
||||
@@ -999,7 +999,7 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
|
||||
## operations.
|
||||
##
|
||||
## If `s` does not begin with ``prefix`` and end with ``suffix`` a
|
||||
## EInvalidValue exception will be raised.
|
||||
## ValueError exception will be raised.
|
||||
result = newStringOfCap(s.len)
|
||||
var i = 0
|
||||
if s[0 .. prefix.len-1] != prefix:
|
||||
@@ -1138,12 +1138,14 @@ proc c_sprintf(buf, frmt: cstring) {.header: "<stdio.h>", importc: "sprintf",
|
||||
varargs, noSideEffect.}
|
||||
|
||||
type
|
||||
TFloatFormat* = enum ## the different modes of floating point formating
|
||||
FloatFormatMode* = enum ## the different modes of floating point formating
|
||||
ffDefault, ## use the shorter floating point notation
|
||||
ffDecimal, ## use decimal floating point notation
|
||||
ffScientific ## use scientific notation (using ``e`` character)
|
||||
|
||||
proc formatBiggestFloat*(f: BiggestFloat, format: TFloatFormat = ffDefault,
|
||||
{.deprecated: [TFloatFormat: FloatFormatMode].}
|
||||
|
||||
proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
|
||||
precision: range[0..32] = 16): string {.
|
||||
noSideEffect, operator: 2, rtl, extern: "nsu$1".} =
|
||||
## Converts a floating point value `f` to a string.
|
||||
@@ -1174,7 +1176,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: TFloatFormat = ffDefault,
|
||||
c_sprintf(buf, frmtstr, f)
|
||||
result = $buf
|
||||
|
||||
proc formatFloat*(f: float, format: TFloatFormat = ffDefault,
|
||||
proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
|
||||
precision: range[0..32] = 16): string {.
|
||||
noSideEffect, operator: 2, rtl, extern: "nsu$1".} =
|
||||
## Converts a floating point value `f` to a string.
|
||||
@@ -1190,7 +1192,7 @@ proc formatFloat*(f: float, format: TFloatFormat = ffDefault,
|
||||
proc formatSize*(bytes: BiggestInt, decimalSep = '.'): string =
|
||||
## Rounds and formats `bytes`. Examples:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## formatSize(1'i64 shl 31 + 300'i64) == "2.204GB"
|
||||
## formatSize(4096) == "4KB"
|
||||
@@ -1280,12 +1282,12 @@ proc `%` *(formatstr: string, a: openArray[string]): string {.noSideEffect,
|
||||
##
|
||||
## This is best explained by an example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## "$1 eats $2." % ["The cat", "fish"]
|
||||
##
|
||||
## Results in:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## "The cat eats fish."
|
||||
##
|
||||
## The substitution variables (the thing after the ``$``) are enumerated
|
||||
@@ -1294,7 +1296,7 @@ proc `%` *(formatstr: string, a: openArray[string]): string {.noSideEffect,
|
||||
## The notation ``$#`` can be used to refer to the next substitution
|
||||
## variable:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## "$# eats $#." % ["The cat", "fish"]
|
||||
##
|
||||
## Substitution variables can also be words (that is
|
||||
@@ -1302,15 +1304,15 @@ proc `%` *(formatstr: string, a: openArray[string]): string {.noSideEffect,
|
||||
## indices are keys and with odd indices are the corresponding values.
|
||||
## An example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## "$animal eats $food." % ["animal", "The cat", "food", "fish"]
|
||||
##
|
||||
## Results in:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block:: nim
|
||||
## "The cat eats fish."
|
||||
##
|
||||
## The variables are compared with `cmpIgnoreStyle`. `EInvalidValue` is
|
||||
## The variables are compared with `cmpIgnoreStyle`. `ValueError` is
|
||||
## raised if an ill-formed format string has been passed to the `%` operator.
|
||||
result = newStringOfCap(formatstr.len + a.len shl 4)
|
||||
addf(result, formatstr, a)
|
||||
@@ -1350,7 +1352,7 @@ when isMainModule:
|
||||
doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz "
|
||||
doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc"
|
||||
|
||||
type TMyEnum = enum enA, enB, enC, enuD, enE
|
||||
doAssert parseEnum[TMyEnum]("enu_D") == enuD
|
||||
type MyEnum = enum enA, enB, enC, enuD, enE
|
||||
doAssert parseEnum[MyEnum]("enu_D") == enuD
|
||||
|
||||
doAssert parseEnum("invalid enum value", enC) == enC
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Nimrod support for `substitution expressions`:idx: (`subex`:idx:).
|
||||
## Nim support for `substitution expressions`:idx: (`subex`:idx:).
|
||||
##
|
||||
## .. include:: ../doc/subexes.txt
|
||||
##
|
||||
@@ -28,11 +28,13 @@ proc findNormalized(x: string, inArray: openarray[string]): int =
|
||||
return -1
|
||||
|
||||
type
|
||||
EInvalidSubex* = object of EInvalidValue ## exception that is raised for
|
||||
## an invalid subex
|
||||
SubexError* = object of ValueError ## exception that is raised for
|
||||
## an invalid subex
|
||||
|
||||
{.deprecated: [EInvalidSubex: SubexError].}
|
||||
|
||||
proc raiseInvalidFormat(msg: string) {.noinline.} =
|
||||
raise newException(EInvalidSubex, "invalid format string: " & msg)
|
||||
raise newException(SubexError, "invalid format string: " & msg)
|
||||
|
||||
type
|
||||
TFormatParser = object {.pure, final.}
|
||||
@@ -291,14 +293,16 @@ proc scanDollar(p: var TFormatParser, a: openarray[string], s: var string) =
|
||||
|
||||
|
||||
type
|
||||
TSubex* = distinct string ## string that contains a substitution expression
|
||||
Subex* = distinct string ## string that contains a substitution expression
|
||||
|
||||
proc subex*(s: string): TSubex =
|
||||
{.deprecated: [TSubex: Subex].}
|
||||
|
||||
proc subex*(s: string): Subex =
|
||||
## constructs a *substitution expression* from `s`. Currently this performs
|
||||
## no syntax checking but this may change in later versions.
|
||||
result = TSubex(s)
|
||||
result = Subex(s)
|
||||
|
||||
proc addf*(s: var string, formatstr: TSubex, a: varargs[string, `$`]) {.
|
||||
proc addf*(s: var string, formatstr: Subex, a: varargs[string, `$`]) {.
|
||||
noSideEffect, rtl, extern: "nfrmtAddf".} =
|
||||
## The same as ``add(s, formatstr % a)``, but more efficient.
|
||||
var p: TFormatParser
|
||||
@@ -312,7 +316,7 @@ proc addf*(s: var string, formatstr: TSubex, a: varargs[string, `$`]) {.
|
||||
emitChar(p, s, p.f[i])
|
||||
inc(i)
|
||||
|
||||
proc `%` *(formatstr: TSubex, a: openarray[string]): string {.noSideEffect,
|
||||
proc `%` *(formatstr: Subex, a: openarray[string]): string {.noSideEffect,
|
||||
rtl, extern: "nfrmtFormatOpenArray".} =
|
||||
## The `substitution`:idx: operator performs string substitutions in
|
||||
## `formatstr` and returns a modified `formatstr`. This is often called
|
||||
@@ -321,13 +325,13 @@ proc `%` *(formatstr: TSubex, a: openarray[string]): string {.noSideEffect,
|
||||
result = newStringOfCap(formatstr.string.len + a.len shl 4)
|
||||
addf(result, formatstr, a)
|
||||
|
||||
proc `%` *(formatstr: TSubex, a: string): string {.noSideEffect,
|
||||
proc `%` *(formatstr: Subex, a: string): string {.noSideEffect,
|
||||
rtl, extern: "nfrmtFormatSingleElem".} =
|
||||
## This is the same as ``formatstr % [a]``.
|
||||
result = newStringOfCap(formatstr.string.len + a.len)
|
||||
addf(result, formatstr, [a])
|
||||
|
||||
proc format*(formatstr: TSubex, a: varargs[string, `$`]): string {.noSideEffect,
|
||||
proc format*(formatstr: Subex, a: varargs[string, `$`]): string {.noSideEffect,
|
||||
rtl, extern: "nfrmtFormatVarargs".} =
|
||||
## The `substitution`:idx: operator performs string substitutions in
|
||||
## `formatstr` and returns a modified `formatstr`. This is often called
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -27,11 +27,11 @@ when defined(windows):
|
||||
var hTemp = GetStdHandle(STD_OUTPUT_HANDLE)
|
||||
if DuplicateHandle(GetCurrentProcess(), hTemp, GetCurrentProcess(),
|
||||
addr(conHandle), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
|
||||
osError(osLastError())
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc getCursorPos(): tuple [x,y: int] =
|
||||
var c: TCONSOLESCREENBUFFERINFO
|
||||
if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0: osError(osLastError())
|
||||
if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0: raiseOSError(osLastError())
|
||||
return (int(c.dwCursorPosition.x), int(c.dwCursorPosition.y))
|
||||
|
||||
proc getAttributes(): int16 =
|
||||
@@ -51,7 +51,7 @@ proc setCursorPos*(x, y: int) =
|
||||
var c: TCOORD
|
||||
c.x = int16(x)
|
||||
c.y = int16(y)
|
||||
if SetConsoleCursorPosition(conHandle, c) == 0: osError(osLastError())
|
||||
if SetConsoleCursorPosition(conHandle, c) == 0: raiseOSError(osLastError())
|
||||
else:
|
||||
stdout.write("\e[" & $y & ';' & $x & 'f')
|
||||
|
||||
@@ -61,10 +61,10 @@ proc setCursorXPos*(x: int) =
|
||||
when defined(windows):
|
||||
var scrbuf: TCONSOLESCREENBUFFERINFO
|
||||
var hStdout = conHandle
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: osError(osLastError())
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: raiseOSError(osLastError())
|
||||
var origin = scrbuf.dwCursorPosition
|
||||
origin.x = int16(x)
|
||||
if SetConsoleCursorPosition(conHandle, origin) == 0: osError(osLastError())
|
||||
if SetConsoleCursorPosition(conHandle, origin) == 0: raiseOSError(osLastError())
|
||||
else:
|
||||
stdout.write("\e[" & $x & 'G')
|
||||
|
||||
@@ -75,10 +75,10 @@ when defined(windows):
|
||||
when defined(windows):
|
||||
var scrbuf: TCONSOLESCREENBUFFERINFO
|
||||
var hStdout = conHandle
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: osError(osLastError())
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: raiseOSError(osLastError())
|
||||
var origin = scrbuf.dwCursorPosition
|
||||
origin.y = int16(y)
|
||||
if SetConsoleCursorPosition(conHandle, origin) == 0: osError(osLastError())
|
||||
if SetConsoleCursorPosition(conHandle, origin) == 0: raiseOSError(osLastError())
|
||||
else:
|
||||
discard
|
||||
|
||||
@@ -155,18 +155,18 @@ proc eraseLine* =
|
||||
var scrbuf: TCONSOLESCREENBUFFERINFO
|
||||
var numwrote: DWORD
|
||||
var hStdout = conHandle
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: osError(osLastError())
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: raiseOSError(osLastError())
|
||||
var origin = scrbuf.dwCursorPosition
|
||||
origin.x = 0'i16
|
||||
if SetConsoleCursorPosition(conHandle, origin) == 0: osError(osLastError())
|
||||
if SetConsoleCursorPosition(conHandle, origin) == 0: raiseOSError(osLastError())
|
||||
var ht = scrbuf.dwSize.Y - origin.Y
|
||||
var wt = scrbuf.dwSize.X - origin.X
|
||||
if FillConsoleOutputCharacter(hStdout,' ', ht*wt,
|
||||
origin, addr(numwrote)) == 0:
|
||||
osError(osLastError())
|
||||
raiseOSError(osLastError())
|
||||
if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, ht * wt,
|
||||
scrbuf.dwCursorPosition, addr(numwrote)) == 0:
|
||||
osError(osLastError())
|
||||
raiseOSError(osLastError())
|
||||
else:
|
||||
stdout.write("\e[2K")
|
||||
setCursorXPos(0)
|
||||
@@ -178,14 +178,14 @@ proc eraseScreen* =
|
||||
var numwrote: DWORD
|
||||
var origin: TCOORD # is inititalized to 0, 0
|
||||
var hStdout = conHandle
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: osError(osLastError())
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: raiseOSError(osLastError())
|
||||
if FillConsoleOutputCharacter(hStdout, ' ', scrbuf.dwSize.X*scrbuf.dwSize.Y,
|
||||
origin, addr(numwrote)) == 0:
|
||||
osError(osLastError())
|
||||
raiseOSError(osLastError())
|
||||
if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes,
|
||||
scrbuf.dwSize.X * scrbuf.dwSize.Y,
|
||||
origin, addr(numwrote)) == 0:
|
||||
osError(osLastError())
|
||||
raiseOSError(osLastError())
|
||||
setCursorXPos(0)
|
||||
else:
|
||||
stdout.write("\e[2J")
|
||||
@@ -199,7 +199,7 @@ proc resetAttributes* {.noconv.} =
|
||||
stdout.write("\e[0m")
|
||||
|
||||
type
|
||||
TStyle* = enum ## different styles for text output
|
||||
Style* = enum ## different styles for text output
|
||||
styleBright = 1, ## bright text
|
||||
styleDim, ## dim text
|
||||
styleUnknown, ## unknown
|
||||
@@ -208,6 +208,8 @@ type
|
||||
styleReverse = 7, ## unknown
|
||||
styleHidden ## hidden text
|
||||
|
||||
{.deprecated: [TStyle: Style].}
|
||||
|
||||
when not defined(windows):
|
||||
var
|
||||
# XXX: These better be thread-local
|
||||
@@ -244,7 +246,7 @@ proc writeStyled*(txt: string, style: set[TStyle] = {styleBright}) =
|
||||
stdout.write("\e[" & $ord(gBG) & 'm')
|
||||
|
||||
type
|
||||
TForegroundColor* = enum ## terminal's foreground colors
|
||||
ForegroundColor* = enum ## terminal's foreground colors
|
||||
fgBlack = 30, ## black
|
||||
fgRed, ## red
|
||||
fgGreen, ## green
|
||||
@@ -254,7 +256,7 @@ type
|
||||
fgCyan, ## cyan
|
||||
fgWhite ## white
|
||||
|
||||
TBackgroundColor* = enum ## terminal's background colors
|
||||
BackgroundColor* = enum ## terminal's background colors
|
||||
bgBlack = 40, ## black
|
||||
bgRed, ## red
|
||||
bgGreen, ## green
|
||||
@@ -264,13 +266,16 @@ type
|
||||
bgCyan, ## cyan
|
||||
bgWhite ## white
|
||||
|
||||
proc setForegroundColor*(fg: TForegroundColor, bright=false) =
|
||||
{.deprecated: [TForegroundColor: ForegroundColor,
|
||||
TBackgroundColor: BackgroundColor].}
|
||||
|
||||
proc setForegroundColor*(fg: ForegroundColor, bright=false) =
|
||||
## sets the terminal's foreground color
|
||||
when defined(windows):
|
||||
var old = getAttributes() and not 0x0007
|
||||
if bright:
|
||||
old = old or FOREGROUND_INTENSITY
|
||||
const lookup: array [TForegroundColor, int] = [
|
||||
const lookup: array [ForegroundColor, int] = [
|
||||
0,
|
||||
(FOREGROUND_RED),
|
||||
(FOREGROUND_GREEN),
|
||||
@@ -285,13 +290,13 @@ proc setForegroundColor*(fg: TForegroundColor, bright=false) =
|
||||
if bright: inc(gFG, 60)
|
||||
stdout.write("\e[" & $gFG & 'm')
|
||||
|
||||
proc setBackgroundColor*(bg: TBackgroundColor, bright=false) =
|
||||
proc setBackgroundColor*(bg: BackgroundColor, bright=false) =
|
||||
## sets the terminal's background color
|
||||
when defined(windows):
|
||||
var old = getAttributes() and not 0x0070
|
||||
if bright:
|
||||
old = old or BACKGROUND_INTENSITY
|
||||
const lookup: array [TBackgroundColor, int] = [
|
||||
const lookup: array [BackgroundColor, int] = [
|
||||
0,
|
||||
(BACKGROUND_RED),
|
||||
(BACKGROUND_GREEN),
|
||||
@@ -306,22 +311,22 @@ proc setBackgroundColor*(bg: TBackgroundColor, bright=false) =
|
||||
if bright: inc(gBG, 60)
|
||||
stdout.write("\e[" & $gBG & 'm')
|
||||
|
||||
proc isatty*(f: TFile): bool =
|
||||
proc isatty*(f: File): bool =
|
||||
## returns true if `f` is associated with a terminal device.
|
||||
when defined(posix):
|
||||
proc isatty(fildes: TFileHandle): cint {.
|
||||
proc isatty(fildes: FileHandle): cint {.
|
||||
importc: "isatty", header: "<unistd.h>".}
|
||||
else:
|
||||
proc isatty(fildes: TFileHandle): cint {.
|
||||
proc isatty(fildes: FileHandle): cint {.
|
||||
importc: "_isatty", header: "<io.h>".}
|
||||
|
||||
result = isatty(fileHandle(f)) != 0'i32
|
||||
result = isatty(getFileHandle(f)) != 0'i32
|
||||
|
||||
proc styledEchoProcessArg(s: string) = write stdout, s
|
||||
proc styledEchoProcessArg(style: TStyle) = setStyle({style})
|
||||
proc styledEchoProcessArg(style: set[TStyle]) = setStyle style
|
||||
proc styledEchoProcessArg(color: TForegroundColor) = setForegroundColor color
|
||||
proc styledEchoProcessArg(color: TBackgroundColor) = setBackgroundColor color
|
||||
proc styledEchoProcessArg(s: string) = write stdout, s
|
||||
proc styledEchoProcessArg(style: Style) = setStyle({style})
|
||||
proc styledEchoProcessArg(style: set[Style]) = setStyle style
|
||||
proc styledEchoProcessArg(color: ForegroundColor) = setForegroundColor color
|
||||
proc styledEchoProcessArg(color: BackgroundColor) = setBackgroundColor color
|
||||
|
||||
macro styledEcho*(m: varargs[expr]): stmt =
|
||||
## to be documented.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -21,9 +21,9 @@ import
|
||||
include "system/inclrtl"
|
||||
|
||||
type
|
||||
TMonth* = enum ## represents a month
|
||||
Month* = enum ## represents a month
|
||||
mJan, mFeb, mMar, mApr, mMay, mJun, mJul, mAug, mSep, mOct, mNov, mDec
|
||||
TWeekDay* = enum ## represents a weekday
|
||||
WeekDay* = enum ## represents a weekday
|
||||
dMon, dTue, dWed, dThu, dFri, dSat, dSun
|
||||
|
||||
var
|
||||
@@ -32,12 +32,12 @@ var
|
||||
|
||||
when defined(posix) and not defined(JS):
|
||||
type
|
||||
TTimeImpl {.importc: "time_t", header: "<sys/time.h>".} = int
|
||||
TTime* = distinct TTimeImpl ## distinct type that represents a time
|
||||
## measured as number of seconds since the epoch
|
||||
TimeImpl {.importc: "time_t", header: "<sys/time.h>".} = int
|
||||
Time* = distinct TimeImpl ## distinct type that represents a time
|
||||
## measured as number of seconds since the epoch
|
||||
|
||||
Ttimeval {.importc: "struct timeval", header: "<sys/select.h>",
|
||||
final, pure.} = object ## struct timeval
|
||||
Timeval {.importc: "struct timeval",
|
||||
header: "<sys/select.h>".} = object ## struct timeval
|
||||
tv_sec: int ## Seconds.
|
||||
tv_usec: int ## Microseconds.
|
||||
|
||||
@@ -45,7 +45,7 @@ when defined(posix) and not defined(JS):
|
||||
# Ok, we could, but I don't want circular dependencies.
|
||||
# And gettimeofday() is not defined in the posix module anyway. Sigh.
|
||||
|
||||
proc posix_gettimeofday(tp: var Ttimeval, unused: pointer = nil) {.
|
||||
proc posix_gettimeofday(tp: var Timeval, unused: pointer = nil) {.
|
||||
importc: "gettimeofday", header: "<sys/time.h>".}
|
||||
|
||||
elif defined(windows):
|
||||
@@ -53,16 +53,16 @@ elif defined(windows):
|
||||
|
||||
when defined(vcc):
|
||||
# newest version of Visual C++ defines time_t to be of 64 bits
|
||||
type TTimeImpl {.importc: "time_t", header: "<time.h>".} = int64
|
||||
type TimeImpl {.importc: "time_t", header: "<time.h>".} = int64
|
||||
else:
|
||||
type TTimeImpl {.importc: "time_t", header: "<time.h>".} = int32
|
||||
type TimeImpl {.importc: "time_t", header: "<time.h>".} = int32
|
||||
|
||||
type
|
||||
TTime* = distinct TTimeImpl
|
||||
Time* = distinct TimeImpl
|
||||
|
||||
elif defined(JS):
|
||||
type
|
||||
TTime* {.final, importc.} = object
|
||||
Time* {.importc.} = object
|
||||
getDay: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||
getFullYear: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||
getHours: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||
@@ -82,7 +82,7 @@ elif defined(JS):
|
||||
getUTCSeconds: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||
getUTCDay: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||
getYear: proc (): int {.tags: [], raises: [], gcsafe.}
|
||||
parse: proc (s: cstring): TTime {.tags: [], raises: [], gcsafe.}
|
||||
parse: proc (s: cstring): Time {.tags: [], raises: [], gcsafe.}
|
||||
setDate: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
||||
setFullYear: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
||||
setHours: proc (x: int) {.tags: [], raises: [], gcsafe.}
|
||||
@@ -103,7 +103,7 @@ elif defined(JS):
|
||||
toLocaleString: proc (): cstring {.tags: [], raises: [], gcsafe.}
|
||||
|
||||
type
|
||||
TTimeInfo* = object of RootObj ## represents a time in different parts
|
||||
TimeInfo* = object of RootObj ## represents a time in different parts
|
||||
second*: range[0..61] ## The number of seconds after the minute,
|
||||
## normally in the range 0 to 59, but can
|
||||
## be up to 61 to allow for leap seconds.
|
||||
@@ -112,9 +112,9 @@ type
|
||||
hour*: range[0..23] ## The number of hours past midnight,
|
||||
## in the range 0 to 23.
|
||||
monthday*: range[1..31] ## The day of the month, in the range 1 to 31.
|
||||
month*: TMonth ## The current month.
|
||||
month*: Month ## The current month.
|
||||
year*: range[-10_000..10_000] ## The current year.
|
||||
weekday*: TWeekDay ## The current day of the week.
|
||||
weekday*: WeekDay ## The current day of the week.
|
||||
yearday*: range[0..365] ## The number of days since January 1,
|
||||
## in the range 0 to 365.
|
||||
## Always 0 if the target is JS.
|
||||
@@ -127,7 +127,7 @@ type
|
||||
## I make some assumptions about the data in here. Either
|
||||
## everything should be positive or everything negative. Zero is
|
||||
## fine too. Mixed signs will lead to unexpected results.
|
||||
TTimeInterval* {.pure.} = object ## a time interval
|
||||
TimeInterval* = object ## a time interval
|
||||
miliseconds*: int ## The number of miliseconds
|
||||
seconds*: int ## The number of seconds
|
||||
minutes*: int ## The number of minutes
|
||||
@@ -136,55 +136,58 @@ type
|
||||
months*: int ## The number of months
|
||||
years*: int ## The number of years
|
||||
|
||||
proc getTime*(): TTime {.tags: [TimeEffect], gcsafe.}
|
||||
{.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time, TTimeval: Timeval,
|
||||
TTimeInterval: TimeInterval, TTimeInfo: TimeInfo].}
|
||||
|
||||
proc getTime*(): Time {.tags: [TimeEffect], gcsafe.}
|
||||
## gets the current calendar time as a UNIX epoch value (number of seconds
|
||||
## elapsed since 1970) with integer precission. Use epochTime for higher
|
||||
## resolution.
|
||||
proc getLocalTime*(t: TTime): TTimeInfo {.tags: [TimeEffect], raises: [], gcsafe.}
|
||||
proc getLocalTime*(t: Time): TimeInfo {.tags: [TimeEffect], raises: [], gcsafe.}
|
||||
## converts the calendar time `t` to broken-time representation,
|
||||
## expressed relative to the user's specified time zone.
|
||||
proc getGMTime*(t: TTime): TTimeInfo {.tags: [TimeEffect], raises: [], gcsafe.}
|
||||
proc getGMTime*(t: Time): TimeInfo {.tags: [TimeEffect], raises: [], gcsafe.}
|
||||
## converts the calendar time `t` to broken-down time representation,
|
||||
## expressed in Coordinated Universal Time (UTC).
|
||||
|
||||
proc timeInfoToTime*(timeInfo: TTimeInfo): TTime {.tags: [], gcsafe.}
|
||||
proc timeInfoToTime*(timeInfo: TimeInfo): Time {.tags: [], gcsafe.}
|
||||
## converts a broken-down time structure to
|
||||
## calendar time representation. The function ignores the specified
|
||||
## contents of the structure members `weekday` and `yearday` and recomputes
|
||||
## them from the other information in the broken-down time structure.
|
||||
|
||||
proc fromSeconds*(since1970: float): TTime {.tags: [], raises: [], gcsafe.}
|
||||
proc fromSeconds*(since1970: float): Time {.tags: [], raises: [], gcsafe.}
|
||||
## Takes a float which contains the number of seconds since the unix epoch and
|
||||
## returns a time object.
|
||||
|
||||
proc fromSeconds*(since1970: int64): TTime {.tags: [], raises: [], gcsafe.} =
|
||||
proc fromSeconds*(since1970: int64): Time {.tags: [], raises: [], gcsafe.} =
|
||||
## Takes an int which contains the number of seconds since the unix epoch and
|
||||
## returns a time object.
|
||||
fromSeconds(float(since1970))
|
||||
|
||||
proc toSeconds*(time: TTime): float {.tags: [], raises: [], gcsafe.}
|
||||
proc toSeconds*(time: Time): float {.tags: [], raises: [], gcsafe.}
|
||||
## Returns the time in seconds since the unix epoch.
|
||||
|
||||
proc `$` *(timeInfo: TTimeInfo): string {.tags: [], raises: [], gcsafe.}
|
||||
## converts a `TTimeInfo` object to a string representation.
|
||||
proc `$` *(time: TTime): string {.tags: [], raises: [], gcsafe.}
|
||||
proc `$` *(timeInfo: TimeInfo): string {.tags: [], raises: [], gcsafe.}
|
||||
## converts a `TimeInfo` object to a string representation.
|
||||
proc `$` *(time: Time): string {.tags: [], raises: [], gcsafe.}
|
||||
## converts a calendar time to a string representation.
|
||||
|
||||
proc `-`*(a, b: TTime): int64 {.
|
||||
proc `-`*(a, b: Time): int64 {.
|
||||
rtl, extern: "ntDiffTime", tags: [], raises: [].}
|
||||
## computes the difference of two calendar times. Result is in seconds.
|
||||
|
||||
proc `<`*(a, b: TTime): bool {.
|
||||
proc `<`*(a, b: Time): bool {.
|
||||
rtl, extern: "ntLtTime", tags: [], raises: [].} =
|
||||
## returns true iff ``a < b``, that is iff a happened before b.
|
||||
result = a - b < 0
|
||||
|
||||
proc `<=` * (a, b: TTime): bool {.
|
||||
proc `<=` * (a, b: Time): bool {.
|
||||
rtl, extern: "ntLeTime", tags: [], raises: [].}=
|
||||
## returns true iff ``a <= b``.
|
||||
result = a - b <= 0
|
||||
|
||||
proc `==`*(a, b: TTime): bool {.
|
||||
proc `==`*(a, b: Time): bool {.
|
||||
rtl, extern: "ntEqTime", tags: [], raises: [].} =
|
||||
## returns true if ``a == b``, that is if both times represent the same value
|
||||
result = a - b == 0
|
||||
@@ -203,8 +206,8 @@ proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], gcsafe.}
|
||||
## version 0.8.10.** Use ``epochTime`` or ``cpuTime`` instead.
|
||||
|
||||
proc initInterval*(miliseconds, seconds, minutes, hours, days, months,
|
||||
years: int = 0): TTimeInterval =
|
||||
## creates a new ``TTimeInterval``.
|
||||
years: int = 0): TimeInterval =
|
||||
## creates a new ``TimeInterval``.
|
||||
result.miliseconds = miliseconds
|
||||
result.seconds = seconds
|
||||
result.minutes = minutes
|
||||
@@ -234,7 +237,7 @@ proc getDaysInMonth*(month: TMonth, year: int): int =
|
||||
of mApr, mJun, mSep, mNov: result = 30
|
||||
else: result = 31
|
||||
|
||||
proc toSeconds(a: TTimeInfo, interval: TTimeInterval): float =
|
||||
proc toSeconds(a: TimeInfo, interval: TimeInterval): float =
|
||||
## Calculates how many seconds the interval is worth by adding up
|
||||
## all the fields
|
||||
|
||||
@@ -257,7 +260,7 @@ proc toSeconds(a: TTimeInfo, interval: TTimeInterval): float =
|
||||
result += float(newinterv.seconds)
|
||||
result += newinterv.miliseconds / 1000
|
||||
|
||||
proc `+`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo =
|
||||
proc `+`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
|
||||
## adds ``interval`` time.
|
||||
##
|
||||
## **Note:** This has been only briefly tested and it may not be
|
||||
@@ -269,7 +272,7 @@ proc `+`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo =
|
||||
else:
|
||||
result = getLocalTime(fromSeconds(t + secs))
|
||||
|
||||
proc `-`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo =
|
||||
proc `-`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
|
||||
## subtracts ``interval`` time.
|
||||
##
|
||||
## **Note:** This has been only briefly tested, it is inaccurate especially
|
||||
@@ -314,38 +317,36 @@ when not defined(JS):
|
||||
yearday {.importc: "tm_yday".},
|
||||
isdst {.importc: "tm_isdst".}: cint
|
||||
|
||||
PTimeInfo = ptr StructTM
|
||||
PTime = ptr TTime
|
||||
TimeInfoPtr = ptr StructTM
|
||||
Clock {.importc: "clock_t".} = distinct int
|
||||
|
||||
TClock {.importc: "clock_t".} = distinct int
|
||||
|
||||
proc localtime(timer: PTime): PTimeInfo {.
|
||||
proc localtime(timer: ptr Time): TimeInfoPtr {.
|
||||
importc: "localtime", header: "<time.h>", tags: [].}
|
||||
proc gmtime(timer: PTime): PTimeInfo {.
|
||||
proc gmtime(timer: ptr Time): TimeInfoPtr {.
|
||||
importc: "gmtime", header: "<time.h>", tags: [].}
|
||||
proc timec(timer: PTime): TTime {.
|
||||
proc timec(timer: ptr Time): Time {.
|
||||
importc: "time", header: "<time.h>", tags: [].}
|
||||
proc mktime(t: StructTM): TTime {.
|
||||
proc mktime(t: StructTM): Time {.
|
||||
importc: "mktime", header: "<time.h>", tags: [].}
|
||||
proc asctime(tblock: StructTM): cstring {.
|
||||
importc: "asctime", header: "<time.h>", tags: [].}
|
||||
proc ctime(time: PTime): cstring {.
|
||||
proc ctime(time: ptr Time): cstring {.
|
||||
importc: "ctime", header: "<time.h>", tags: [].}
|
||||
# strftime(s: CString, maxsize: int, fmt: CString, t: tm): int {.
|
||||
# importc: "strftime", header: "<time.h>".}
|
||||
proc clock(): TClock {.importc: "clock", header: "<time.h>", tags: [TimeEffect].}
|
||||
proc difftime(a, b: TTime): float {.importc: "difftime", header: "<time.h>",
|
||||
proc getClock(): Clock {.importc: "clock", header: "<time.h>", tags: [TimeEffect].}
|
||||
proc difftime(a, b: Time): float {.importc: "difftime", header: "<time.h>",
|
||||
tags: [].}
|
||||
|
||||
var
|
||||
clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl.}: int
|
||||
|
||||
# our own procs on top of that:
|
||||
proc tmToTimeInfo(tm: StructTM, local: bool): TTimeInfo =
|
||||
proc tmToTimeInfo(tm: StructTM, local: bool): TimeInfo =
|
||||
const
|
||||
weekDays: array [0..6, TWeekDay] = [
|
||||
weekDays: array [0..6, WeekDay] = [
|
||||
dSun, dMon, dTue, dWed, dThu, dFri, dSat]
|
||||
TTimeInfo(second: int(tm.second),
|
||||
TimeInfo(second: int(tm.second),
|
||||
minute: int(tm.minute),
|
||||
hour: int(tm.hour),
|
||||
monthday: int(tm.monthday),
|
||||
@@ -364,9 +365,9 @@ when not defined(JS):
|
||||
timezone: if local: getTimezone() else: 0
|
||||
)
|
||||
|
||||
proc timeInfoToTM(t: TTimeInfo): StructTM =
|
||||
proc timeInfoToTM(t: TimeInfo): StructTM =
|
||||
const
|
||||
weekDays: array [TWeekDay, int8] = [1'i8,2'i8,3'i8,4'i8,5'i8,6'i8,0'i8]
|
||||
weekDays: array [WeekDay, int8] = [1'i8,2'i8,3'i8,4'i8,5'i8,6'i8,0'i8]
|
||||
result.second = t.second
|
||||
result.minute = t.minute
|
||||
result.hour = t.hour
|
||||
@@ -378,36 +379,36 @@ when not defined(JS):
|
||||
result.isdst = if t.isDST: 1 else: 0
|
||||
|
||||
when not defined(useNimRtl):
|
||||
proc `-` (a, b: TTime): int64 =
|
||||
proc `-` (a, b: Time): int64 =
|
||||
return toBiggestInt(difftime(a, b))
|
||||
|
||||
proc getStartMilsecs(): int =
|
||||
#echo "clocks per sec: ", clocksPerSec, "clock: ", int(clock())
|
||||
#return clock() div (clocksPerSec div 1000)
|
||||
#echo "clocks per sec: ", clocksPerSec, "clock: ", int(getClock())
|
||||
#return getClock() div (clocksPerSec div 1000)
|
||||
when defined(macosx):
|
||||
result = toInt(toFloat(int(clock())) / (toFloat(clocksPerSec) / 1000.0))
|
||||
result = toInt(toFloat(int(getClock())) / (toFloat(clocksPerSec) / 1000.0))
|
||||
else:
|
||||
result = int(clock()) div (clocksPerSec div 1000)
|
||||
result = int(getClock()) div (clocksPerSec div 1000)
|
||||
when false:
|
||||
var a: Ttimeval
|
||||
var a: Timeval
|
||||
posix_gettimeofday(a)
|
||||
result = a.tv_sec * 1000'i64 + a.tv_usec div 1000'i64
|
||||
#echo "result: ", result
|
||||
|
||||
proc getTime(): TTime = return timec(nil)
|
||||
proc getLocalTime(t: TTime): TTimeInfo =
|
||||
proc getTime(): Time = return timec(nil)
|
||||
proc getLocalTime(t: Time): TimeInfo =
|
||||
var a = t
|
||||
result = tmToTimeInfo(localtime(addr(a))[], true)
|
||||
# copying is needed anyway to provide reentrancity; thus
|
||||
# the conversion is not expensive
|
||||
|
||||
proc getGMTime(t: TTime): TTimeInfo =
|
||||
proc getGMTime(t: Time): TimeInfo =
|
||||
var a = t
|
||||
result = tmToTimeInfo(gmtime(addr(a))[], false)
|
||||
# copying is needed anyway to provide reentrancity; thus
|
||||
# the conversion is not expensive
|
||||
|
||||
proc timeInfoToTime(timeInfo: TTimeInfo): TTime =
|
||||
proc timeInfoToTime(timeInfo: TimeInfo): Time =
|
||||
var cTimeInfo = timeInfo # for C++ we have to make a copy,
|
||||
# because the header of mktime is broken in my version of libc
|
||||
return mktime(timeInfoToTM(cTimeInfo))
|
||||
@@ -419,12 +420,12 @@ when not defined(JS):
|
||||
add(result, p[i])
|
||||
inc(i)
|
||||
|
||||
proc `$`(timeInfo: TTimeInfo): string =
|
||||
proc `$`(timeInfo: TimeInfo): string =
|
||||
# BUGFIX: asctime returns a newline at the end!
|
||||
var p = asctime(timeInfoToTM(timeInfo))
|
||||
result = toStringTillNL(p)
|
||||
|
||||
proc `$`(time: TTime): string =
|
||||
proc `$`(time: Time): string =
|
||||
# BUGFIX: ctime returns a newline at the end!
|
||||
var a = time
|
||||
return toStringTillNL(ctime(addr(a)))
|
||||
@@ -433,13 +434,13 @@ when not defined(JS):
|
||||
epochDiff = 116444736000000000'i64
|
||||
rateDiff = 10000000'i64 # 100 nsecs
|
||||
|
||||
proc unixTimeToWinTime*(t: TTime): int64 =
|
||||
## converts a UNIX `TTime` (``time_t``) to a Windows file time
|
||||
proc unixTimeToWinTime*(t: Time): int64 =
|
||||
## converts a UNIX `Time` (``time_t``) to a Windows file time
|
||||
result = int64(t) * rateDiff + epochDiff
|
||||
|
||||
proc winTimeToUnixTime*(t: int64): TTime =
|
||||
## converts a Windows time to a UNIX `TTime` (``time_t``)
|
||||
result = TTime((t - epochDiff) div rateDiff)
|
||||
proc winTimeToUnixTime*(t: int64): Time =
|
||||
## converts a Windows time to a UNIX `Time` (``time_t``)
|
||||
result = Time((t - epochDiff) div rateDiff)
|
||||
|
||||
proc getTzname(): tuple[nonDST, DST: string] =
|
||||
return ($tzname[0], $tzname[1])
|
||||
@@ -447,9 +448,9 @@ when not defined(JS):
|
||||
proc getTimezone(): int =
|
||||
return timezone
|
||||
|
||||
proc fromSeconds(since1970: float): TTime = TTime(since1970)
|
||||
proc fromSeconds(since1970: float): Time = Time(since1970)
|
||||
|
||||
proc toSeconds(time: TTime): float = float(time)
|
||||
proc toSeconds(time: Time): float = float(time)
|
||||
|
||||
when not defined(useNimRtl):
|
||||
proc epochTime(): float =
|
||||
@@ -468,15 +469,15 @@ when not defined(JS):
|
||||
{.error: "unknown OS".}
|
||||
|
||||
proc cpuTime(): float =
|
||||
result = toFloat(int(clock())) / toFloat(clocksPerSec)
|
||||
result = toFloat(int(getClock())) / toFloat(clocksPerSec)
|
||||
|
||||
elif defined(JS):
|
||||
proc newDate(): TTime {.importc: "new Date".}
|
||||
proc internGetTime(): TTime {.importc: "new Date", tags: [].}
|
||||
proc newDate(): Time {.importc: "new Date".}
|
||||
proc internGetTime(): Time {.importc: "new Date", tags: [].}
|
||||
|
||||
proc newDate(value: float): TTime {.importc: "new Date".}
|
||||
proc newDate(value: string): TTime {.importc: "new Date".}
|
||||
proc getTime(): TTime =
|
||||
proc newDate(value: float): Time {.importc: "new Date".}
|
||||
proc newDate(value: string): Time {.importc: "new Date".}
|
||||
proc getTime(): Time =
|
||||
# Warning: This is something different in JS.
|
||||
return newDate()
|
||||
|
||||
@@ -484,7 +485,7 @@ elif defined(JS):
|
||||
weekDays: array [0..6, TWeekDay] = [
|
||||
dSun, dMon, dTue, dWed, dThu, dFri, dSat]
|
||||
|
||||
proc getLocalTime(t: TTime): TTimeInfo =
|
||||
proc getLocalTime(t: Time): TimeInfo =
|
||||
result.second = t.getSeconds()
|
||||
result.minute = t.getMinutes()
|
||||
result.hour = t.getHours()
|
||||
@@ -494,7 +495,7 @@ elif defined(JS):
|
||||
result.weekday = weekDays[t.getDay()]
|
||||
result.yearday = 0
|
||||
|
||||
proc getGMTime(t: TTime): TTimeInfo =
|
||||
proc getGMTime(t: Time): TimeInfo =
|
||||
result.second = t.getUTCSeconds()
|
||||
result.minute = t.getUTCMinutes()
|
||||
result.hour = t.getUTCHours()
|
||||
@@ -504,7 +505,7 @@ elif defined(JS):
|
||||
result.weekday = weekDays[t.getUTCDay()]
|
||||
result.yearday = 0
|
||||
|
||||
proc timeInfoToTime*(timeInfo: TTimeInfo): TTime =
|
||||
proc timeInfoToTime*(timeInfo: TimeInfo): Time =
|
||||
result = internGetTime()
|
||||
result.setSeconds(timeInfo.second)
|
||||
result.setMinutes(timeInfo.minute)
|
||||
@@ -513,10 +514,10 @@ elif defined(JS):
|
||||
result.setFullYear(timeInfo.year)
|
||||
result.setDate(timeInfo.monthday)
|
||||
|
||||
proc `$`(timeInfo: TTimeInfo): string = return $(TimeInfoToTIme(timeInfo))
|
||||
proc `$`(time: TTime): string = return $time.toLocaleString()
|
||||
proc `$`(timeInfo: TimeInfo): string = return $(TimeInfoToTIme(timeInfo))
|
||||
proc `$`(time: Time): string = return $time.toLocaleString()
|
||||
|
||||
proc `-` (a, b: TTime): int64 =
|
||||
proc `-` (a, b: Time): int64 =
|
||||
return a.getTime() - b.getTime()
|
||||
|
||||
var
|
||||
@@ -526,11 +527,11 @@ elif defined(JS):
|
||||
## get the miliseconds from the start of the program
|
||||
return int(getTime() - startMilsecs)
|
||||
|
||||
proc valueOf(time: TTime): float {.importcpp: "getTime", tags:[]}
|
||||
proc valueOf(time: Time): float {.importcpp: "getTime", tags:[]}
|
||||
|
||||
proc fromSeconds(since1970: float): TTime = result = newDate(since1970)
|
||||
proc fromSeconds(since1970: float): Time = result = newDate(since1970)
|
||||
|
||||
proc toSeconds(time: TTime): float = result = time.valueOf() / 1000
|
||||
proc toSeconds(time: Time): float = result = time.valueOf() / 1000
|
||||
|
||||
proc getTimezone(): int = result = newDate().getTimezoneOffset()
|
||||
|
||||
@@ -546,20 +547,20 @@ proc getClockStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} =
|
||||
result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) &
|
||||
':' & intToStr(ti.second, 2)
|
||||
|
||||
proc `$`*(day: TWeekDay): string =
|
||||
## stingify operator for ``TWeekDay``.
|
||||
const lookup: array[TWeekDay, string] = ["Monday", "Tuesday", "Wednesday",
|
||||
proc `$`*(day: WeekDay): string =
|
||||
## stingify operator for ``WeekDay``.
|
||||
const lookup: array[WeekDay, string] = ["Monday", "Tuesday", "Wednesday",
|
||||
"Thursday", "Friday", "Saturday", "Sunday"]
|
||||
return lookup[day]
|
||||
|
||||
proc `$`*(m: TMonth): string =
|
||||
proc `$`*(m: Month): string =
|
||||
## stingify operator for ``TMonth``.
|
||||
const lookup: array[TMonth, string] = ["January", "February", "March",
|
||||
const lookup: array[Month, string] = ["January", "February", "March",
|
||||
"April", "May", "June", "July", "August", "September", "October",
|
||||
"November", "December"]
|
||||
return lookup[m]
|
||||
|
||||
proc format_token(info: TTimeInfo, token: string, buf: var string) =
|
||||
proc formatToken(info: TimeInfo, token: string, buf: var string) =
|
||||
## Helper of the format proc to parse individual tokens.
|
||||
##
|
||||
## Pass the found token in the user input string, and the buffer where the
|
||||
@@ -672,7 +673,7 @@ proc format_token(info: TTimeInfo, token: string, buf: var string) =
|
||||
raise newException(ValueError, "Invalid format string: " & token)
|
||||
|
||||
|
||||
proc format*(info: TTimeInfo, f: string): string =
|
||||
proc format*(info: TimeInfo, f: string): string =
|
||||
## This function formats `info` as specified by `f`. The following format
|
||||
## specifiers are available:
|
||||
##
|
||||
@@ -718,7 +719,7 @@ proc format*(info: TTimeInfo, f: string): string =
|
||||
while true:
|
||||
case f[i]
|
||||
of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
|
||||
format_token(info, currentF, result)
|
||||
formatToken(info, currentF, result)
|
||||
|
||||
currentF = ""
|
||||
if f[i] == '\0': break
|
||||
@@ -735,7 +736,7 @@ proc format*(info: TTimeInfo, f: string): string =
|
||||
if currentF.len < 1 or currentF[high(currentF)] == f[i]:
|
||||
currentF.add(f[i])
|
||||
else:
|
||||
format_token(info, currentF, result)
|
||||
formatToken(info, currentF, result)
|
||||
dec(i) # Move position back to re-process the character separately.
|
||||
currentF = ""
|
||||
|
||||
@@ -764,7 +765,7 @@ when isMainModule:
|
||||
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
|
||||
"27 27 Mon Monday 4 04 16 16 6 06 1 01 Jan January 29 29 P PM 5 75 975 1975 01975 0 00 00:00 UTC"
|
||||
|
||||
when not defined(JS) and sizeof(TTime) == 8:
|
||||
when not defined(JS) and sizeof(Time) == 8:
|
||||
var t3 = getGMTime(fromSeconds(889067643645)) # Fri 7 Jun 19:20:45 BST 30143
|
||||
assert t3.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
|
||||
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Nimrod Contributors
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -14,13 +14,15 @@
|
||||
include "system/inclrtl"
|
||||
|
||||
type
|
||||
IRune = int # underlying type of TRune
|
||||
TRune* = distinct IRune ## type that can hold any Unicode character
|
||||
TRune16* = distinct int16 ## 16 bit Unicode character
|
||||
RuneImpl = int # underlying type of Rune
|
||||
Rune* = distinct RuneImpl ## type that can hold any Unicode character
|
||||
Rune16* = distinct int16 ## 16 bit Unicode character
|
||||
|
||||
{.deprecated: [TRune: Rune, TRune16: Rune16].}
|
||||
|
||||
proc `<=%`*(a, b: TRune): bool = return int(a) <=% int(b)
|
||||
proc `<%`*(a, b: TRune): bool = return int(a) <% int(b)
|
||||
proc `==`*(a, b: TRune): bool = return int(a) == int(b)
|
||||
proc `<=%`*(a, b: Rune): bool = return int(a) <=% int(b)
|
||||
proc `<%`*(a, b: Rune): bool = return int(a) <% int(b)
|
||||
proc `==`*(a, b: Rune): bool = return int(a) == int(b)
|
||||
|
||||
template ones(n: expr): expr = ((1 shl n)-1)
|
||||
|
||||
@@ -52,17 +54,17 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) =
|
||||
## `i` is incremented by the number of bytes that have been processed.
|
||||
bind ones
|
||||
if ord(s[i]) <=% 127:
|
||||
result = TRune(ord(s[i]))
|
||||
result = Rune(ord(s[i]))
|
||||
when doInc: inc(i)
|
||||
elif ord(s[i]) shr 5 == 0b110:
|
||||
# assert(ord(s[i+1]) shr 6 == 0b10)
|
||||
result = TRune((ord(s[i]) and (ones(5))) shl 6 or
|
||||
(ord(s[i+1]) and ones(6)))
|
||||
result = Rune((ord(s[i]) and (ones(5))) shl 6 or
|
||||
(ord(s[i+1]) and ones(6)))
|
||||
when doInc: inc(i, 2)
|
||||
elif ord(s[i]) shr 4 == 0b1110:
|
||||
# assert(ord(s[i+1]) shr 6 == 0b10)
|
||||
# assert(ord(s[i+2]) shr 6 == 0b10)
|
||||
result = TRune((ord(s[i]) and ones(4)) shl 12 or
|
||||
result = Rune((ord(s[i]) and ones(4)) shl 12 or
|
||||
(ord(s[i+1]) and ones(6)) shl 6 or
|
||||
(ord(s[i+2]) and ones(6)))
|
||||
when doInc: inc(i, 3)
|
||||
@@ -70,7 +72,7 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) =
|
||||
# assert(ord(s[i+1]) shr 6 == 0b10)
|
||||
# assert(ord(s[i+2]) shr 6 == 0b10)
|
||||
# assert(ord(s[i+3]) shr 6 == 0b10)
|
||||
result = TRune((ord(s[i]) and ones(3)) shl 18 or
|
||||
result = Rune((ord(s[i]) and ones(3)) shl 18 or
|
||||
(ord(s[i+1]) and ones(6)) shl 12 or
|
||||
(ord(s[i+2]) and ones(6)) shl 6 or
|
||||
(ord(s[i+3]) and ones(6)))
|
||||
@@ -80,7 +82,7 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) =
|
||||
# assert(ord(s[i+2]) shr 6 == 0b10)
|
||||
# assert(ord(s[i+3]) shr 6 == 0b10)
|
||||
# assert(ord(s[i+4]) shr 6 == 0b10)
|
||||
result = TRune((ord(s[i]) and ones(2)) shl 24 or
|
||||
result = Rune((ord(s[i]) and ones(2)) shl 24 or
|
||||
(ord(s[i+1]) and ones(6)) shl 18 or
|
||||
(ord(s[i+2]) and ones(6)) shl 12 or
|
||||
(ord(s[i+3]) and ones(6)) shl 6 or
|
||||
@@ -92,7 +94,7 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) =
|
||||
# assert(ord(s[i+3]) shr 6 == 0b10)
|
||||
# assert(ord(s[i+4]) shr 6 == 0b10)
|
||||
# assert(ord(s[i+5]) shr 6 == 0b10)
|
||||
result = TRune((ord(s[i]) and ones(1)) shl 30 or
|
||||
result = Rune((ord(s[i]) and ones(1)) shl 30 or
|
||||
(ord(s[i+1]) and ones(6)) shl 24 or
|
||||
(ord(s[i+2]) and ones(6)) shl 18 or
|
||||
(ord(s[i+3]) and ones(6)) shl 12 or
|
||||
@@ -100,16 +102,16 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) =
|
||||
(ord(s[i+5]) and ones(6)))
|
||||
when doInc: inc(i, 6)
|
||||
else:
|
||||
result = TRune(ord(s[i]))
|
||||
result = Rune(ord(s[i]))
|
||||
when doInc: inc(i)
|
||||
|
||||
proc runeAt*(s: string, i: int): TRune =
|
||||
proc runeAt*(s: string, i: int): Rune =
|
||||
## returns the unicode character in `s` at byte index `i`
|
||||
fastRuneAt(s, i, result, false)
|
||||
|
||||
proc toUTF8*(c: TRune): string {.rtl, extern: "nuc$1".} =
|
||||
proc toUTF8*(c: Rune): string {.rtl, extern: "nuc$1".} =
|
||||
## converts a rune into its UTF8 representation
|
||||
var i = IRune(c)
|
||||
var i = RuneImpl(c)
|
||||
if i <=% 127:
|
||||
result = newString(1)
|
||||
result[0] = chr(i)
|
||||
@@ -132,11 +134,11 @@ proc toUTF8*(c: TRune): string {.rtl, extern: "nuc$1".} =
|
||||
result = newString(1)
|
||||
result[0] = chr(i)
|
||||
|
||||
proc `$`*(rune: TRune): string =
|
||||
proc `$`*(rune: Rune): string =
|
||||
## converts a rune to a string
|
||||
rune.toUTF8
|
||||
|
||||
proc `$`*(runes: seq[TRune]): string =
|
||||
proc `$`*(runes: seq[Rune]): string =
|
||||
## converts a sequence of runes to a string
|
||||
result = ""
|
||||
for rune in runes: result.add(rune.toUTF8)
|
||||
@@ -1100,7 +1102,7 @@ const
|
||||
0x01f1, 501, #
|
||||
0x01f3, 499] #
|
||||
|
||||
proc binarySearch(c: IRune, tab: openArray[IRune], len, stride: int): int =
|
||||
proc binarySearch(c: RuneImpl, tab: openArray[RuneImpl], len, stride: int): int =
|
||||
var n = len
|
||||
var t = 0
|
||||
while n > 1:
|
||||
@@ -1115,41 +1117,41 @@ proc binarySearch(c: IRune, tab: openArray[IRune], len, stride: int): int =
|
||||
return t
|
||||
return -1
|
||||
|
||||
proc toLower*(c: TRune): TRune {.rtl, extern: "nuc$1", procvar.} =
|
||||
proc toLower*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
|
||||
## Converts `c` into lower case. This works for any Unicode character.
|
||||
## If possible, prefer `toLower` over `toUpper`.
|
||||
var c = IRune(c)
|
||||
var c = RuneImpl(c)
|
||||
var p = binarySearch(c, tolowerRanges, len(tolowerRanges) div 3, 3)
|
||||
if p >= 0 and c >= tolowerRanges[p] and c <= tolowerRanges[p+1]:
|
||||
return TRune(c + tolowerRanges[p+2] - 500)
|
||||
return Rune(c + tolowerRanges[p+2] - 500)
|
||||
p = binarySearch(c, tolowerSinglets, len(tolowerSinglets) div 2, 2)
|
||||
if p >= 0 and c == tolowerSinglets[p]:
|
||||
return TRune(c + tolowerSinglets[p+1] - 500)
|
||||
return TRune(c)
|
||||
return Rune(c + tolowerSinglets[p+1] - 500)
|
||||
return Rune(c)
|
||||
|
||||
proc toUpper*(c: TRune): TRune {.rtl, extern: "nuc$1", procvar.} =
|
||||
proc toUpper*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
|
||||
## Converts `c` into upper case. This works for any Unicode character.
|
||||
## If possible, prefer `toLower` over `toUpper`.
|
||||
var c = IRune(c)
|
||||
var c = RuneImpl(c)
|
||||
var p = binarySearch(c, toupperRanges, len(toupperRanges) div 3, 3)
|
||||
if p >= 0 and c >= toupperRanges[p] and c <= toupperRanges[p+1]:
|
||||
return TRune(c + toupperRanges[p+2] - 500)
|
||||
return Rune(c + toupperRanges[p+2] - 500)
|
||||
p = binarySearch(c, toupperSinglets, len(toupperSinglets) div 2, 2)
|
||||
if p >= 0 and c == toupperSinglets[p]:
|
||||
return TRune(c + toupperSinglets[p+1] - 500)
|
||||
return TRune(c)
|
||||
return Rune(c + toupperSinglets[p+1] - 500)
|
||||
return Rune(c)
|
||||
|
||||
proc toTitle*(c: TRune): TRune {.rtl, extern: "nuc$1", procvar.} =
|
||||
var c = IRune(c)
|
||||
proc toTitle*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
|
||||
var c = RuneImpl(c)
|
||||
var p = binarySearch(c, toTitleSinglets, len(toTitleSinglets) div 2, 2)
|
||||
if p >= 0 and c == toTitleSinglets[p]:
|
||||
return TRune(c + toTitleSinglets[p+1] - 500)
|
||||
return TRune(c)
|
||||
return Rune(c + toTitleSinglets[p+1] - 500)
|
||||
return Rune(c)
|
||||
|
||||
proc isLower*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||
proc isLower*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||
## returns true iff `c` is a lower case Unicode character
|
||||
## If possible, prefer `isLower` over `isUpper`.
|
||||
var c = IRune(c)
|
||||
var c = RuneImpl(c)
|
||||
# Note: toUpperRanges is correct here!
|
||||
var p = binarySearch(c, toupperRanges, len(toupperRanges) div 3, 3)
|
||||
if p >= 0 and c >= toupperRanges[p] and c <= toupperRanges[p+1]:
|
||||
@@ -1158,10 +1160,10 @@ proc isLower*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||
if p >= 0 and c == toupperSinglets[p]:
|
||||
return true
|
||||
|
||||
proc isUpper*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||
proc isUpper*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||
## returns true iff `c` is a upper case Unicode character
|
||||
## If possible, prefer `isLower` over `isUpper`.
|
||||
var c = IRune(c)
|
||||
var c = RuneImpl(c)
|
||||
# Note: toLowerRanges is correct here!
|
||||
var p = binarySearch(c, tolowerRanges, len(tolowerRanges) div 3, 3)
|
||||
if p >= 0 and c >= tolowerRanges[p] and c <= tolowerRanges[p+1]:
|
||||
@@ -1170,11 +1172,11 @@ proc isUpper*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||
if p >= 0 and c == tolowerSinglets[p]:
|
||||
return true
|
||||
|
||||
proc isAlpha*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||
proc isAlpha*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||
## returns true iff `c` is an *alpha* Unicode character (i.e. a letter)
|
||||
if isUpper(c) or isLower(c):
|
||||
return true
|
||||
var c = IRune(c)
|
||||
var c = RuneImpl(c)
|
||||
var p = binarySearch(c, alphaRanges, len(alphaRanges) div 2, 2)
|
||||
if p >= 0 and c >= alphaRanges[p] and c <= alphaRanges[p+1]:
|
||||
return true
|
||||
@@ -1182,21 +1184,21 @@ proc isAlpha*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||
if p >= 0 and c == alphaSinglets[p]:
|
||||
return true
|
||||
|
||||
proc isTitle*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||
proc isTitle*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||
return isUpper(c) and isLower(c)
|
||||
|
||||
proc isWhiteSpace*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||
proc isWhiteSpace*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
|
||||
## returns true iff `c` is a Unicode whitespace character
|
||||
var c = IRune(c)
|
||||
var c = RuneImpl(c)
|
||||
var p = binarySearch(c, spaceRanges, len(spaceRanges) div 2, 2)
|
||||
if p >= 0 and c >= spaceRanges[p] and c <= spaceRanges[p+1]:
|
||||
return true
|
||||
|
||||
iterator runes*(s: string): TRune =
|
||||
iterator runes*(s: string): Rune =
|
||||
## iterates over any unicode character of the string `s`.
|
||||
var
|
||||
i = 0
|
||||
result: TRune
|
||||
result: Rune
|
||||
while i < len(s):
|
||||
fastRuneAt(s, i, result, true)
|
||||
yield result
|
||||
@@ -1209,12 +1211,12 @@ proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1", procvar.} =
|
||||
## | > 0 iff a > b
|
||||
var i = 0
|
||||
var j = 0
|
||||
var ar, br: TRune
|
||||
var ar, br: Rune
|
||||
while i < a.len and j < b.len:
|
||||
# slow path:
|
||||
fastRuneAt(a, i, ar)
|
||||
fastRuneAt(b, j, br)
|
||||
result = IRune(toLower(ar)) - IRune(toLower(br))
|
||||
result = RuneImpl(toLower(ar)) - RuneImpl(toLower(br))
|
||||
if result != 0: return
|
||||
result = a.len - b.len
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -55,7 +55,7 @@ proc unidecode*(s: string): string =
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## ..code-block:: nimrod
|
||||
## ..code-block:: nim
|
||||
##
|
||||
## unidecode("\x53\x17\x4E\xB0")
|
||||
##
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Nimrod Contributors
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -26,16 +26,19 @@ when not defined(ECMAScript):
|
||||
import terminal
|
||||
|
||||
type
|
||||
TTestStatus* = enum OK, FAILED
|
||||
TOutputLevel* = enum PRINT_ALL, PRINT_FAILURES, PRINT_NONE
|
||||
TestStatus* = enum OK, FAILED
|
||||
OutputLevel* = enum PRINT_ALL, PRINT_FAILURES, PRINT_NONE
|
||||
|
||||
{.deprecated: [TTestStatus: TestStatus, TOutputLevel: OutputLevel]}
|
||||
|
||||
var
|
||||
# XXX: These better be thread-local
|
||||
AbortOnError*: bool
|
||||
OutputLevel*: TOutputLevel
|
||||
ColorOutput*: bool
|
||||
abortOnError* {.threadvar.}: bool
|
||||
outputLevel* {.threadvar.}: OutputLevel
|
||||
colorOutput* {.threadvar.}: bool
|
||||
|
||||
checkpoints: seq[string] = @[]
|
||||
checkpoints {.threadvar.}: seq[string]
|
||||
|
||||
checkpoints = @[]
|
||||
|
||||
template TestSetupIMPL*: stmt {.immediate, dirty.} = discard
|
||||
template TestTeardownIMPL*: stmt {.immediate, dirty.} = discard
|
||||
@@ -53,7 +56,7 @@ template suite*(name: expr, body: stmt): stmt {.immediate, dirty.} =
|
||||
|
||||
body
|
||||
|
||||
proc testDone(name: string, s: TTestStatus) =
|
||||
proc testDone(name: string, s: TestStatus) =
|
||||
if s == FAILED:
|
||||
program_result += 1
|
||||
|
||||
@@ -192,15 +195,15 @@ when declared(stdout):
|
||||
## Reading settings
|
||||
var envOutLvl = os.getEnv("NIMTEST_OUTPUT_LVL").string
|
||||
|
||||
AbortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")
|
||||
ColorOutput = not existsEnv("NIMTEST_NO_COLOR")
|
||||
abortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")
|
||||
colorOutput = not existsEnv("NIMTEST_NO_COLOR")
|
||||
|
||||
else:
|
||||
var envOutLvl = "" # TODO
|
||||
ColorOutput = false
|
||||
colorOutput = false
|
||||
|
||||
if envOutLvl.len > 0:
|
||||
for opt in countup(low(TOutputLevel), high(TOutputLevel)):
|
||||
for opt in countup(low(OutputLevel), high(OutputLevel)):
|
||||
if $opt == envOutLvl:
|
||||
OutputLevel = opt
|
||||
outputLevel = opt
|
||||
break
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2014 Dominik Picheta
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -11,12 +11,14 @@
|
||||
|
||||
import strutils, parseutils
|
||||
type
|
||||
TUrl* = distinct string
|
||||
Url* = distinct string
|
||||
|
||||
TUri* = object
|
||||
Uri* = object
|
||||
scheme*, username*, password*: string
|
||||
hostname*, port*, path*, query*, anchor*: string
|
||||
|
||||
{.deprecated: [TUrl: Url, TUri: Uri].}
|
||||
|
||||
proc `$`*(url: TUrl): string {.deprecated.} =
|
||||
## **Deprecated since 0.9.6**: Use ``TUri`` instead.
|
||||
return string(url)
|
||||
@@ -172,7 +174,7 @@ proc combine*(base: TUri, reference: TUri): TUri =
|
||||
##
|
||||
## Examples:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block::
|
||||
## let foo = combine(parseUri("http://example.com/foo/bar"), parseUri("/baz"))
|
||||
## assert foo.path == "/baz"
|
||||
##
|
||||
@@ -229,7 +231,7 @@ proc `/`*(x: TUri, path: string): TUri =
|
||||
##
|
||||
## Examples:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block::
|
||||
## let foo = parseUri("http://example.com/foo/bar") / parseUri("/baz")
|
||||
## assert foo.path == "/foo/bar/baz"
|
||||
##
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2010 Dominik Picheta
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -9,7 +9,8 @@
|
||||
|
||||
|
||||
import strutils
|
||||
## This module implements XML DOM Level 2 Core specification(http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html)
|
||||
## This module implements XML DOM Level 2 Core
|
||||
## specification (http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html)
|
||||
|
||||
|
||||
#http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2010 Dominik Picheta
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -156,7 +156,7 @@ proc loadXMLFile*(path: string): PDocument =
|
||||
|
||||
|
||||
when isMainModule:
|
||||
var xml = loadXMLFile(r"C:\Users\Dominik\Desktop\Code\Nimrod\xmldom\test.xml")
|
||||
var xml = loadXMLFile("nim/xmldom/test.xml")
|
||||
#echo(xml.getElementsByTagName("m:test2")[0].namespaceURI)
|
||||
#echo(xml.getElementsByTagName("bla:test")[0].namespaceURI)
|
||||
#echo(xml.getElementsByTagName("test")[0].namespaceURI)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2010 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -12,9 +12,11 @@
|
||||
import streams, parsexml, strtabs, xmltree
|
||||
|
||||
type
|
||||
EInvalidXml* = object of EInvalidValue ## exception that is raised
|
||||
## for invalid XML
|
||||
errors*: seq[string] ## all detected parsing errors
|
||||
XmlError* = object of ValueError ## exception that is raised
|
||||
## for invalid XML
|
||||
errors*: seq[string] ## all detected parsing errors
|
||||
|
||||
{.deprecated: [EInvalidXml: XmlError].}
|
||||
|
||||
proc raiseInvalidXml(errors: seq[string]) =
|
||||
var e: ref EInvalidXml
|
||||
@@ -23,12 +25,12 @@ proc raiseInvalidXml(errors: seq[string]) =
|
||||
e.errors = errors
|
||||
raise e
|
||||
|
||||
proc addNode(father, son: PXmlNode) =
|
||||
proc addNode(father, son: XmlNode) =
|
||||
if son != nil: add(father, son)
|
||||
|
||||
proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode
|
||||
proc parse(x: var XmlParser, errors: var seq[string]): XmlNode
|
||||
|
||||
proc untilElementEnd(x: var TXmlParser, result: PXmlNode,
|
||||
proc untilElementEnd(x: var XmlParser, result: XmlNode,
|
||||
errors: var seq[string]) =
|
||||
while true:
|
||||
case x.kind
|
||||
@@ -45,7 +47,7 @@ proc untilElementEnd(x: var TXmlParser, result: PXmlNode,
|
||||
else:
|
||||
result.addNode(parse(x, errors))
|
||||
|
||||
proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode =
|
||||
proc parse(x: var XmlParser, errors: var seq[string]): XmlNode =
|
||||
case x.kind
|
||||
of xmlComment:
|
||||
result = newComment(x.charData)
|
||||
@@ -98,11 +100,11 @@ proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode =
|
||||
next(x)
|
||||
of xmlEof: discard
|
||||
|
||||
proc parseXml*(s: PStream, filename: string,
|
||||
errors: var seq[string]): PXmlNode =
|
||||
proc parseXml*(s: Stream, filename: string,
|
||||
errors: var seq[string]): XmlNode =
|
||||
## parses the XML from stream `s` and returns a ``PXmlNode``. Every
|
||||
## occured parsing error is added to the `errors` sequence.
|
||||
var x: TXmlParser
|
||||
var x: XmlParser
|
||||
open(x, s, filename, {reportComments})
|
||||
while true:
|
||||
x.next()
|
||||
@@ -118,14 +120,14 @@ proc parseXml*(s: PStream, filename: string,
|
||||
break
|
||||
close(x)
|
||||
|
||||
proc parseXml*(s: PStream): PXmlNode =
|
||||
proc parseXml*(s: Stream): XmlNode =
|
||||
## parses the XTML from stream `s` and returns a ``PXmlNode``. All parsing
|
||||
## errors are turned into an ``EInvalidXML`` exception.
|
||||
var errors: seq[string] = @[]
|
||||
result = parseXml(s, "unknown_html_doc", errors)
|
||||
if errors.len > 0: raiseInvalidXMl(errors)
|
||||
|
||||
proc loadXml*(path: string, errors: var seq[string]): PXmlNode =
|
||||
proc loadXml*(path: string, errors: var seq[string]): XmlNode =
|
||||
## Loads and parses XML from file specified by ``path``, and returns
|
||||
## a ``PXmlNode``. Every occured parsing error is added to the `errors`
|
||||
## sequence.
|
||||
@@ -133,7 +135,7 @@ proc loadXml*(path: string, errors: var seq[string]): PXmlNode =
|
||||
if s == nil: raise newException(EIO, "Unable to read file: " & path)
|
||||
result = parseXml(s, path, errors)
|
||||
|
||||
proc loadXml*(path: string): PXmlNode =
|
||||
proc loadXml*(path: string): XmlNode =
|
||||
## Loads and parses XML from file specified by ``path``, and returns
|
||||
## a ``PXmlNode``. All parsing errors are turned into an ``EInvalidXML``
|
||||
## exception.
|
||||
@@ -148,7 +150,7 @@ when isMainModule:
|
||||
var x = loadXml(paramStr(1), errors)
|
||||
for e in items(errors): echo e
|
||||
|
||||
var f: TFile
|
||||
var f: File
|
||||
if open(f, "xmltest.txt", fmWrite):
|
||||
f.write($x)
|
||||
f.close()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
@@ -12,33 +12,36 @@
|
||||
import macros, strtabs
|
||||
|
||||
type
|
||||
PXmlNode* = ref TXmlNode ## an XML tree consists of ``PXmlNode``'s.
|
||||
XmlNode* = ref TXmlNode ## an XML tree consists of ``PXmlNode``'s.
|
||||
|
||||
TXmlNodeKind* = enum ## different kinds of ``PXmlNode``'s
|
||||
XmlNodeKind* = enum ## different kinds of ``PXmlNode``'s
|
||||
xnText, ## a text element
|
||||
xnElement, ## an element with 0 or more children
|
||||
xnCData, ## a CDATA node
|
||||
xnEntity, ## an entity (like ``&thing;``)
|
||||
xnComment ## an XML comment
|
||||
|
||||
PXmlAttributes* = PStringTable ## an alias for a string to string mapping
|
||||
XmlAttributes* = StringTableRef ## an alias for a string to string mapping
|
||||
|
||||
TXmlNode {.pure, final, acyclic.} = object
|
||||
case k: TXmlNodeKind # private, use the kind() proc to read this field.
|
||||
XmlNodeObj {.acyclic.} = object
|
||||
case k: XmlNodeKind # private, use the kind() proc to read this field.
|
||||
of xnText, xnComment, xnCData, xnEntity:
|
||||
fText: string
|
||||
of xnElement:
|
||||
fTag: string
|
||||
s: seq[PXmlNode]
|
||||
fAttr: PXmlAttributes
|
||||
s: seq[XmlNode]
|
||||
fAttr: XmlAttributes
|
||||
fClientData: int ## for other clients
|
||||
|
||||
proc newXmlNode(kind: TXmlNodeKind): PXmlNode =
|
||||
## creates a new ``PXmlNode``.
|
||||
|
||||
{.deprecated: [PXmlNode: XmlNode, TXmlNodeKind: XmlNodeKind, PXmlAttributes:
|
||||
XmlAttributes, TXmlNode: XmlNodeObj].}
|
||||
|
||||
proc newXmlNode(kind: XmlNodeKind): XmlNode =
|
||||
## creates a new ``XmlNode``.
|
||||
new(result)
|
||||
result.k = kind
|
||||
|
||||
proc newElement*(tag: string): PXmlNode =
|
||||
proc newElement*(tag: string): XmlNode =
|
||||
## creates a new ``PXmlNode`` of kind ``xnText`` with the given `tag`.
|
||||
result = newXmlNode(xnElement)
|
||||
result.fTag = tag
|
||||
@@ -304,10 +307,10 @@ proc findAll*(n: PXmlNode, tag: string, result: var seq[PXmlNode]) =
|
||||
## Found nodes satisfying the condition will be appended to the `result`
|
||||
## sequence, which can't be nil or the proc will crash. Usage example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## .. code-block::
|
||||
## var
|
||||
## html: PXmlNode
|
||||
## tags: seq[PXmlNode] = @[]
|
||||
## html: XmlNode
|
||||
## tags: seq[XmlNode] = @[]
|
||||
##
|
||||
## html = buildHtml()
|
||||
## findAll(html, "img", tags)
|
||||
@@ -326,8 +329,8 @@ proc findAll*(n: PXmlNode, tag: string, result: var seq[PXmlNode]) =
|
||||
proc findAll*(n: PXmlNode, tag: string): seq[PXmlNode] =
|
||||
## Shortcut version to assign in let blocks. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## var html: PXmlNode
|
||||
## .. code-block::
|
||||
## var html: XmlNode
|
||||
##
|
||||
## html = buildHtml(html)
|
||||
## for imgTag in html.findAll("img"):
|
||||
|
||||
@@ -2437,6 +2437,8 @@ when not defined(JS): #and not defined(NimrodVM):
|
||||
## returns the OS file handle of the file ``f``. This is only useful for
|
||||
## platform specific programming.
|
||||
|
||||
{.deprecated: [fileHandle: getFileHandle].}
|
||||
|
||||
proc cstringArrayToSeq*(a: cstringArray, len: int): seq[string] =
|
||||
## converts a ``cstringArray`` to a ``seq[string]``. `a` is supposed to be
|
||||
## of length ``len``.
|
||||
|
||||
1
todo.txt
1
todo.txt
@@ -49,7 +49,6 @@ Bugs
|
||||
- bug: type conversions concerning proc types are weird
|
||||
- compilation of niminst takes way too long. looks like a regression
|
||||
- docgen: sometimes effects are listed twice
|
||||
- 'result' is not properly cleaned for NRVO --> use uninit checking instead
|
||||
- blocks can "export" an identifier but the CCG generates {} for them ...
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user