mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-19 22:10:33 +00:00
Merge branch 'devel' into faster-nimsuggest
This commit is contained in:
@@ -460,6 +460,8 @@ type
|
||||
# proc foo(T: typedesc, list: seq[T]): var T
|
||||
# proc foo(L: static[int]): array[L, int]
|
||||
# can be attached to ranges to indicate that the range
|
||||
# can be attached to generic procs with free standing
|
||||
# type parameters: e.g. proc foo[T]()
|
||||
# depends on unresolved static params.
|
||||
tfRetType, # marks return types in proc (used to detect type classes
|
||||
# used as return types for return type inference)
|
||||
|
||||
@@ -67,6 +67,23 @@ proc debug*(n: PSym) {.deprecated.}
|
||||
proc debug*(n: PType) {.deprecated.}
|
||||
proc debug*(n: PNode) {.deprecated.}
|
||||
|
||||
template mdbg*: bool {.dirty.} =
|
||||
when compiles(c.module):
|
||||
c.module.fileIdx == gProjectMainIdx
|
||||
elif compiles(m.c.module):
|
||||
m.c.module.fileIdx == gProjectMainIdx
|
||||
elif compiles(cl.c.module):
|
||||
cl.c.module.fileIdx == gProjectMainIdx
|
||||
elif compiles(p):
|
||||
when compiles(p.lex):
|
||||
p.lex.fileIdx == gProjectMainIdx
|
||||
else:
|
||||
p.module.module.fileIdx == gProjectMainIdx
|
||||
elif compiles(L.fileIdx):
|
||||
L.fileIdx == gProjectMainIdx
|
||||
else:
|
||||
false
|
||||
|
||||
# --------------------------- ident tables ----------------------------------
|
||||
proc idTableGet*(t: TIdTable, key: PIdObj): RootRef
|
||||
proc idTableGet*(t: TIdTable, key: int): RootRef
|
||||
|
||||
@@ -80,9 +80,14 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode =
|
||||
expectedRegularParams = <s.typ.len
|
||||
givenRegularParams = totalParams - genericParams
|
||||
if givenRegularParams < 0: givenRegularParams = 0
|
||||
|
||||
if totalParams > expectedRegularParams + genericParams:
|
||||
globalError(n.info, errWrongNumberOfArguments)
|
||||
|
||||
if totalParams < genericParams:
|
||||
globalError(n.info, errMissingGenericParamsForTemplate,
|
||||
n.renderTree)
|
||||
|
||||
result = newNodeI(nkArgList, n.info)
|
||||
for i in 1 .. givenRegularParams:
|
||||
result.addSon n.sons[i]
|
||||
|
||||
@@ -1080,7 +1080,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
|
||||
inc(L.bufpos)
|
||||
of '.':
|
||||
when defined(nimsuggest):
|
||||
if L.fileIdx == gTrackPos.fileIndex and tok.col+1 == gTrackPos.col and
|
||||
if L.fileIdx == gTrackPos.fileIndex and tok.col == gTrackPos.col and
|
||||
tok.line == gTrackPos.line and gIdeCmd == ideSug:
|
||||
tok.tokType = tkDot
|
||||
L.cursor = CursorPosition.InToken
|
||||
|
||||
@@ -64,6 +64,8 @@ type
|
||||
errVarForOutParamNeeded,
|
||||
errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX,
|
||||
errAmbiguousCallXYZ, errWrongNumberOfArguments,
|
||||
errWrongNumberOfArgumentsInCall,
|
||||
errMissingGenericParamsForTemplate,
|
||||
errXCannotBePassedToProcVar,
|
||||
errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProcX, errImplOfXNotAllowed,
|
||||
errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX,
|
||||
@@ -89,6 +91,7 @@ type
|
||||
errMainModuleMustBeSpecified,
|
||||
errXExpected,
|
||||
errTIsNotAConcreteType,
|
||||
errCastToANonConcreteType,
|
||||
errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,
|
||||
errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
|
||||
errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly,
|
||||
@@ -107,6 +110,7 @@ type
|
||||
errCannotInferTypeOfTheLiteral,
|
||||
errCannotInferReturnType,
|
||||
errGenericLambdaNotAllowed,
|
||||
errProcHasNoConcreteType,
|
||||
errCompilerDoesntSupportTarget,
|
||||
errUser,
|
||||
warnCannotOpenFile,
|
||||
@@ -269,6 +273,8 @@ const
|
||||
errButExpectedX: "but expected \'$1\'",
|
||||
errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3",
|
||||
errWrongNumberOfArguments: "wrong number of arguments",
|
||||
errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'",
|
||||
errMissingGenericParamsForTemplate: "'$1' has unspecified generic parameters",
|
||||
errXCannotBePassedToProcVar: "\'$1\' cannot be passed to a procvar",
|
||||
errXCannotBeInParamDecl: "$1 cannot be declared in parameter declaration",
|
||||
errPragmaOnlyInHeaderOfProcX: "pragmas are only allowed in the header of a proc; redefinition of $1",
|
||||
@@ -326,6 +332,7 @@ const
|
||||
errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file",
|
||||
errXExpected: "\'$1\' expected",
|
||||
errTIsNotAConcreteType: "\'$1\' is not a concrete type.",
|
||||
errCastToANonConcreteType: "cannot cast to a non concrete type: \'$1\'",
|
||||
errInvalidSectionStart: "invalid section start",
|
||||
errGridTableNotImplemented: "grid table is not implemented",
|
||||
errGeneralParseError: "general parse error",
|
||||
@@ -369,6 +376,7 @@ const
|
||||
errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " &
|
||||
"it is used as an operand to another routine and the types " &
|
||||
"of the generic paramers can be inferred from the expected signature.",
|
||||
errProcHasNoConcreteType: "'$1' doesn't have a concrete type, due to unspecified generic parameters.",
|
||||
errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target",
|
||||
errUser: "$1",
|
||||
warnCannotOpenFile: "cannot open \'$1\'",
|
||||
|
||||
@@ -420,11 +420,6 @@ proc binaryStrSearch*(x: openArray[string], y: string): int =
|
||||
return mid
|
||||
result = - 1
|
||||
|
||||
template nimdbg*: untyped = c.module.fileIdx == gProjectMainIdx
|
||||
template cnimdbg*: untyped = p.module.module.fileIdx == gProjectMainIdx
|
||||
template pnimdbg*: untyped = p.lex.fileIdx == gProjectMainIdx
|
||||
template lnimdbg*: untyped = L.fileIdx == gProjectMainIdx
|
||||
|
||||
proc parseIdeCmd*(s: string): IdeCmd =
|
||||
case s:
|
||||
of "sug": ideSug
|
||||
|
||||
@@ -385,6 +385,13 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
|
||||
if sym == c.p.owner:
|
||||
globalError(n.info, errRecursiveDependencyX, sym.name.s)
|
||||
|
||||
let genericParams = if sfImmediate in sym.flags: 0
|
||||
else: sym.ast[genericParamsPos].len
|
||||
let suppliedParams = max(n.safeLen - 1, 0)
|
||||
|
||||
if suppliedParams < genericParams:
|
||||
globalError(n.info, errMissingGenericParamsForTemplate, n.renderTree)
|
||||
|
||||
#if c.evalContext == nil:
|
||||
# c.evalContext = c.createEvalContext(emStatic)
|
||||
result = evalMacroCall(c.module, c.cache, n, nOrig, sym)
|
||||
|
||||
@@ -411,6 +411,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
|
||||
let tm = typeRel(m, formal, arg, true)
|
||||
if tm in {isNone, isConvertible}: return nil
|
||||
var newInst = generateInstance(c, s, m.bindings, n.info)
|
||||
newInst.typ.flags.excl tfUnresolved
|
||||
markUsed(n.info, s, c.graph.usageSym)
|
||||
styleCheckUse(n.info, s)
|
||||
result = newSymNode(newInst, n.info)
|
||||
|
||||
@@ -30,6 +30,8 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
# result = errorNode(c, n)
|
||||
if result.typ != nil:
|
||||
# XXX tyGenericInst here?
|
||||
if result.typ.kind == tyProc and tfUnresolved in result.typ.flags:
|
||||
localError(n.info, errProcHasNoConcreteType, n.renderTree)
|
||||
if result.typ.kind == tyVar: result = newDeref(result)
|
||||
elif {efWantStmt, efAllowStmt} * flags != {}:
|
||||
result.typ = newTypeS(tyVoid, c)
|
||||
@@ -218,13 +220,16 @@ proc semConv(c: PContext, n: PNode): PNode =
|
||||
proc semCast(c: PContext, n: PNode): PNode =
|
||||
## Semantically analyze a casting ("cast[type](param)")
|
||||
checkSonsLen(n, 2)
|
||||
let targetType = semTypeNode(c, n.sons[0], nil)
|
||||
let castedExpr = semExprWithType(c, n.sons[1])
|
||||
if tfHasMeta in targetType.flags:
|
||||
localError(n.sons[0].info, errCastToANonConcreteType, $targetType)
|
||||
if not isCastable(targetType, castedExpr.typ):
|
||||
localError(n.info, errExprCannotBeCastToX, $targetType)
|
||||
result = newNodeI(nkCast, n.info)
|
||||
result.typ = semTypeNode(c, n.sons[0], nil)
|
||||
result.typ = targetType
|
||||
addSon(result, copyTree(n.sons[0]))
|
||||
addSon(result, semExprWithType(c, n.sons[1]))
|
||||
if not isCastable(result.typ, result.sons[1].typ):
|
||||
localError(result.info, errExprCannotBeCastToX,
|
||||
typeToString(result.typ))
|
||||
addSon(result, castedExpr)
|
||||
|
||||
proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
|
||||
const
|
||||
|
||||
@@ -503,6 +503,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
if hasEmpty(typ):
|
||||
localError(def.info, errCannotInferTypeOfTheLiteral,
|
||||
($typ.kind).substr(2).toLowerAscii)
|
||||
elif typ.kind == tyProc and tfUnresolved in typ.flags:
|
||||
localError(def.info, errProcHasNoConcreteType, def.renderTree)
|
||||
else:
|
||||
if symkind == skLet: localError(a.info, errLetNeedsInit)
|
||||
|
||||
|
||||
@@ -1009,8 +1009,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
result.sons[0] = r
|
||||
result.n.typ = r
|
||||
|
||||
if genericParams != nil:
|
||||
if genericParams != nil and genericParams.len > 0:
|
||||
for n in genericParams:
|
||||
if {sfUsed, sfAnon} * n.sym.flags == {}:
|
||||
result.flags.incl tfUnresolved
|
||||
|
||||
if tfWildcard in n.sym.typ.flags:
|
||||
n.sym.kind = skType
|
||||
n.sym.typ.flags.excl tfWildcard
|
||||
|
||||
@@ -1514,6 +1514,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
|
||||
if arg.sons[i].sym.kind in {skProc, skMethod, skConverter, skIterator}:
|
||||
copyCandidate(z, m)
|
||||
z.callee = arg.sons[i].typ
|
||||
if tfUnresolved in z.callee.flags: continue
|
||||
z.calleeSym = arg.sons[i].sym
|
||||
#if arg.sons[i].sym.name.s == "cmp":
|
||||
# ggDebug = true
|
||||
@@ -1650,7 +1651,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||
if a >= formalLen-1 and formal != nil and formal.typ.isVarargsUntyped:
|
||||
incl(marker, formal.position)
|
||||
if container.isNil:
|
||||
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, n.info))
|
||||
container = newNodeIT(nkArgList, n.sons[a].info, arrayConstr(c, n.info))
|
||||
setSon(m.call, formal.position + 1, container)
|
||||
else:
|
||||
incrIndexType(container.typ)
|
||||
@@ -1738,7 +1739,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||
|
||||
if formal.typ.isVarargsUntyped:
|
||||
if container.isNil:
|
||||
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, n.info))
|
||||
container = newNodeIT(nkArgList, n.sons[a].info, arrayConstr(c, n.info))
|
||||
setSon(m.call, formal.position + 1, container)
|
||||
else:
|
||||
incrIndexType(container.typ)
|
||||
|
||||
@@ -20,6 +20,7 @@ type
|
||||
preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg
|
||||
|
||||
proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string
|
||||
template `$`*(typ: PType): string = typeToString(typ)
|
||||
|
||||
proc base*(t: PType): PType =
|
||||
result = t.sons[0]
|
||||
@@ -547,7 +548,9 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||
if prefer != preferExported:
|
||||
result.add("(" & typeToString(t.sons[0]) & ")")
|
||||
of tyProc:
|
||||
result = if tfIterator in t.flags: "iterator (" else: "proc ("
|
||||
result = if tfIterator in t.flags: "iterator " else: "proc "
|
||||
if tfUnresolved in t.flags: result.add "[*missing parameters*]"
|
||||
result.add "("
|
||||
for i in countup(1, sonsLen(t) - 1):
|
||||
if t.n != nil and i < t.n.len and t.n[i].kind == nkSym:
|
||||
add(result, t.n[i].sym.name.s)
|
||||
|
||||
174
doc/tut1.rst
174
doc/tut1.rst
@@ -393,7 +393,7 @@ Since counting up occurs so often in programs, Nim also has a `..
|
||||
for i in 1..10:
|
||||
...
|
||||
|
||||
Zero-indexed counting have two shortcuts ``..<`` and ``..^`` to simplify counting to one less then the higher index:
|
||||
Zero-indexed counting have two shortcuts ``..<`` and ``..^`` to simplify counting to one less than the higher index:
|
||||
|
||||
.. code-block:: nim
|
||||
for i in 0..<10:
|
||||
@@ -411,8 +411,8 @@ Other useful iterators for collections (like arrays and sequences) are
|
||||
* ``pairs`` and ``mpairs`` which provides the element and an index number (immutable and mutable respectively)
|
||||
|
||||
.. code-block:: nim
|
||||
for indx, itm in ["a","b"].pairs:
|
||||
echo itm, " at index ", indx
|
||||
for index, item in ["a","b"].pairs:
|
||||
echo item, " at index ", index
|
||||
# => a at index 0
|
||||
# => b at index 1
|
||||
|
||||
@@ -489,10 +489,10 @@ Example:
|
||||
else:
|
||||
echo "unknown operating system"
|
||||
|
||||
The ``when`` statement is almost identical to the ``if`` statement with some
|
||||
The ``when`` statement is almost identical to the ``if`` statement, but with these
|
||||
differences:
|
||||
|
||||
* Each condition has to be a constant expression since it is evaluated by the
|
||||
* Each condition must be a constant expression since it is evaluated by the
|
||||
compiler.
|
||||
* The statements within a branch do not open a new scope.
|
||||
* The compiler checks the semantics and produces code *only* for the statements
|
||||
@@ -516,8 +516,8 @@ 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:
|
||||
contain other statements. To avoid ambiguities, complex statements must always
|
||||
be indented, but single simple statements do not:
|
||||
|
||||
.. code-block:: nim
|
||||
# no indentation needed for single assignment statement:
|
||||
@@ -586,9 +586,9 @@ false if they answered "no" (or something similar). A ``return`` statement
|
||||
leaves the procedure (and therefore the while loop) immediately. The
|
||||
``(question: string): bool`` syntax describes that the procedure expects a
|
||||
parameter named ``question`` of type ``string`` and returns a value of type
|
||||
``bool``. ``Bool`` is a built-in type: the only valid values for ``bool`` are
|
||||
``bool``. The ``bool`` type is built-in: the only valid values for ``bool`` are
|
||||
``true`` and ``false``.
|
||||
The conditions in if or while statements should be of the type ``bool``.
|
||||
The conditions in if or while statements must be of type ``bool``.
|
||||
|
||||
Some terminology: in the example ``question`` is called a (formal) *parameter*,
|
||||
``"Should I..."`` is called an *argument* that is passed to this parameter.
|
||||
@@ -658,8 +658,8 @@ 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. Nim does not
|
||||
allow to silently throw away a return value:
|
||||
its return value, a ``discard`` statement **must** be used. Nim does not
|
||||
allow silently throwing away a return value:
|
||||
|
||||
.. code-block:: nim
|
||||
discard yes("May I ask a pointless question?")
|
||||
@@ -708,7 +708,7 @@ The compiler checks that each parameter receives exactly one argument.
|
||||
Default values
|
||||
--------------
|
||||
To make the ``createWindow`` proc easier to use it should provide `default
|
||||
values`, these are values that are used as arguments if the caller does not
|
||||
values`; these are values that are used as arguments if the caller does not
|
||||
specify them:
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -750,19 +750,19 @@ algorithm. Ambiguous calls are reported as errors.
|
||||
Operators
|
||||
---------
|
||||
The Nim library makes heavy use of overloading - one reason for this is that
|
||||
each operator like ``+`` is a just an overloaded proc. The parser lets you
|
||||
each operator like ``+`` is 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
|
||||
(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 Nim.
|
||||
``(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:
|
||||
``+ - * \ / < > = @ $ ~ & % ! ? ^ . |``
|
||||
|
||||
User defined operators are allowed. Nothing stops you from defining your own
|
||||
``@!?+~`` operator, but readability can suffer.
|
||||
``@!?+~`` operator, but doing so may reduce readability.
|
||||
|
||||
The operator's precedence is determined by its first character. The details
|
||||
can be found in the manual.
|
||||
@@ -785,7 +785,7 @@ Forward declarations
|
||||
--------------------
|
||||
|
||||
Every variable, procedure, etc. needs to be declared before it can be used.
|
||||
(The reason for this is that it is non-trivial to do better than that in a
|
||||
(The reason for this is that it is non-trivial to avoid this need in a
|
||||
language that supports meta programming as extensively as Nim does.)
|
||||
However, this cannot be done for mutually recursive procedures:
|
||||
|
||||
@@ -822,7 +822,7 @@ whose value is then returned implicitly.
|
||||
Iterators
|
||||
=========
|
||||
|
||||
Let's return to the boring counting example:
|
||||
Let's return to the simple counting example:
|
||||
|
||||
.. code-block:: nim
|
||||
echo "Counting to ten: "
|
||||
@@ -843,7 +843,7 @@ However, this does not work. The problem is that the procedure should not
|
||||
only ``return``, but return and **continue** after an iteration has
|
||||
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:
|
||||
and here it is - our first iterator:
|
||||
|
||||
.. code-block:: nim
|
||||
iterator countup(a, b: int): int =
|
||||
@@ -856,8 +856,8 @@ Iterators look very similar to procedures, but there are several
|
||||
important differences:
|
||||
|
||||
* Iterators can only be called from for loops.
|
||||
* Iterators cannot contain a ``return`` statement and procs cannot contain a
|
||||
``yield`` statement.
|
||||
* Iterators cannot contain a ``return`` statement (and procs cannot contain a
|
||||
``yield`` statement).
|
||||
* Iterators have no implicit ``result`` variable.
|
||||
* Iterators do not support recursion.
|
||||
* Iterators cannot be forward declared, because the compiler must be able
|
||||
@@ -866,8 +866,8 @@ important differences:
|
||||
|
||||
However, you can also use a ``closure`` iterator to get a different set of
|
||||
restrictions. See `first class iterators <manual.html#first-class-iterators>`_
|
||||
for details. Iterators can have the same name and parameters as a proc,
|
||||
essentially they have their own namespace. Therefore it is common practice to
|
||||
for details. Iterators can have the same name and parameters as a proc, since
|
||||
essentially they have their own namespaces. Therefore it is common practice to
|
||||
wrap iterators in procs of the same name which accumulate the result of the
|
||||
iterator and return it as a sequence, like ``split`` from the `strutils module
|
||||
<strutils.html>`_.
|
||||
@@ -882,13 +882,13 @@ that are available for them in detail.
|
||||
Booleans
|
||||
--------
|
||||
|
||||
The boolean type is named ``bool`` in Nim and consists of the two
|
||||
Nim's boolean type is called ``bool`` and consists of the two
|
||||
pre-defined values ``true`` and ``false``. Conditions in while,
|
||||
if, elif, when statements need to be of type bool.
|
||||
if, elif, and when statements must be of type bool.
|
||||
|
||||
The operators ``not, and, or, xor, <, <=, >, >=, !=, ==`` are defined
|
||||
for the bool type. The ``and`` and ``or`` operators perform short-cut
|
||||
evaluation. Example:
|
||||
for the bool type. The ``and`` and ``or`` operators perform short-circuit
|
||||
evaluation. For example:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
@@ -899,8 +899,8 @@ evaluation. Example:
|
||||
|
||||
Characters
|
||||
----------
|
||||
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 `character type` is called ``char``. Its size is always one byte, so
|
||||
it cannot represent most UTF-8 characters; but it *can* represent one of the bytes that makes up a multi-byte UTF-8 character.
|
||||
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
|
||||
designed for this.
|
||||
@@ -914,11 +914,11 @@ Converting from an integer to a ``char`` is done with the ``chr`` proc.
|
||||
|
||||
Strings
|
||||
-------
|
||||
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``
|
||||
String variables are **mutable**, so appending to a string
|
||||
is possible, and quite efficient. Strings in Nim are both zero-terminated and have a
|
||||
length field. A string's length can be retrieved 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:
|
||||
terminating zero is not an error and often leads to simpler code:
|
||||
|
||||
.. code-block:: nim
|
||||
if s[i] == 'a' and s[i+1] == 'b':
|
||||
@@ -928,15 +928,15 @@ terminating zero is no error and often leads to simpler code:
|
||||
The assignment operator for strings copies the string. You can use the ``&``
|
||||
operator to concatenate strings and ``add`` to append to a string.
|
||||
|
||||
Strings are compared by their lexicographical order. All comparison operators
|
||||
are available. Per convention, all strings are UTF-8 strings, but this is not
|
||||
Strings are compared using their lexicographical order. All the comparison operators
|
||||
are supported. By convention, all strings are UTF-8 encoded, but this is not
|
||||
enforced. For example, when reading strings from binary files, they are merely
|
||||
a sequence of bytes. The index operation ``s[i]`` means the i-th *char* of
|
||||
``s``, not the i-th *unichar*.
|
||||
|
||||
String variables are initialized with a special value, called ``nil``. However,
|
||||
most string operations cannot deal with ``nil`` (leading to an exception being
|
||||
raised) for performance reasons. One should use empty strings ``""``
|
||||
raised) for performance reasons. It is best to use empty strings ``""``
|
||||
rather than ``nil`` as the *empty* value. But ``""`` often creates a string
|
||||
object on the heap, so there is a trade-off to be made here.
|
||||
|
||||
@@ -947,7 +947,7 @@ 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:
|
||||
to specify a non-default integer type:
|
||||
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -961,18 +961,18 @@ Most often integers are used for counting objects that reside in memory, so
|
||||
``int`` has the same size as a pointer.
|
||||
|
||||
The common operators ``+ - * div mod < <= == != > >=`` are defined for
|
||||
integers. The ``and or xor not`` operators are defined for integers too and
|
||||
integers. The ``and or xor not`` operators are also defined for integers, and
|
||||
provide *bitwise* operations. Left bit shifting is done with the ``shl``, right
|
||||
shifting with the ``shr`` operator. Bit shifting operators always treat their
|
||||
arguments as *unsigned*. For `arithmetic bit shifts`:idx: ordinary
|
||||
multiplication or division can be used.
|
||||
|
||||
Unsigned operations all wrap around; they cannot lead to over- or underflow
|
||||
Unsigned operations all wrap around; they cannot lead to over- or under-flow
|
||||
errors.
|
||||
|
||||
`Automatic type conversion`:idx: is performed in expressions where different
|
||||
Lossless `Automatic type conversion`:idx: is performed in expressions where different
|
||||
kinds of integer types are used. However, if the type conversion
|
||||
loses information, the `EOutOfRange`:idx: exception is raised (if the error
|
||||
would cause loss of information, the `EOutOfRange`:idx: exception is raised (if the error
|
||||
cannot be detected at compile time).
|
||||
|
||||
|
||||
@@ -981,9 +981,9 @@ Floats
|
||||
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.
|
||||
``float`` is always 64-bits.
|
||||
|
||||
Float literals can have a *type suffix* to mark them to be of another float
|
||||
Float literals can have a *type suffix* to specify a non-default float
|
||||
type:
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -993,18 +993,18 @@ type:
|
||||
z = 0.0'f64 # z is of type ``float64``
|
||||
|
||||
The common operators ``+ - * / < <= == != > >=`` are defined for
|
||||
floats and follow the IEEE standard.
|
||||
floats and follow the IEEE-754 standard.
|
||||
|
||||
Automatic type conversion in expressions with different kinds of floating
|
||||
point types is performed: the smaller type is converted to the larger. Integer
|
||||
types are **not** converted to floating point types automatically and vice
|
||||
versa. The `toInt <system.html#toInt>`_ and `toFloat <system.html#toFloat>`_
|
||||
procs can be used for these conversions.
|
||||
types are **not** converted to floating point types automatically, nor vice
|
||||
versa. Use the `toInt <system.html#toInt>`_ and `toFloat <system.html#toFloat>`_
|
||||
procs for these conversions.
|
||||
|
||||
|
||||
Type Conversion
|
||||
---------------
|
||||
Conversion between basic types in nim is performed by using the
|
||||
Conversion between basic types is performed by using the
|
||||
type as a function:
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -1019,9 +1019,9 @@ Internal type representation
|
||||
============================
|
||||
|
||||
As mentioned earlier, the built-in `$ <system.html#$>`_ (stringify) operator
|
||||
turns any basic type into a string, which you can then print to the screen
|
||||
with the ``echo`` proc. However, advanced types, or types you may define
|
||||
yourself won't work with the ``$`` operator until you define one for them.
|
||||
turns any basic type into a string, which you can then print to the console
|
||||
using the ``echo`` proc. However, advanced types, and your own custom types,
|
||||
won't work with the ``$`` operator until you define it for them.
|
||||
Sometimes you just want to debug the current value of a complex type without
|
||||
having to write its ``$`` operator. You can use then the `repr
|
||||
<system.html#repr>`_ proc which works with any type and even complex data
|
||||
@@ -1057,16 +1057,16 @@ In Nim new types can be defined within a ``type`` statement:
|
||||
biggestInt = int64 # biggest integer type that is available
|
||||
biggestFloat = float64 # biggest float type that is available
|
||||
|
||||
Enumeration and object types cannot be defined on the fly, but only within a
|
||||
Enumeration and object types may only be defined within a
|
||||
``type`` statement.
|
||||
|
||||
|
||||
Enumerations
|
||||
------------
|
||||
A variable of an enumeration type can only be assigned a value of a
|
||||
limited set. This set consists of ordered symbols. Each symbol is mapped
|
||||
A variable of an enumeration type can only be assigned one of the enumeration's specified values.
|
||||
These values are a set 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:
|
||||
at runtime by 0, the second by 1 and so on. For example:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
@@ -1077,17 +1077,17 @@ at runtime by 0, the second by 1 and so on. Example:
|
||||
var x = south # `x` is of type `Direction`; its value is `south`
|
||||
echo x # writes "south" to `stdout`
|
||||
|
||||
All comparison operators can be used with enumeration types.
|
||||
All the comparison operators can be used with enumeration types.
|
||||
|
||||
An enumeration's symbol can be qualified to avoid ambiguities:
|
||||
``Direction.south``.
|
||||
|
||||
The ``$`` operator can convert any enumeration value to its name, the ``ord``
|
||||
proc to its underlying integer value.
|
||||
The ``$`` operator can convert any enumeration value to its name, and the ``ord``
|
||||
proc can convert it to its underlying integer value.
|
||||
|
||||
For better interfacing to other programming languages, the symbols of enum
|
||||
types can be assigned an explicit ordinal value. However, the ordinal values
|
||||
have to be in ascending order. A symbol whose ordinal value is not
|
||||
must be in ascending order. A symbol whose ordinal value is not
|
||||
explicitly given is assigned the value of the previous symbol + 1.
|
||||
|
||||
An explicit ordered enum can have *holes*:
|
||||
@@ -1142,8 +1142,8 @@ subrange types (and vice versa) are allowed.
|
||||
|
||||
The ``system`` module defines the important `Natural <system.html#Natural>`_
|
||||
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
|
||||
maximal value). Other programming languages may suggest the use of unsigned
|
||||
integers for natural numbers. This is often **unwise**: you don't want unsigned
|
||||
arithmetic (which wraps around) just because the numbers cannot be negative.
|
||||
Nim's ``Natural`` type helps to avoid this common programming error.
|
||||
|
||||
@@ -1156,9 +1156,9 @@ Sets
|
||||
Arrays
|
||||
------
|
||||
An array is a simple fixed length container. Each element in
|
||||
the array has the same type. The array's index type can be any ordinal type.
|
||||
an array has the same type. The array's index type can be any ordinal type.
|
||||
|
||||
Arrays can be constructed via ``[]``:
|
||||
Arrays can be constructed using ``[]``:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
@@ -1222,7 +1222,7 @@ subdivided in height levels accessed through their integer index:
|
||||
#tower[0][1] = on
|
||||
|
||||
Note how the built-in ``len`` proc returns only the array's first dimension
|
||||
length. Another way of defining the ``LightTower`` to show better its
|
||||
length. Another way of defining the ``LightTower`` to better illustrate its
|
||||
nested nature would be to omit the previous definition of the ``LevelSetting``
|
||||
type and instead write it embedded directly as the type of the first dimension:
|
||||
|
||||
@@ -1230,7 +1230,7 @@ type and instead write it embedded directly as the type of the first dimension:
|
||||
type
|
||||
LightTower = array[1..10, array[north..west, BlinkLights]]
|
||||
|
||||
It is quite frequent to have arrays start at zero, so there's a shortcut syntax
|
||||
It is quite common 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:: nim
|
||||
@@ -1288,8 +1288,8 @@ value. Here the ``for`` statement is looping over the results from the
|
||||
<system.html>`_ module. Examples:
|
||||
|
||||
.. code-block:: nim
|
||||
for i in @[3, 4, 5]:
|
||||
echo i
|
||||
for value in @[3, 4, 5]:
|
||||
echo value
|
||||
# --> 3
|
||||
# --> 4
|
||||
# --> 5
|
||||
@@ -1320,7 +1320,7 @@ type does not matter.
|
||||
|
||||
fruits = @[] # creates an empty sequence on the heap that will be referenced by 'fruits'
|
||||
|
||||
capitals = ["New York", "London", "Berlin"] # array 'capitals' allows only assignment of three elements
|
||||
capitals = ["New York", "London", "Berlin"] # array 'capitals' allows assignment of only three elements
|
||||
fruits.add("Banana") # sequence 'fruits' is dynamically expandable during runtime
|
||||
fruits.add("Mango")
|
||||
|
||||
@@ -1406,7 +1406,7 @@ the same type and of the same name in the same order.
|
||||
|
||||
The assignment operator for tuples copies each component. The notation
|
||||
``t.field`` is used to access a tuple's field. Another notation is
|
||||
``t[i]`` to access the ``i``'th field. Here ``i`` needs to be a constant
|
||||
``t[i]`` to access the ``i``'th field. Here ``i`` must be a constant
|
||||
integer.
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -1449,10 +1449,10 @@ Tuples can be *unpacked* during variable assignment (and only then!). This can
|
||||
be handy to assign directly the fields of the tuples to individually named
|
||||
variables. An example of this is the `splitFile <os.html#splitFile>`_ proc
|
||||
from the `os module <os.html>`_ which returns the directory, name and
|
||||
extension of a path at the same time. For tuple unpacking to work you have to
|
||||
use parenthesis around the values you want to assign the unpacking to,
|
||||
extension of a path at the same time. For tuple unpacking to work you must
|
||||
use parentheses around the values you want to assign the unpacking to,
|
||||
otherwise you will be assigning the same value to all the individual
|
||||
variables! Example:
|
||||
variables! For example:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
@@ -1494,12 +1494,12 @@ point to and modify the same location in memory.
|
||||
|
||||
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
|
||||
objects in a garbage collected heap, untraced references point to
|
||||
manually allocated objects or to objects elsewhere in memory. Thus
|
||||
untraced references are *unsafe*. However for certain low-level operations
|
||||
(accessing the hardware) untraced references are unavoidable.
|
||||
(e.g., accessing the hardware), untraced references are necessary.
|
||||
|
||||
Traced references are declared with the **ref** keyword, untraced references
|
||||
Traced references are declared with the **ref** keyword; untraced references
|
||||
are declared with the **ptr** keyword.
|
||||
|
||||
The empty ``[]`` subscript notation can be used to *derefer* a reference,
|
||||
@@ -1520,10 +1520,10 @@ operators perform implicit dereferencing operations for reference types:
|
||||
n.data = 9
|
||||
# no need to write n[].data; in fact n[].data is highly discouraged!
|
||||
|
||||
To allocate a new traced object, the built-in procedure ``new`` has to be used.
|
||||
To allocate a new traced object, the built-in procedure ``new`` must be used.
|
||||
To deal with untraced memory, the procedures ``alloc``, ``dealloc`` and
|
||||
``realloc`` can be used. The documentation of the `system <system.html>`_
|
||||
module contains further information.
|
||||
``realloc`` can be used. The `system <system.html>`_
|
||||
module's documentation contains further details.
|
||||
|
||||
If a reference points to *nothing*, it has the value ``nil``.
|
||||
|
||||
@@ -1555,8 +1555,8 @@ listed in the `manual <manual.html#types-procedural-type>`_.
|
||||
|
||||
Distinct type
|
||||
-------------
|
||||
A Distinct type allows for the creation of new type that "does not imply a subtype relationship between it and its base type".
|
||||
You must EXPLICITLY define all behaviour for the distinct type.
|
||||
A Distinct type allows for the creation of new type that "does not imply a subtype relationship between it and its base type".
|
||||
You must **explicitly** define all behaviour for the distinct type.
|
||||
To help with this, both the distinct type and its base type can cast from one type to the other.
|
||||
Examples are provided in the `manual <manual.html#types-distinct-type>`_.
|
||||
|
||||
@@ -1564,8 +1564,8 @@ Modules
|
||||
=======
|
||||
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
|
||||
`separate compilation`:idx:. A module may gain access to the symbols of another
|
||||
module by using the `import`:idx: statement. Only top-level symbols that are marked
|
||||
with an asterisk (``*``) are exported:
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -1585,7 +1585,7 @@ with an asterisk (``*``) are exported:
|
||||
|
||||
The above module exports ``x`` and ``*``, but not ``y``.
|
||||
|
||||
The top-level statements of a module are executed at the start of the program.
|
||||
A module's top-level statements are executed at the start of the program.
|
||||
This can be used to initialize complex data structures for example.
|
||||
|
||||
Each module has a special magic constant ``isMainModule`` that is true if the
|
||||
@@ -1625,8 +1625,8 @@ This is best illustrated by an example:
|
||||
result = x + 1
|
||||
|
||||
|
||||
A symbol of a module *can* be *qualified* with the ``module.symbol`` syntax. If
|
||||
the symbol is ambiguous, it even *has* to be qualified. A symbol is ambiguous
|
||||
A symbol of a module *can* be *qualified* with the ``module.symbol`` syntax. And if
|
||||
a symbol is ambiguous, it *must* 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:
|
||||
|
||||
@@ -1642,7 +1642,7 @@ imported by a third one:
|
||||
# Module C
|
||||
import A, B
|
||||
write(stdout, x) # error: x is ambiguous
|
||||
write(stdout, A.x) # no error: qualifier used
|
||||
write(stdout, A.x) # okay: qualifier used
|
||||
|
||||
var x = 4
|
||||
write(stdout, x) # not ambiguous: uses the module C's x
|
||||
|
||||
9
koch.nim
9
koch.nim
@@ -512,6 +512,14 @@ proc pushCsources() =
|
||||
finally:
|
||||
setCurrentDir(cwd)
|
||||
|
||||
proc valgrind(cmd: string) =
|
||||
exec("nim c " & cmd)
|
||||
var i = cmd.len-1
|
||||
while i >= 0 and cmd[i] != ' ': dec i
|
||||
let file = if i >= 0: substr(cmd, i+1) else: cmd
|
||||
let supp = getAppDir() / "tools" / "nimgrind.supp"
|
||||
exec("valgrind --suppressions=" & supp & " " & changeFileExt(file, ""))
|
||||
|
||||
proc showHelp() =
|
||||
quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)),
|
||||
CompileDate, CompileTime], QuitSuccess)
|
||||
@@ -548,5 +556,6 @@ of cmdArgument:
|
||||
of "nimsuggest": bundleNimsuggest(buildExe=true)
|
||||
of "tools": buildTools(existsDir(".git"))
|
||||
of "pushcsource", "pushcsources": pushCsources()
|
||||
of "valgrind": valgrind(op.cmdLineRest)
|
||||
else: showHelp()
|
||||
of cmdEnd: showHelp()
|
||||
|
||||
@@ -16,9 +16,6 @@
|
||||
## <manual.html#anonymous-procs>`_ to procs like ``filter`` to reduce typing.
|
||||
## Anonymous procs can use `the special do notation <manual.html#do-notation>`_
|
||||
## which is more convenient in certain situations.
|
||||
##
|
||||
## **Note**: This interface will change as soon as the compiler supports
|
||||
## closures and proper coroutines.
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
|
||||
@@ -83,7 +83,8 @@ proc unmapMem*(f: var MemFile, p: pointer, size: int) =
|
||||
|
||||
|
||||
proc open*(filename: string, mode: FileMode = fmRead,
|
||||
mappedSize = -1, offset = 0, newFileSize = -1): MemFile =
|
||||
mappedSize = -1, offset = 0, newFileSize = -1,
|
||||
allowRemap = false): 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
|
||||
@@ -95,6 +96,9 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||
## ``offset`` must be multiples of the PAGE SIZE of your OS
|
||||
## (usually 4K or 8K but is unique to your OS)
|
||||
##
|
||||
## ``allowRemap`` only needs to be true if you want to call ``mapMem`` on
|
||||
## the resulting MemFile; else file handles are not kept open.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
@@ -189,11 +193,14 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||
else: result.size = fileSize.int
|
||||
|
||||
result.wasOpened = true
|
||||
if not allowRemap and result.fHandle != INVALID_HANDLE_VALUE:
|
||||
if closeHandle(result.fHandle) == 0:
|
||||
result.fHandle = INVALID_HANDLE_VALUE
|
||||
|
||||
else:
|
||||
template fail(errCode: OSErrorCode, msg: expr) =
|
||||
rollback()
|
||||
if result.handle != 0: discard close(result.handle)
|
||||
if result.handle != -1: discard close(result.handle)
|
||||
raiseOSError(errCode)
|
||||
|
||||
var flags = if readonly: O_RDONLY else: O_RDWR
|
||||
@@ -236,6 +243,10 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||
if result.mem == cast[pointer](MAP_FAILED):
|
||||
fail(osLastError(), "file mapping failed")
|
||||
|
||||
if not allowRemap and result.handle != -1:
|
||||
if close(result.handle) == 0:
|
||||
result.handle = -1
|
||||
|
||||
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.
|
||||
@@ -244,15 +255,16 @@ proc close*(f: var MemFile) =
|
||||
var lastErr: OSErrorCode
|
||||
|
||||
when defined(windows):
|
||||
if f.fHandle != INVALID_HANDLE_VALUE and f.wasOpened:
|
||||
if f.wasOpened:
|
||||
error = unmapViewOfFile(f.mem) == 0
|
||||
lastErr = osLastError()
|
||||
error = (closeHandle(f.mapHandle) == 0) or error
|
||||
error = (closeHandle(f.fHandle) == 0) or error
|
||||
if f.fHandle != INVALID_HANDLE_VALUE:
|
||||
error = (closeHandle(f.fHandle) == 0) or error
|
||||
else:
|
||||
if f.handle != 0:
|
||||
error = munmap(f.mem, f.size) != 0
|
||||
lastErr = osLastError()
|
||||
error = munmap(f.mem, f.size) != 0
|
||||
lastErr = osLastError()
|
||||
if f.handle != -1:
|
||||
error = (close(f.handle) != 0) or error
|
||||
|
||||
f.size = 0
|
||||
@@ -263,7 +275,7 @@ proc close*(f: var MemFile) =
|
||||
f.mapHandle = 0
|
||||
f.wasOpened = false
|
||||
else:
|
||||
f.handle = 0
|
||||
f.handle = -1
|
||||
|
||||
if error: raiseOSError(lastErr)
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ when not declared(getEnv) or defined(nimscript):
|
||||
WriteEnvEffect* = object of WriteIOEffect ## effect that denotes a write
|
||||
## to an environment variable
|
||||
|
||||
ReadDirEffect* = object of ReadIOEffect ## effect that denotes a write
|
||||
## operation to the directory
|
||||
ReadDirEffect* = object of ReadIOEffect ## effect that denotes a read
|
||||
## operation from the directory
|
||||
## structure
|
||||
WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write
|
||||
## operation to
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
## var msg = createMessage("Hello from Nim's SMTP",
|
||||
## "Hello!.\n Is this awesome or what?",
|
||||
## @["foo@gmail.com"])
|
||||
## var smtpConn = connect("smtp.gmail.com", Port 465, true, true)
|
||||
## let smtpConn = newSmtp(useSsl = true, debug=true)
|
||||
## smtpConn.connect("smtp.gmail.com", Port 465)
|
||||
## smtpConn.auth("username", "password")
|
||||
## smtpConn.sendmail("username@gmail.com", @["foo@gmail.com"], $msg)
|
||||
##
|
||||
@@ -34,10 +35,6 @@ import asyncnet, asyncdispatch
|
||||
export Port
|
||||
|
||||
type
|
||||
Smtp* = object
|
||||
sock: Socket
|
||||
debug: bool
|
||||
|
||||
Message* = object
|
||||
msgTo: seq[string]
|
||||
msgCc: seq[string]
|
||||
@@ -47,37 +44,29 @@ type
|
||||
|
||||
ReplyError* = object of IOError
|
||||
|
||||
AsyncSmtp* = ref object
|
||||
sock: AsyncSocket
|
||||
address: string
|
||||
port: Port
|
||||
useSsl: bool
|
||||
SmtpBase[SocketType] = ref object
|
||||
sock: SocketType
|
||||
debug: bool
|
||||
|
||||
Smtp* = SmtpBase[Socket]
|
||||
AsyncSmtp* = SmtpBase[AsyncSocket]
|
||||
|
||||
{.deprecated: [EInvalidReply: ReplyError, TMessage: Message, TSMTP: Smtp].}
|
||||
|
||||
proc debugSend(smtp: Smtp, cmd: string) =
|
||||
proc debugSend(smtp: Smtp | AsyncSmtp, cmd: string) {.multisync.} =
|
||||
if smtp.debug:
|
||||
echo("C:" & cmd)
|
||||
smtp.sock.send(cmd)
|
||||
|
||||
proc debugRecv(smtp: var Smtp): TaintedString =
|
||||
var line = TaintedString""
|
||||
smtp.sock.readLine(line)
|
||||
await smtp.sock.send(cmd)
|
||||
|
||||
proc debugRecv(smtp: Smtp | AsyncSmtp): Future[TaintedString] {.multisync.} =
|
||||
result = await smtp.sock.recvLine()
|
||||
if smtp.debug:
|
||||
echo("S:" & line.string)
|
||||
return line
|
||||
echo("S:" & result.string)
|
||||
|
||||
proc quitExcpt(smtp: Smtp, msg: string) =
|
||||
smtp.debugSend("QUIT")
|
||||
raise newException(ReplyError, msg)
|
||||
|
||||
proc checkReply(smtp: var Smtp, reply: string) =
|
||||
var line = smtp.debugRecv()
|
||||
if not line.string.startswith(reply):
|
||||
quitExcpt(smtp, "Expected " & reply & " reply, got: " & line.string)
|
||||
|
||||
const compiledWithSsl = defined(ssl)
|
||||
|
||||
when not defined(ssl):
|
||||
@@ -86,63 +75,6 @@ when not defined(ssl):
|
||||
else:
|
||||
let defaultSSLContext = newContext(verifyMode = CVerifyNone)
|
||||
|
||||
proc connect*(address: string, port = Port(25),
|
||||
ssl = false, debug = false,
|
||||
sslContext = defaultSSLContext): Smtp =
|
||||
## Establishes a connection with a SMTP server.
|
||||
## May fail with ReplyError or with a socket error.
|
||||
result.sock = newSocket()
|
||||
if ssl:
|
||||
when compiledWithSsl:
|
||||
sslContext.wrapSocket(result.sock)
|
||||
else:
|
||||
raise newException(ESystem,
|
||||
"SMTP module compiled without SSL support")
|
||||
result.sock.connect(address, port)
|
||||
result.debug = debug
|
||||
|
||||
result.checkReply("220")
|
||||
result.debugSend("HELO " & address & "\c\L")
|
||||
result.checkReply("250")
|
||||
|
||||
proc auth*(smtp: var Smtp, username, password: string) =
|
||||
## Sends an AUTH command to the server to login as the `username`
|
||||
## using `password`.
|
||||
## May fail with ReplyError.
|
||||
|
||||
smtp.debugSend("AUTH LOGIN\c\L")
|
||||
smtp.checkReply("334") # TODO: Check whether it's asking for the "Username:"
|
||||
# i.e "334 VXNlcm5hbWU6"
|
||||
smtp.debugSend(encode(username) & "\c\L")
|
||||
smtp.checkReply("334") # TODO: Same as above, only "Password:" (I think?)
|
||||
|
||||
smtp.debugSend(encode(password) & "\c\L")
|
||||
smtp.checkReply("235") # Check whether the authentification was successful.
|
||||
|
||||
proc sendmail*(smtp: var Smtp, fromaddr: string,
|
||||
toaddrs: seq[string], msg: string) =
|
||||
## Sends `msg` from `fromaddr` to `toaddr`.
|
||||
## Messages may be formed using ``createMessage`` by converting the
|
||||
## Message into a string.
|
||||
|
||||
smtp.debugSend("MAIL FROM:<" & fromaddr & ">\c\L")
|
||||
smtp.checkReply("250")
|
||||
for address in items(toaddrs):
|
||||
smtp.debugSend("RCPT TO:<" & address & ">\c\L")
|
||||
smtp.checkReply("250")
|
||||
|
||||
# Send the message
|
||||
smtp.debugSend("DATA " & "\c\L")
|
||||
smtp.checkReply("354")
|
||||
smtp.debugSend(msg & "\c\L")
|
||||
smtp.debugSend(".\c\L")
|
||||
smtp.checkReply("250")
|
||||
|
||||
proc close*(smtp: Smtp) =
|
||||
## Disconnects from the SMTP server and closes the socket.
|
||||
smtp.debugSend("QUIT\c\L")
|
||||
smtp.sock.close()
|
||||
|
||||
proc createMessage*(mSubject, mBody: string, mTo, mCc: seq[string],
|
||||
otherHeaders: openarray[tuple[name, value: string]]): Message =
|
||||
## Creates a new MIME compliant message.
|
||||
@@ -178,81 +110,94 @@ proc `$`*(msg: Message): string =
|
||||
result.add("\c\L")
|
||||
result.add(msg.msgBody)
|
||||
|
||||
proc newAsyncSmtp*(address: string, port: Port, useSsl = false,
|
||||
proc newSmtp*(useSsl = false, debug=false,
|
||||
sslContext = defaultSslContext): Smtp =
|
||||
## Creates a new ``Smtp`` instance.
|
||||
new result
|
||||
result.debug = debug
|
||||
|
||||
result.sock = newSocket()
|
||||
if useSsl:
|
||||
when compiledWithSsl:
|
||||
sslContext.wrapSocket(result.sock)
|
||||
else:
|
||||
raise newException(SystemError,
|
||||
"SMTP module compiled without SSL support")
|
||||
|
||||
proc newAsyncSmtp*(useSsl = false, debug=false,
|
||||
sslContext = defaultSslContext): AsyncSmtp =
|
||||
## Creates a new ``AsyncSmtp`` instance.
|
||||
new result
|
||||
result.address = address
|
||||
result.port = port
|
||||
result.useSsl = useSsl
|
||||
result.debug = debug
|
||||
|
||||
result.sock = newAsyncSocket()
|
||||
if useSsl:
|
||||
when compiledWithSsl:
|
||||
sslContext.wrapSocket(result.sock)
|
||||
else:
|
||||
raise newException(ESystem,
|
||||
raise newException(SystemError,
|
||||
"SMTP module compiled without SSL support")
|
||||
|
||||
proc quitExcpt(smtp: AsyncSmtp, msg: string): Future[void] =
|
||||
var retFuture = newFuture[void]()
|
||||
var sendFut = smtp.sock.send("QUIT")
|
||||
var sendFut = smtp.debugSend("QUIT")
|
||||
sendFut.callback =
|
||||
proc () =
|
||||
# TODO: Fix this in async procs.
|
||||
raise newException(ReplyError, msg)
|
||||
return retFuture
|
||||
|
||||
proc checkReply(smtp: AsyncSmtp, reply: string) {.async.} =
|
||||
var line = await smtp.sock.recvLine()
|
||||
if not line.string.startswith(reply):
|
||||
await quitExcpt(smtp, "Expected " & reply & " reply, got: " & line.string)
|
||||
proc checkReply(smtp: Smtp | AsyncSmtp, reply: string) {.multisync.} =
|
||||
var line = await smtp.debugRecv()
|
||||
if not line.startswith(reply):
|
||||
await quitExcpt(smtp, "Expected " & reply & " reply, got: " & line)
|
||||
|
||||
proc connect*(smtp: AsyncSmtp) {.async.} =
|
||||
proc connect*(smtp: Smtp | AsyncSmtp,
|
||||
address: string, port: Port) {.multisync.} =
|
||||
## Establishes a connection with a SMTP server.
|
||||
## May fail with ReplyError or with a socket error.
|
||||
await smtp.sock.connect(smtp.address, smtp.port)
|
||||
await smtp.sock.connect(address, port)
|
||||
|
||||
await smtp.checkReply("220")
|
||||
await smtp.sock.send("HELO " & smtp.address & "\c\L")
|
||||
await smtp.debugSend("HELO " & address & "\c\L")
|
||||
await smtp.checkReply("250")
|
||||
|
||||
proc auth*(smtp: AsyncSmtp, username, password: string) {.async.} =
|
||||
proc auth*(smtp: Smtp | AsyncSmtp, username, password: string) {.multisync.} =
|
||||
## Sends an AUTH command to the server to login as the `username`
|
||||
## using `password`.
|
||||
## May fail with ReplyError.
|
||||
|
||||
await smtp.sock.send("AUTH LOGIN\c\L")
|
||||
await smtp.debugSend("AUTH LOGIN\c\L")
|
||||
await smtp.checkReply("334") # TODO: Check whether it's asking for the "Username:"
|
||||
# i.e "334 VXNlcm5hbWU6"
|
||||
await smtp.sock.send(encode(username) & "\c\L")
|
||||
await smtp.debugSend(encode(username) & "\c\L")
|
||||
await smtp.checkReply("334") # TODO: Same as above, only "Password:" (I think?)
|
||||
|
||||
await smtp.sock.send(encode(password) & "\c\L")
|
||||
await smtp.debugSend(encode(password) & "\c\L")
|
||||
await smtp.checkReply("235") # Check whether the authentification was successful.
|
||||
|
||||
proc sendMail*(smtp: AsyncSmtp, fromAddr: string,
|
||||
toAddrs: seq[string], msg: string) {.async.} =
|
||||
proc sendMail*(smtp: Smtp | AsyncSmtp, fromAddr: string,
|
||||
toAddrs: seq[string], msg: string) {.multisync.} =
|
||||
## Sends ``msg`` from ``fromAddr`` to the addresses specified in ``toAddrs``.
|
||||
## Messages may be formed using ``createMessage`` by converting the
|
||||
## Message into a string.
|
||||
|
||||
await smtp.sock.send("MAIL FROM:<" & fromAddr & ">\c\L")
|
||||
await smtp.debugSend("MAIL FROM:<" & fromAddr & ">\c\L")
|
||||
await smtp.checkReply("250")
|
||||
for address in items(toAddrs):
|
||||
await smtp.sock.send("RCPT TO:<" & address & ">\c\L")
|
||||
await smtp.debugSend("RCPT TO:<" & address & ">\c\L")
|
||||
await smtp.checkReply("250")
|
||||
|
||||
# Send the message
|
||||
await smtp.sock.send("DATA " & "\c\L")
|
||||
await smtp.debugSend("DATA " & "\c\L")
|
||||
await smtp.checkReply("354")
|
||||
await smtp.sock.send(msg & "\c\L")
|
||||
await smtp.sock.send(".\c\L")
|
||||
await smtp.debugSend(".\c\L")
|
||||
await smtp.checkReply("250")
|
||||
|
||||
proc close*(smtp: AsyncSmtp) {.async.} =
|
||||
proc close*(smtp: Smtp | AsyncSmtp) {.multisync.} =
|
||||
## Disconnects from the SMTP server and closes the socket.
|
||||
await smtp.sock.send("QUIT\c\L")
|
||||
await smtp.debugSend("QUIT\c\L")
|
||||
smtp.sock.close()
|
||||
|
||||
when not defined(testing) and isMainModule:
|
||||
@@ -278,25 +223,24 @@ when not defined(testing) and isMainModule:
|
||||
|
||||
proc async_test() {.async.} =
|
||||
let client = newAsyncSmtp(
|
||||
conf["smtphost"],
|
||||
conf["port"].parseInt.Port,
|
||||
conf["use_tls"].parseBool
|
||||
conf["use_tls"].parseBool,
|
||||
debug=true
|
||||
)
|
||||
await client.connect()
|
||||
await client.connect(conf["smtphost"], conf["port"].parseInt.Port)
|
||||
await client.auth(conf["username"], conf["password"])
|
||||
await client.sendMail(conf["sender"], @[conf["recipient"]], $msg)
|
||||
await client.close()
|
||||
echo "async email sent"
|
||||
|
||||
proc sync_test() =
|
||||
var smtpConn = connect(
|
||||
conf["smtphost"],
|
||||
conf["port"].parseInt.Port,
|
||||
var smtpConn = newSmtp(
|
||||
conf["use_tls"].parseBool,
|
||||
true, # debug
|
||||
debug=true
|
||||
)
|
||||
smtpConn.connect(conf["smtphost"], conf["port"].parseInt.Port)
|
||||
smtpConn.auth(conf["username"], conf["password"])
|
||||
smtpConn.sendmail(conf["sender"], @[conf["recipient"]], $msg)
|
||||
smtpConn.sendMail(conf["sender"], @[conf["recipient"]], $msg)
|
||||
smtpConn.close()
|
||||
echo "sync email sent"
|
||||
|
||||
waitFor async_test()
|
||||
|
||||
@@ -417,7 +417,7 @@ type
|
||||
## Base exception class.
|
||||
##
|
||||
## Each exception has to inherit from `Exception`. See the full `exception
|
||||
## hierarchy`_.
|
||||
## hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
parent*: ref Exception ## parent exception (can be used as a stack)
|
||||
name*: cstring ## The exception's name is its Nim identifier.
|
||||
## This field is filled automatically in the
|
||||
@@ -430,51 +430,51 @@ type
|
||||
SystemError* = object of Exception ## \
|
||||
## Abstract class for exceptions that the runtime system raises.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
IOError* = object of SystemError ## \
|
||||
## Raised if an IO error occurred.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
EOFError* = object of IOError ## \
|
||||
## Raised if an IO "end of file" error occurred.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
OSError* = object of SystemError ## \
|
||||
## Raised if an operating system service failed.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
errorCode*: int32 ## OS-defined error code describing this error.
|
||||
LibraryError* = object of OSError ## \
|
||||
## Raised if a dynamic library could not be loaded.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
ResourceExhaustedError* = object of SystemError ## \
|
||||
## Raised if a resource request could not be fulfilled.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
ArithmeticError* = object of Exception ## \
|
||||
## Raised if any kind of arithmetic error occurred.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
DivByZeroError* = object of ArithmeticError ## \
|
||||
## Raised for runtime integer divide-by-zero errors.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
|
||||
OverflowError* = object of ArithmeticError ## \
|
||||
## Raised for runtime integer overflows.
|
||||
##
|
||||
## This happens for calculations whose results are too large to fit in the
|
||||
## provided bits. See the full `exception hierarchy`_.
|
||||
## provided bits. See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
AccessViolationError* = object of Exception ## \
|
||||
## Raised for invalid memory access errors
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
AssertionError* = object of Exception ## \
|
||||
## Raised when assertion is proved wrong.
|
||||
##
|
||||
## Usually the result of using the `assert() template <#assert>`_. See the
|
||||
## full `exception hierarchy`_.
|
||||
## full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
ValueError* = object of Exception ## \
|
||||
## Raised for string and object conversion errors.
|
||||
KeyError* = object of ValueError ## \
|
||||
@@ -482,66 +482,66 @@ type
|
||||
##
|
||||
## Mostly used by the `tables <tables.html>`_ module, it can also be raised
|
||||
## by other collection modules like `sets <sets.html>`_ or `strtabs
|
||||
## <strtabs.html>`_. See the full `exception hierarchy`_.
|
||||
## <strtabs.html>`_. See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
OutOfMemError* = object of SystemError ## \
|
||||
## Raised for unsuccessful attempts to allocate memory.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
IndexError* = object of Exception ## \
|
||||
## Raised if an array index is out of bounds.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
|
||||
FieldError* = object of Exception ## \
|
||||
## Raised if a record field is not accessible because its dicriminant's
|
||||
## value does not fit.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
RangeError* = object of Exception ## \
|
||||
## Raised if a range check error occurred.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
StackOverflowError* = object of SystemError ## \
|
||||
## Raised if the hardware stack used for subroutine calls overflowed.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
ReraiseError* = object of Exception ## \
|
||||
## Raised if there is no exception to reraise.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
ObjectAssignmentError* = object of Exception ## \
|
||||
## Raised if an object gets assigned to its parent's object.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
ObjectConversionError* = object of Exception ## \
|
||||
## Raised if an object is converted to an incompatible object type.
|
||||
## You can use ``of`` operator to check if conversion will succeed.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
FloatingPointError* = object of Exception ## \
|
||||
## Base class for floating point exceptions.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
FloatInvalidOpError* = object of FloatingPointError ## \
|
||||
## Raised by invalid operations according to IEEE.
|
||||
##
|
||||
## Raised by ``0.0/0.0``, for example. See the full `exception
|
||||
## hierarchy`_.
|
||||
## hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
FloatDivByZeroError* = object of FloatingPointError ## \
|
||||
## Raised by division by zero.
|
||||
##
|
||||
## Divisor is zero and dividend is a finite nonzero number. See the full
|
||||
## `exception hierarchy`_.
|
||||
## `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
FloatOverflowError* = object of FloatingPointError ## \
|
||||
## Raised for overflows.
|
||||
##
|
||||
## The operation produced a result that exceeds the range of the exponent.
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
FloatUnderflowError* = object of FloatingPointError ## \
|
||||
## Raised for underflows.
|
||||
##
|
||||
## The operation produced a result that is too small to be represented as a
|
||||
## normal number. See the full `exception hierarchy`_.
|
||||
## normal number. See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
FloatInexactError* = object of FloatingPointError ## \
|
||||
## Raised for inexact results.
|
||||
##
|
||||
@@ -549,11 +549,11 @@ type
|
||||
## precision -- for example: ``2.0 / 3.0, log(1.1)``
|
||||
##
|
||||
## **NOTE**: Nim currently does not detect these! See the full
|
||||
## `exception hierarchy`_.
|
||||
## `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
DeadThreadError* = object of Exception ## \
|
||||
## Raised if it is attempted to send a message to a dead thread.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
|
||||
{.deprecated: [TObject: RootObj, PObject: RootRef, TEffect: RootEffect,
|
||||
FTime: TimeEffect, FIO: IOEffect, FReadIO: ReadIOEffect,
|
||||
|
||||
17
tests/errmsgs/t5167_1.nim
Normal file
17
tests/errmsgs/t5167_1.nim
Normal file
@@ -0,0 +1,17 @@
|
||||
discard """
|
||||
errormsg: "'bar' doesn't have a concrete type, due to unspecified generic parameters."
|
||||
line: 16
|
||||
"""
|
||||
|
||||
proc foo[T]() =
|
||||
var y1 = foo[string]
|
||||
var y2 = foo[T]
|
||||
|
||||
proc bar[T]() =
|
||||
let x = 0
|
||||
|
||||
let good1 = foo[int]
|
||||
let good2 = bar[int]
|
||||
|
||||
let err = bar
|
||||
|
||||
12
tests/errmsgs/t5167_2.nim
Normal file
12
tests/errmsgs/t5167_2.nim
Normal file
@@ -0,0 +1,12 @@
|
||||
discard """
|
||||
cmd: "nim c --threads:on $file"
|
||||
errormsg: "'threadFunc' doesn't have a concrete type, due to unspecified generic parameters."
|
||||
line: 11
|
||||
"""
|
||||
|
||||
proc threadFunc[T]() {.thread.} =
|
||||
let x = 0
|
||||
|
||||
var thr: Thread[void]
|
||||
thr.createThread(threadFunc)
|
||||
|
||||
25
tests/errmsgs/t5167_3.nim
Normal file
25
tests/errmsgs/t5167_3.nim
Normal file
@@ -0,0 +1,25 @@
|
||||
discard """
|
||||
cmd: "nim c --threads:on $file"
|
||||
errormsg: "type mismatch"
|
||||
line: 24
|
||||
"""
|
||||
|
||||
type
|
||||
TGeneric[T] = object
|
||||
x: int
|
||||
|
||||
proc foo1[A, B, C, D](x: proc (a: A, b: B, c: C, d: D)) =
|
||||
echo "foo1"
|
||||
|
||||
proc foo2(x: proc(x: int)) =
|
||||
echo "foo2"
|
||||
|
||||
# The goal of this test is to verify that none of the generic parameters of the
|
||||
# proc will be marked as unused. The error message should be "type mismatch" instead
|
||||
# of "'bar' doesn't have a concrete type, due to unspecified generic parameters".
|
||||
proc bar[A, B, C, D](x: A, y: seq[B], z: array[4, TGeneric[C]], r: TGeneric[D]) =
|
||||
echo "bar"
|
||||
|
||||
foo1[int, seq[int], array[4, TGeneric[float]], TGeneric[string]] bar
|
||||
foo2 bar
|
||||
|
||||
20
tests/errmsgs/t5167_4.nim
Normal file
20
tests/errmsgs/t5167_4.nim
Normal file
@@ -0,0 +1,20 @@
|
||||
discard """
|
||||
errormsg: "type mismatch: got (proc [*missing parameters*](x: int) | proc (x: string){.gcsafe, locks: 0.})"
|
||||
line: 19
|
||||
"""
|
||||
|
||||
type
|
||||
TGeneric[T] = object
|
||||
x: int
|
||||
|
||||
proc foo[B](x: int) =
|
||||
echo "foo1"
|
||||
|
||||
proc foo(x: string) =
|
||||
echo "foo2"
|
||||
|
||||
proc bar(x: proc (x: int)) =
|
||||
echo "bar"
|
||||
|
||||
bar foo
|
||||
|
||||
25
tests/errmsgs/t5167_5.nim
Normal file
25
tests/errmsgs/t5167_5.nim
Normal file
@@ -0,0 +1,25 @@
|
||||
discard """
|
||||
cmd: "nim check $file"
|
||||
errormsg: "'m' has unspecified generic parameters"
|
||||
nimout: '''
|
||||
t5167_5.nim(20, 9) Error: 't' has unspecified generic parameters
|
||||
t5167_5.nim(21, 5) Error: 't' has unspecified generic parameters
|
||||
t5167_5.nim(23, 9) Error: 'm' has unspecified generic parameters
|
||||
t5167_5.nim(24, 5) Error: 'm' has unspecified generic parameters
|
||||
'''
|
||||
"""
|
||||
|
||||
template t[B]() =
|
||||
echo "foo1"
|
||||
|
||||
macro m[T]: stmt = nil
|
||||
|
||||
proc bar(x: proc (x: int)) =
|
||||
echo "bar"
|
||||
|
||||
let x = t
|
||||
bar t
|
||||
|
||||
let y = m
|
||||
bar m
|
||||
|
||||
47
tests/errmsgs/tnon_concrete_cast.nim
Normal file
47
tests/errmsgs/tnon_concrete_cast.nim
Normal file
@@ -0,0 +1,47 @@
|
||||
discard """
|
||||
errormsg: "cannot cast to a non concrete type: 'ptr SomeNumber'"
|
||||
line: 36
|
||||
"""
|
||||
|
||||
# https://github.com/nim-lang/Nim/issues/5428
|
||||
|
||||
type
|
||||
MemFile = object
|
||||
mem: pointer
|
||||
|
||||
proc memfileopen(filename: string, newFileSize: int): MemFile =
|
||||
# just a memfile mock
|
||||
return
|
||||
|
||||
type
|
||||
MyData = object
|
||||
member1: seq[int]
|
||||
member2: int
|
||||
|
||||
type
|
||||
MyReadWrite = object
|
||||
memfile: MemFile
|
||||
offset: int
|
||||
|
||||
# Here, SomeNumber is bound to a concrete type, and that's OK
|
||||
proc write(rw: var MyReadWrite; value: SomeNumber): void =
|
||||
(cast[ptr SomeNumber](cast[uint](rw.memfile.mem) + rw.offset.uint))[] = value
|
||||
rw.offset += sizeof(SomeNumber)
|
||||
|
||||
# Here, we try to use SomeNumber without binding it to a type. This should
|
||||
# produce an error message for now. It's also possible to relax the rules
|
||||
# and allow for type-class based type inference in such situations.
|
||||
proc write[T](rw: var MyReadWrite; value: seq[T]): void =
|
||||
rw.write value.len
|
||||
let dst = cast[ptr SomeNumber](cast[uint](rw.memfile.mem) + uint(rw.offset))
|
||||
let src = cast[pointer](value[0].unsafeAddr)
|
||||
let size = sizeof(T) * value.len
|
||||
copyMem(dst, src, size)
|
||||
rw.offset += size
|
||||
|
||||
proc saveBinFile(arg: var MyData, filename: string): void =
|
||||
var rw: MyReadWrite
|
||||
rw.memfile = memfileOpen(filename, newFileSize = rw.offset)
|
||||
rw.offset = 0
|
||||
rw.write arg.member1
|
||||
|
||||
@@ -5,7 +5,7 @@ discard """
|
||||
import
|
||||
macros, strutils
|
||||
|
||||
macro test_macro*(n: stmt): stmt {.immediate.} =
|
||||
macro test_macro*(s: string, n: stmt): stmt {.immediate.} =
|
||||
result = newNimNode(nnkStmtList)
|
||||
var ass : NimNode = newNimNode(nnkAsgn)
|
||||
add(ass, newIdentNode("str"))
|
||||
|
||||
@@ -6,7 +6,7 @@ discard """
|
||||
|
||||
import macros
|
||||
|
||||
macro quoteWords(n: expr): expr {.immediate.} =
|
||||
macro quoteWords(n: varargs[expr]): expr {.immediate.} =
|
||||
let n = callsite()
|
||||
result = newNimNode(nnkBracket, n)
|
||||
for i in 1..n.len-1:
|
||||
|
||||
37
tests/overload/tparam_forwarding.nim
Normal file
37
tests/overload/tparam_forwarding.nim
Normal file
@@ -0,0 +1,37 @@
|
||||
discard """
|
||||
output: '''baz
|
||||
10
|
||||
100
|
||||
1000
|
||||
a
|
||||
b
|
||||
c
|
||||
'''
|
||||
"""
|
||||
|
||||
type
|
||||
Foo = object
|
||||
x: int
|
||||
|
||||
proc stringVarargs*(strings: varargs[string, `$`]): void =
|
||||
for s in strings: echo s
|
||||
|
||||
proc fooVarargs*(foos: varargs[Foo]) =
|
||||
for f in foos: echo f.x
|
||||
|
||||
template templateForwarding*(callable: untyped,
|
||||
condition: bool,
|
||||
forwarded: varargs[untyped]): untyped =
|
||||
if condition:
|
||||
callable(forwarded)
|
||||
|
||||
proc procForwarding(args: varargs[string]) =
|
||||
stringVarargs(args)
|
||||
|
||||
templateForwarding stringVarargs, 17 + 4 < 21, "foo", "bar", 100
|
||||
templateForwarding stringVarargs, 10 < 21, "baz"
|
||||
|
||||
templateForwarding fooVarargs, "test".len > 3, Foo(x: 10), Foo(x: 100), Foo(x: 1000)
|
||||
|
||||
procForwarding "a", "b", "c"
|
||||
|
||||
@@ -18,7 +18,7 @@ mm = memfiles.open(fn, mode = fmReadWrite, newFileSize = 20)
|
||||
mm.close()
|
||||
|
||||
# read, change
|
||||
mm_full = memfiles.open(fn, mode = fmWrite, mappedSize = -1)
|
||||
mm_full = memfiles.open(fn, mode = fmWrite, mappedSize = -1, allowRemap = true)
|
||||
echo "Full read size: ",mm_full.size
|
||||
p = mm_full.mapMem(fmReadWrite, 20, 0)
|
||||
var p2 = cast[cstring](p)
|
||||
|
||||
14
tools/nimgrind.supp
Normal file
14
tools/nimgrind.supp
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
markstackandregisters_Cond
|
||||
Memcheck:Cond
|
||||
...
|
||||
fun:markStackAndRegisters*
|
||||
...
|
||||
}
|
||||
{
|
||||
markstackandregisters_Value8
|
||||
Memcheck:Value8
|
||||
...
|
||||
fun:markStackAndRegisters*
|
||||
...
|
||||
}
|
||||
@@ -23,6 +23,12 @@ Changes affecting backwards compatibility
|
||||
pointer. Now the hash is calculated from the contents of the string, assuming
|
||||
``cstring`` is a null-terminated string. Equal ``string`` and ``cstring``
|
||||
values produce an equal hash value.
|
||||
- Macros accepting `varargs` arguments will now receive a node having the
|
||||
`nkArgList` node kind. Previous code expecting the node kind to be `nkBracket`
|
||||
may have to be updated.
|
||||
- ``memfiles.open`` now closes file handleds/fds by default. Passing
|
||||
``allowRemap=true`` to ``memfiles.open`` recovers the old behavior. The old
|
||||
behavior is only needed to call ``mapMem`` on the resulting ``MemFile``.
|
||||
|
||||
Library Additions
|
||||
-----------------
|
||||
|
||||
Reference in New Issue
Block a user