Merge branch 'devel' into faster-nimsuggest
This commit is contained in:
commit
4912300ca7
32 changed files with 474 additions and 260 deletions
|
|
@ -460,6 +460,8 @@ type
|
||||||
# proc foo(T: typedesc, list: seq[T]): var T
|
# proc foo(T: typedesc, list: seq[T]): var T
|
||||||
# proc foo(L: static[int]): array[L, int]
|
# proc foo(L: static[int]): array[L, int]
|
||||||
# can be attached to ranges to indicate that the range
|
# 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.
|
# depends on unresolved static params.
|
||||||
tfRetType, # marks return types in proc (used to detect type classes
|
tfRetType, # marks return types in proc (used to detect type classes
|
||||||
# used as return types for return type inference)
|
# 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: PType) {.deprecated.}
|
||||||
proc debug*(n: PNode) {.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 ----------------------------------
|
# --------------------------- ident tables ----------------------------------
|
||||||
proc idTableGet*(t: TIdTable, key: PIdObj): RootRef
|
proc idTableGet*(t: TIdTable, key: PIdObj): RootRef
|
||||||
proc idTableGet*(t: TIdTable, key: int): 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
|
expectedRegularParams = <s.typ.len
|
||||||
givenRegularParams = totalParams - genericParams
|
givenRegularParams = totalParams - genericParams
|
||||||
if givenRegularParams < 0: givenRegularParams = 0
|
if givenRegularParams < 0: givenRegularParams = 0
|
||||||
|
|
||||||
if totalParams > expectedRegularParams + genericParams:
|
if totalParams > expectedRegularParams + genericParams:
|
||||||
globalError(n.info, errWrongNumberOfArguments)
|
globalError(n.info, errWrongNumberOfArguments)
|
||||||
|
|
||||||
|
if totalParams < genericParams:
|
||||||
|
globalError(n.info, errMissingGenericParamsForTemplate,
|
||||||
|
n.renderTree)
|
||||||
|
|
||||||
result = newNodeI(nkArgList, n.info)
|
result = newNodeI(nkArgList, n.info)
|
||||||
for i in 1 .. givenRegularParams:
|
for i in 1 .. givenRegularParams:
|
||||||
result.addSon n.sons[i]
|
result.addSon n.sons[i]
|
||||||
|
|
|
||||||
|
|
@ -1080,7 +1080,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
|
||||||
inc(L.bufpos)
|
inc(L.bufpos)
|
||||||
of '.':
|
of '.':
|
||||||
when defined(nimsuggest):
|
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.line == gTrackPos.line and gIdeCmd == ideSug:
|
||||||
tok.tokType = tkDot
|
tok.tokType = tkDot
|
||||||
L.cursor = CursorPosition.InToken
|
L.cursor = CursorPosition.InToken
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,8 @@ type
|
||||||
errVarForOutParamNeeded,
|
errVarForOutParamNeeded,
|
||||||
errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX,
|
errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX,
|
||||||
errAmbiguousCallXYZ, errWrongNumberOfArguments,
|
errAmbiguousCallXYZ, errWrongNumberOfArguments,
|
||||||
|
errWrongNumberOfArgumentsInCall,
|
||||||
|
errMissingGenericParamsForTemplate,
|
||||||
errXCannotBePassedToProcVar,
|
errXCannotBePassedToProcVar,
|
||||||
errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProcX, errImplOfXNotAllowed,
|
errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProcX, errImplOfXNotAllowed,
|
||||||
errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX,
|
errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX,
|
||||||
|
|
@ -89,6 +91,7 @@ type
|
||||||
errMainModuleMustBeSpecified,
|
errMainModuleMustBeSpecified,
|
||||||
errXExpected,
|
errXExpected,
|
||||||
errTIsNotAConcreteType,
|
errTIsNotAConcreteType,
|
||||||
|
errCastToANonConcreteType,
|
||||||
errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,
|
errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,
|
||||||
errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
|
errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
|
||||||
errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly,
|
errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly,
|
||||||
|
|
@ -107,6 +110,7 @@ type
|
||||||
errCannotInferTypeOfTheLiteral,
|
errCannotInferTypeOfTheLiteral,
|
||||||
errCannotInferReturnType,
|
errCannotInferReturnType,
|
||||||
errGenericLambdaNotAllowed,
|
errGenericLambdaNotAllowed,
|
||||||
|
errProcHasNoConcreteType,
|
||||||
errCompilerDoesntSupportTarget,
|
errCompilerDoesntSupportTarget,
|
||||||
errUser,
|
errUser,
|
||||||
warnCannotOpenFile,
|
warnCannotOpenFile,
|
||||||
|
|
@ -269,6 +273,8 @@ const
|
||||||
errButExpectedX: "but expected \'$1\'",
|
errButExpectedX: "but expected \'$1\'",
|
||||||
errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3",
|
errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3",
|
||||||
errWrongNumberOfArguments: "wrong number of arguments",
|
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",
|
errXCannotBePassedToProcVar: "\'$1\' cannot be passed to a procvar",
|
||||||
errXCannotBeInParamDecl: "$1 cannot be declared in parameter declaration",
|
errXCannotBeInParamDecl: "$1 cannot be declared in parameter declaration",
|
||||||
errPragmaOnlyInHeaderOfProcX: "pragmas are only allowed in the header of a proc; redefinition of $1",
|
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",
|
errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file",
|
||||||
errXExpected: "\'$1\' expected",
|
errXExpected: "\'$1\' expected",
|
||||||
errTIsNotAConcreteType: "\'$1\' is not a concrete type.",
|
errTIsNotAConcreteType: "\'$1\' is not a concrete type.",
|
||||||
|
errCastToANonConcreteType: "cannot cast to a non concrete type: \'$1\'",
|
||||||
errInvalidSectionStart: "invalid section start",
|
errInvalidSectionStart: "invalid section start",
|
||||||
errGridTableNotImplemented: "grid table is not implemented",
|
errGridTableNotImplemented: "grid table is not implemented",
|
||||||
errGeneralParseError: "general parse error",
|
errGeneralParseError: "general parse error",
|
||||||
|
|
@ -369,6 +376,7 @@ const
|
||||||
errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " &
|
errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " &
|
||||||
"it is used as an operand to another routine and the types " &
|
"it is used as an operand to another routine and the types " &
|
||||||
"of the generic paramers can be inferred from the expected signature.",
|
"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",
|
errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target",
|
||||||
errUser: "$1",
|
errUser: "$1",
|
||||||
warnCannotOpenFile: "cannot open \'$1\'",
|
warnCannotOpenFile: "cannot open \'$1\'",
|
||||||
|
|
|
||||||
|
|
@ -420,11 +420,6 @@ proc binaryStrSearch*(x: openArray[string], y: string): int =
|
||||||
return mid
|
return mid
|
||||||
result = - 1
|
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 =
|
proc parseIdeCmd*(s: string): IdeCmd =
|
||||||
case s:
|
case s:
|
||||||
of "sug": ideSug
|
of "sug": ideSug
|
||||||
|
|
|
||||||
|
|
@ -385,6 +385,13 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
|
||||||
if sym == c.p.owner:
|
if sym == c.p.owner:
|
||||||
globalError(n.info, errRecursiveDependencyX, sym.name.s)
|
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:
|
#if c.evalContext == nil:
|
||||||
# c.evalContext = c.createEvalContext(emStatic)
|
# c.evalContext = c.createEvalContext(emStatic)
|
||||||
result = evalMacroCall(c.module, c.cache, n, nOrig, sym)
|
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)
|
let tm = typeRel(m, formal, arg, true)
|
||||||
if tm in {isNone, isConvertible}: return nil
|
if tm in {isNone, isConvertible}: return nil
|
||||||
var newInst = generateInstance(c, s, m.bindings, n.info)
|
var newInst = generateInstance(c, s, m.bindings, n.info)
|
||||||
|
newInst.typ.flags.excl tfUnresolved
|
||||||
markUsed(n.info, s, c.graph.usageSym)
|
markUsed(n.info, s, c.graph.usageSym)
|
||||||
styleCheckUse(n.info, s)
|
styleCheckUse(n.info, s)
|
||||||
result = newSymNode(newInst, n.info)
|
result = newSymNode(newInst, n.info)
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
# result = errorNode(c, n)
|
# result = errorNode(c, n)
|
||||||
if result.typ != nil:
|
if result.typ != nil:
|
||||||
# XXX tyGenericInst here?
|
# 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)
|
if result.typ.kind == tyVar: result = newDeref(result)
|
||||||
elif {efWantStmt, efAllowStmt} * flags != {}:
|
elif {efWantStmt, efAllowStmt} * flags != {}:
|
||||||
result.typ = newTypeS(tyVoid, c)
|
result.typ = newTypeS(tyVoid, c)
|
||||||
|
|
@ -218,13 +220,16 @@ proc semConv(c: PContext, n: PNode): PNode =
|
||||||
proc semCast(c: PContext, n: PNode): PNode =
|
proc semCast(c: PContext, n: PNode): PNode =
|
||||||
## Semantically analyze a casting ("cast[type](param)")
|
## Semantically analyze a casting ("cast[type](param)")
|
||||||
checkSonsLen(n, 2)
|
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 = newNodeI(nkCast, n.info)
|
||||||
result.typ = semTypeNode(c, n.sons[0], nil)
|
result.typ = targetType
|
||||||
addSon(result, copyTree(n.sons[0]))
|
addSon(result, copyTree(n.sons[0]))
|
||||||
addSon(result, semExprWithType(c, n.sons[1]))
|
addSon(result, castedExpr)
|
||||||
if not isCastable(result.typ, result.sons[1].typ):
|
|
||||||
localError(result.info, errExprCannotBeCastToX,
|
|
||||||
typeToString(result.typ))
|
|
||||||
|
|
||||||
proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
|
proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
|
||||||
const
|
const
|
||||||
|
|
|
||||||
|
|
@ -503,6 +503,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||||
if hasEmpty(typ):
|
if hasEmpty(typ):
|
||||||
localError(def.info, errCannotInferTypeOfTheLiteral,
|
localError(def.info, errCannotInferTypeOfTheLiteral,
|
||||||
($typ.kind).substr(2).toLowerAscii)
|
($typ.kind).substr(2).toLowerAscii)
|
||||||
|
elif typ.kind == tyProc and tfUnresolved in typ.flags:
|
||||||
|
localError(def.info, errProcHasNoConcreteType, def.renderTree)
|
||||||
else:
|
else:
|
||||||
if symkind == skLet: localError(a.info, errLetNeedsInit)
|
if symkind == skLet: localError(a.info, errLetNeedsInit)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1009,8 +1009,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||||
result.sons[0] = r
|
result.sons[0] = r
|
||||||
result.n.typ = r
|
result.n.typ = r
|
||||||
|
|
||||||
if genericParams != nil:
|
if genericParams != nil and genericParams.len > 0:
|
||||||
for n in genericParams:
|
for n in genericParams:
|
||||||
|
if {sfUsed, sfAnon} * n.sym.flags == {}:
|
||||||
|
result.flags.incl tfUnresolved
|
||||||
|
|
||||||
if tfWildcard in n.sym.typ.flags:
|
if tfWildcard in n.sym.typ.flags:
|
||||||
n.sym.kind = skType
|
n.sym.kind = skType
|
||||||
n.sym.typ.flags.excl tfWildcard
|
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}:
|
if arg.sons[i].sym.kind in {skProc, skMethod, skConverter, skIterator}:
|
||||||
copyCandidate(z, m)
|
copyCandidate(z, m)
|
||||||
z.callee = arg.sons[i].typ
|
z.callee = arg.sons[i].typ
|
||||||
|
if tfUnresolved in z.callee.flags: continue
|
||||||
z.calleeSym = arg.sons[i].sym
|
z.calleeSym = arg.sons[i].sym
|
||||||
#if arg.sons[i].sym.name.s == "cmp":
|
#if arg.sons[i].sym.name.s == "cmp":
|
||||||
# ggDebug = true
|
# ggDebug = true
|
||||||
|
|
@ -1650,7 +1651,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||||
if a >= formalLen-1 and formal != nil and formal.typ.isVarargsUntyped:
|
if a >= formalLen-1 and formal != nil and formal.typ.isVarargsUntyped:
|
||||||
incl(marker, formal.position)
|
incl(marker, formal.position)
|
||||||
if container.isNil:
|
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)
|
setSon(m.call, formal.position + 1, container)
|
||||||
else:
|
else:
|
||||||
incrIndexType(container.typ)
|
incrIndexType(container.typ)
|
||||||
|
|
@ -1738,7 +1739,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||||
|
|
||||||
if formal.typ.isVarargsUntyped:
|
if formal.typ.isVarargsUntyped:
|
||||||
if container.isNil:
|
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)
|
setSon(m.call, formal.position + 1, container)
|
||||||
else:
|
else:
|
||||||
incrIndexType(container.typ)
|
incrIndexType(container.typ)
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ type
|
||||||
preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg
|
preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg
|
||||||
|
|
||||||
proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string
|
proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string
|
||||||
|
template `$`*(typ: PType): string = typeToString(typ)
|
||||||
|
|
||||||
proc base*(t: PType): PType =
|
proc base*(t: PType): PType =
|
||||||
result = t.sons[0]
|
result = t.sons[0]
|
||||||
|
|
@ -547,7 +548,9 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||||
if prefer != preferExported:
|
if prefer != preferExported:
|
||||||
result.add("(" & typeToString(t.sons[0]) & ")")
|
result.add("(" & typeToString(t.sons[0]) & ")")
|
||||||
of tyProc:
|
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):
|
for i in countup(1, sonsLen(t) - 1):
|
||||||
if t.n != nil and i < t.n.len and t.n[i].kind == nkSym:
|
if t.n != nil and i < t.n.len and t.n[i].kind == nkSym:
|
||||||
add(result, t.n[i].sym.name.s)
|
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:
|
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
|
.. code-block:: nim
|
||||||
for i in 0..<10:
|
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)
|
* ``pairs`` and ``mpairs`` which provides the element and an index number (immutable and mutable respectively)
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
for indx, itm in ["a","b"].pairs:
|
for index, item in ["a","b"].pairs:
|
||||||
echo itm, " at index ", indx
|
echo item, " at index ", index
|
||||||
# => a at index 0
|
# => a at index 0
|
||||||
# => b at index 1
|
# => b at index 1
|
||||||
|
|
||||||
|
|
@ -489,10 +489,10 @@ Example:
|
||||||
else:
|
else:
|
||||||
echo "unknown operating system"
|
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:
|
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.
|
compiler.
|
||||||
* The statements within a branch do not open a new scope.
|
* The statements within a branch do not open a new scope.
|
||||||
* The compiler checks the semantics and produces code *only* for the statements
|
* 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:
|
statements*. *Simple statements* cannot contain other statements:
|
||||||
Assignment, procedure calls or the ``return`` statement belong to the simple
|
Assignment, procedure calls or the ``return`` statement belong to the simple
|
||||||
statements. *Complex statements* like ``if``, ``when``, ``for``, ``while`` can
|
statements. *Complex statements* like ``if``, ``when``, ``for``, ``while`` can
|
||||||
contain other statements. To avoid ambiguities, complex statements always have
|
contain other statements. To avoid ambiguities, complex statements must always
|
||||||
to be indented, but single simple statements do not:
|
be indented, but single simple statements do not:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
# no indentation needed for single assignment statement:
|
# 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
|
leaves the procedure (and therefore the while loop) immediately. The
|
||||||
``(question: string): bool`` syntax describes that the procedure expects a
|
``(question: string): bool`` syntax describes that the procedure expects a
|
||||||
parameter named ``question`` of type ``string`` and returns a value of type
|
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``.
|
``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*,
|
Some terminology: in the example ``question`` is called a (formal) *parameter*,
|
||||||
``"Should I..."`` is called an *argument* that is passed to this 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
|
Discard statement
|
||||||
-----------------
|
-----------------
|
||||||
To call a procedure that returns a value just for its side effects and ignoring
|
To call a procedure that returns a value just for its side effects and ignoring
|
||||||
its return value, a ``discard`` statement **has** to be used. Nim does not
|
its return value, a ``discard`` statement **must** be used. Nim does not
|
||||||
allow to silently throw away a return value:
|
allow silently throwing away a return value:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
discard yes("May I ask a pointless question?")
|
discard yes("May I ask a pointless question?")
|
||||||
|
|
@ -708,7 +708,7 @@ The compiler checks that each parameter receives exactly one argument.
|
||||||
Default values
|
Default values
|
||||||
--------------
|
--------------
|
||||||
To make the ``createWindow`` proc easier to use it should provide `default
|
To make the ``createWindow`` proc easier to use it should provide `default
|
||||||
values`, these are values that are used as arguments if the caller does not
|
values`; these are values that are used as arguments if the caller does not
|
||||||
specify them:
|
specify them:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -750,19 +750,19 @@ algorithm. Ambiguous calls are reported as errors.
|
||||||
Operators
|
Operators
|
||||||
---------
|
---------
|
||||||
The Nim library makes heavy use of overloading - one reason for this is that
|
The Nim library makes heavy use of overloading - one reason for this is that
|
||||||
each operator like ``+`` is a just an overloaded proc. The parser lets you
|
each operator like ``+`` is just an overloaded proc. The parser lets you
|
||||||
use operators in `infix notation` (``a + b``) or `prefix notation` (``+ a``).
|
use operators in `infix notation` (``a + b``) or `prefix notation` (``+ a``).
|
||||||
An infix operator always receives two arguments, a prefix operator always one.
|
An infix operator always receives two arguments, a prefix operator always one.
|
||||||
Postfix operators are not possible, because this would be ambiguous: does
|
(Postfix operators are not possible, because this would be ambiguous: does
|
||||||
``a @ @ b`` mean ``(a) @ (@b)`` or ``(a@) @ (b)``? It always means
|
``a @ @ b`` mean ``(a) @ (@b)`` or ``(a@) @ (b)``? It always means
|
||||||
``(a) @ (@b)``, because there are no postfix operators in Nim.
|
``(a) @ (@b)``, because there are no postfix operators in Nim.)
|
||||||
|
|
||||||
Apart from a few built-in keyword operators such as ``and``, ``or``, ``not``,
|
Apart from a few built-in keyword operators such as ``and``, ``or``, ``not``,
|
||||||
operators always consist of these characters:
|
operators always consist of these characters:
|
||||||
``+ - * \ / < > = @ $ ~ & % ! ? ^ . |``
|
``+ - * \ / < > = @ $ ~ & % ! ? ^ . |``
|
||||||
|
|
||||||
User defined operators are allowed. Nothing stops you from defining your own
|
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
|
The operator's precedence is determined by its first character. The details
|
||||||
can be found in the manual.
|
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.
|
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.)
|
language that supports meta programming as extensively as Nim does.)
|
||||||
However, this cannot be done for mutually recursive procedures:
|
However, this cannot be done for mutually recursive procedures:
|
||||||
|
|
||||||
|
|
@ -822,7 +822,7 @@ whose value is then returned implicitly.
|
||||||
Iterators
|
Iterators
|
||||||
=========
|
=========
|
||||||
|
|
||||||
Let's return to the boring counting example:
|
Let's return to the simple counting example:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
echo "Counting to ten: "
|
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
|
only ``return``, but return and **continue** after an iteration has
|
||||||
finished. This *return and continue* is called a `yield` statement. Now
|
finished. This *return and continue* is called a `yield` statement. Now
|
||||||
the only thing left to do is to replace the ``proc`` keyword by ``iterator``
|
the only thing left to do is to replace the ``proc`` keyword by ``iterator``
|
||||||
and there it is - our first iterator:
|
and here it is - our first iterator:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
iterator countup(a, b: int): int =
|
iterator countup(a, b: int): int =
|
||||||
|
|
@ -856,8 +856,8 @@ Iterators look very similar to procedures, but there are several
|
||||||
important differences:
|
important differences:
|
||||||
|
|
||||||
* Iterators can only be called from for loops.
|
* Iterators can only be called from for loops.
|
||||||
* Iterators cannot contain a ``return`` statement and procs cannot contain a
|
* Iterators cannot contain a ``return`` statement (and procs cannot contain a
|
||||||
``yield`` statement.
|
``yield`` statement).
|
||||||
* Iterators have no implicit ``result`` variable.
|
* Iterators have no implicit ``result`` variable.
|
||||||
* Iterators do not support recursion.
|
* Iterators do not support recursion.
|
||||||
* Iterators cannot be forward declared, because the compiler must be able
|
* 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
|
However, you can also use a ``closure`` iterator to get a different set of
|
||||||
restrictions. See `first class iterators <manual.html#first-class-iterators>`_
|
restrictions. See `first class iterators <manual.html#first-class-iterators>`_
|
||||||
for details. Iterators can have the same name and parameters as a proc,
|
for details. Iterators can have the same name and parameters as a proc, since
|
||||||
essentially they have their own namespace. Therefore it is common practice to
|
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
|
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
|
iterator and return it as a sequence, like ``split`` from the `strutils module
|
||||||
<strutils.html>`_.
|
<strutils.html>`_.
|
||||||
|
|
@ -882,13 +882,13 @@ that are available for them in detail.
|
||||||
Booleans
|
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,
|
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
|
The operators ``not, and, or, xor, <, <=, >, >=, !=, ==`` are defined
|
||||||
for the bool type. The ``and`` and ``or`` operators perform short-cut
|
for the bool type. The ``and`` and ``or`` operators perform short-circuit
|
||||||
evaluation. Example:
|
evaluation. For example:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
|
|
@ -899,8 +899,8 @@ evaluation. Example:
|
||||||
|
|
||||||
Characters
|
Characters
|
||||||
----------
|
----------
|
||||||
The `character type` is named ``char`` in Nim. Its size is one byte.
|
The `character type` is called ``char``. Its size is always one byte, so
|
||||||
Thus it cannot represent an UTF-8 character, but a part of it.
|
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 reason for this is efficiency: for the overwhelming majority of use-cases,
|
||||||
the resulting programs will still handle UTF-8 properly as UTF-8 was specially
|
the resulting programs will still handle UTF-8 properly as UTF-8 was specially
|
||||||
designed for this.
|
designed for this.
|
||||||
|
|
@ -914,11 +914,11 @@ Converting from an integer to a ``char`` is done with the ``chr`` proc.
|
||||||
|
|
||||||
Strings
|
Strings
|
||||||
-------
|
-------
|
||||||
String variables in Nim are **mutable**, so appending to a string
|
String variables are **mutable**, so appending to a string
|
||||||
is quite efficient. Strings in Nim are both zero-terminated and have a
|
is possible, and quite efficient. Strings in Nim are both zero-terminated and have a
|
||||||
length field. One can retrieve a string's length with the builtin ``len``
|
length field. A string's length can be retrieved with the builtin ``len``
|
||||||
procedure; the length never counts the terminating zero. Accessing the
|
procedure; the length never counts the terminating zero. Accessing the
|
||||||
terminating zero is no error and often leads to simpler code:
|
terminating zero is not an error and often leads to simpler code:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
if s[i] == 'a' and s[i+1] == 'b':
|
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 ``&``
|
The assignment operator for strings copies the string. You can use the ``&``
|
||||||
operator to concatenate strings and ``add`` to append to a string.
|
operator to concatenate strings and ``add`` to append to a string.
|
||||||
|
|
||||||
Strings are compared by their lexicographical order. All comparison operators
|
Strings are compared using their lexicographical order. All the comparison operators
|
||||||
are available. Per convention, all strings are UTF-8 strings, but this is not
|
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
|
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
|
a sequence of bytes. The index operation ``s[i]`` means the i-th *char* of
|
||||||
``s``, not the i-th *unichar*.
|
``s``, not the i-th *unichar*.
|
||||||
|
|
||||||
String variables are initialized with a special value, called ``nil``. However,
|
String variables are initialized with a special value, called ``nil``. However,
|
||||||
most string operations cannot deal with ``nil`` (leading to an exception being
|
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
|
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.
|
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``.
|
``int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64``.
|
||||||
|
|
||||||
The default integer type is ``int``. Integer literals can have a *type suffix*
|
The default integer type is ``int``. Integer literals can have a *type suffix*
|
||||||
to mark them to be of another integer type:
|
to specify a non-default integer type:
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: nim
|
.. 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.
|
``int`` has the same size as a pointer.
|
||||||
|
|
||||||
The common operators ``+ - * div mod < <= == != > >=`` are defined for
|
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
|
provide *bitwise* operations. Left bit shifting is done with the ``shl``, right
|
||||||
shifting with the ``shr`` operator. Bit shifting operators always treat their
|
shifting with the ``shr`` operator. Bit shifting operators always treat their
|
||||||
arguments as *unsigned*. For `arithmetic bit shifts`:idx: ordinary
|
arguments as *unsigned*. For `arithmetic bit shifts`:idx: ordinary
|
||||||
multiplication or division can be used.
|
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.
|
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
|
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).
|
cannot be detected at compile time).
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -981,9 +981,9 @@ Floats
|
||||||
Nim has these floating point types built-in: ``float float32 float64``.
|
Nim has these floating point types built-in: ``float float32 float64``.
|
||||||
|
|
||||||
The default float type is ``float``. In the current implementation,
|
The default float type is ``float``. In the current implementation,
|
||||||
``float`` is always 64 bit wide.
|
``float`` is always 64-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:
|
type:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -993,18 +993,18 @@ type:
|
||||||
z = 0.0'f64 # z is of type ``float64``
|
z = 0.0'f64 # z is of type ``float64``
|
||||||
|
|
||||||
The common operators ``+ - * / < <= == != > >=`` are defined for
|
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
|
Automatic type conversion in expressions with different kinds of floating
|
||||||
point types is performed: the smaller type is converted to the larger. Integer
|
point types is performed: the smaller type is converted to the larger. Integer
|
||||||
types are **not** converted to floating point types automatically and vice
|
types are **not** converted to floating point types automatically, nor vice
|
||||||
versa. The `toInt <system.html#toInt>`_ and `toFloat <system.html#toFloat>`_
|
versa. Use the `toInt <system.html#toInt>`_ and `toFloat <system.html#toFloat>`_
|
||||||
procs can be used for these conversions.
|
procs for these conversions.
|
||||||
|
|
||||||
|
|
||||||
Type Conversion
|
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:
|
type as a function:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -1019,9 +1019,9 @@ Internal type representation
|
||||||
============================
|
============================
|
||||||
|
|
||||||
As mentioned earlier, the built-in `$ <system.html#$>`_ (stringify) operator
|
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
|
turns any basic type into a string, which you can then print to the console
|
||||||
with the ``echo`` proc. However, advanced types, or types you may define
|
using the ``echo`` proc. However, advanced types, and your own custom types,
|
||||||
yourself won't work with the ``$`` operator until you define one for them.
|
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
|
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
|
having to write its ``$`` operator. You can use then the `repr
|
||||||
<system.html#repr>`_ proc which works with any type and even complex data
|
<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
|
biggestInt = int64 # biggest integer type that is available
|
||||||
biggestFloat = float64 # biggest float 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.
|
``type`` statement.
|
||||||
|
|
||||||
|
|
||||||
Enumerations
|
Enumerations
|
||||||
------------
|
------------
|
||||||
A variable of an enumeration type can only be assigned a value of a
|
A variable of an enumeration type can only be assigned one of the enumeration's specified values.
|
||||||
limited set. This set consists of ordered symbols. Each symbol is mapped
|
These values are a set of ordered symbols. Each symbol is mapped
|
||||||
to an integer value internally. The first symbol is represented
|
to an integer value internally. The first symbol is represented
|
||||||
at runtime by 0, the second by 1 and so on. Example:
|
at runtime by 0, the second by 1 and so on. For example:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. 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`
|
var x = south # `x` is of type `Direction`; its value is `south`
|
||||||
echo x # writes "south" to `stdout`
|
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:
|
An enumeration's symbol can be qualified to avoid ambiguities:
|
||||||
``Direction.south``.
|
``Direction.south``.
|
||||||
|
|
||||||
The ``$`` operator can convert any enumeration value to its name, the ``ord``
|
The ``$`` operator can convert any enumeration value to its name, and the ``ord``
|
||||||
proc to its underlying integer value.
|
proc can convert it to its underlying integer value.
|
||||||
|
|
||||||
For better interfacing to other programming languages, the symbols of enum
|
For better interfacing to other programming languages, the symbols of enum
|
||||||
types can be assigned an explicit ordinal value. However, the ordinal values
|
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.
|
explicitly given is assigned the value of the previous symbol + 1.
|
||||||
|
|
||||||
An explicit ordered enum can have *holes*:
|
An explicit ordered enum can have *holes*:
|
||||||
|
|
@ -1142,8 +1142,8 @@ subrange types (and vice versa) are allowed.
|
||||||
|
|
||||||
The ``system`` module defines the important `Natural <system.html#Natural>`_
|
The ``system`` module defines the important `Natural <system.html#Natural>`_
|
||||||
type as ``range[0..high(int)]`` (`high <system.html#high>`_ returns the
|
type as ``range[0..high(int)]`` (`high <system.html#high>`_ returns the
|
||||||
maximal value). Other programming languages mandate the usage of unsigned
|
maximal value). Other programming languages may suggest the use of unsigned
|
||||||
integers for natural numbers. This is often **wrong**: you don't want 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.
|
arithmetic (which wraps around) just because the numbers cannot be negative.
|
||||||
Nim's ``Natural`` type helps to avoid this common programming error.
|
Nim's ``Natural`` type helps to avoid this common programming error.
|
||||||
|
|
||||||
|
|
@ -1156,9 +1156,9 @@ Sets
|
||||||
Arrays
|
Arrays
|
||||||
------
|
------
|
||||||
An array is a simple fixed length container. Each element in
|
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
|
.. code-block:: nim
|
||||||
|
|
||||||
|
|
@ -1222,7 +1222,7 @@ subdivided in height levels accessed through their integer index:
|
||||||
#tower[0][1] = on
|
#tower[0][1] = on
|
||||||
|
|
||||||
Note how the built-in ``len`` proc returns only the array's first dimension
|
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``
|
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:
|
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
|
type
|
||||||
LightTower = array[1..10, array[north..west, BlinkLights]]
|
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:
|
to specify a range from zero to the specified index minus one:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -1288,8 +1288,8 @@ value. Here the ``for`` statement is looping over the results from the
|
||||||
<system.html>`_ module. Examples:
|
<system.html>`_ module. Examples:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
for i in @[3, 4, 5]:
|
for value in @[3, 4, 5]:
|
||||||
echo i
|
echo value
|
||||||
# --> 3
|
# --> 3
|
||||||
# --> 4
|
# --> 4
|
||||||
# --> 5
|
# --> 5
|
||||||
|
|
@ -1320,7 +1320,7 @@ type does not matter.
|
||||||
|
|
||||||
fruits = @[] # creates an empty sequence on the heap that will be referenced by 'fruits'
|
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("Banana") # sequence 'fruits' is dynamically expandable during runtime
|
||||||
fruits.add("Mango")
|
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
|
The assignment operator for tuples copies each component. The notation
|
||||||
``t.field`` is used to access a tuple's field. Another notation is
|
``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.
|
integer.
|
||||||
|
|
||||||
.. code-block:: nim
|
.. 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
|
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
|
variables. An example of this is the `splitFile <os.html#splitFile>`_ proc
|
||||||
from the `os module <os.html>`_ which returns the directory, name and
|
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
|
extension of a path at the same time. For tuple unpacking to work you must
|
||||||
use parenthesis around the values you want to assign the unpacking to,
|
use parentheses around the values you want to assign the unpacking to,
|
||||||
otherwise you will be assigning the same value to all the individual
|
otherwise you will be assigning the same value to all the individual
|
||||||
variables! Example:
|
variables! For example:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. 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.
|
Nim distinguishes between `traced`:idx: and `untraced`:idx: references.
|
||||||
Untraced references are also called *pointers*. Traced references point to
|
Untraced references are also called *pointers*. Traced references point to
|
||||||
objects of a garbage collected heap, untraced references point to
|
objects in a garbage collected heap, untraced references point to
|
||||||
manually allocated objects or to objects somewhere else in memory. Thus
|
manually allocated objects or to objects elsewhere in memory. Thus
|
||||||
untraced references are *unsafe*. However for certain low-level operations
|
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.
|
are declared with the **ptr** keyword.
|
||||||
|
|
||||||
The empty ``[]`` subscript notation can be used to *derefer* a reference,
|
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
|
n.data = 9
|
||||||
# no need to write n[].data; in fact n[].data is highly discouraged!
|
# 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
|
To deal with untraced memory, the procedures ``alloc``, ``dealloc`` and
|
||||||
``realloc`` can be used. The documentation of the `system <system.html>`_
|
``realloc`` can be used. The `system <system.html>`_
|
||||||
module contains further information.
|
module's documentation contains further details.
|
||||||
|
|
||||||
If a reference points to *nothing*, it has the value ``nil``.
|
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
|
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".
|
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.
|
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.
|
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>`_.
|
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.
|
Nim supports splitting a program into pieces with a module concept.
|
||||||
Each module is in its own file. Modules enable `information hiding`:idx: and
|
Each module is in its own file. Modules enable `information hiding`:idx: and
|
||||||
`separate compilation`:idx:. A module may gain access to symbols of another
|
`separate compilation`:idx:. A module may gain access to the symbols of another
|
||||||
module by the `import`:idx: statement. Only top-level symbols that are marked
|
module by using the `import`:idx: statement. Only top-level symbols that are marked
|
||||||
with an asterisk (``*``) are exported:
|
with an asterisk (``*``) are exported:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -1585,7 +1585,7 @@ with an asterisk (``*``) are exported:
|
||||||
|
|
||||||
The above module exports ``x`` and ``*``, but not ``y``.
|
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.
|
This can be used to initialize complex data structures for example.
|
||||||
|
|
||||||
Each module has a special magic constant ``isMainModule`` that is true if the
|
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
|
result = x + 1
|
||||||
|
|
||||||
|
|
||||||
A symbol of a module *can* be *qualified* with the ``module.symbol`` syntax. If
|
A symbol of a module *can* be *qualified* with the ``module.symbol`` syntax. And if
|
||||||
the symbol is ambiguous, it even *has* to be qualified. A symbol is ambiguous
|
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
|
if it is defined in two (or more) different modules and both modules are
|
||||||
imported by a third one:
|
imported by a third one:
|
||||||
|
|
||||||
|
|
@ -1642,7 +1642,7 @@ imported by a third one:
|
||||||
# Module C
|
# Module C
|
||||||
import A, B
|
import A, B
|
||||||
write(stdout, x) # error: x is ambiguous
|
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
|
var x = 4
|
||||||
write(stdout, x) # not ambiguous: uses the module C's x
|
write(stdout, x) # not ambiguous: uses the module C's x
|
||||||
|
|
|
||||||
9
koch.nim
9
koch.nim
|
|
@ -512,6 +512,14 @@ proc pushCsources() =
|
||||||
finally:
|
finally:
|
||||||
setCurrentDir(cwd)
|
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() =
|
proc showHelp() =
|
||||||
quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)),
|
quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)),
|
||||||
CompileDate, CompileTime], QuitSuccess)
|
CompileDate, CompileTime], QuitSuccess)
|
||||||
|
|
@ -548,5 +556,6 @@ of cmdArgument:
|
||||||
of "nimsuggest": bundleNimsuggest(buildExe=true)
|
of "nimsuggest": bundleNimsuggest(buildExe=true)
|
||||||
of "tools": buildTools(existsDir(".git"))
|
of "tools": buildTools(existsDir(".git"))
|
||||||
of "pushcsource", "pushcsources": pushCsources()
|
of "pushcsource", "pushcsources": pushCsources()
|
||||||
|
of "valgrind": valgrind(op.cmdLineRest)
|
||||||
else: showHelp()
|
else: showHelp()
|
||||||
of cmdEnd: showHelp()
|
of cmdEnd: showHelp()
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,6 @@
|
||||||
## <manual.html#anonymous-procs>`_ to procs like ``filter`` to reduce typing.
|
## <manual.html#anonymous-procs>`_ to procs like ``filter`` to reduce typing.
|
||||||
## Anonymous procs can use `the special do notation <manual.html#do-notation>`_
|
## Anonymous procs can use `the special do notation <manual.html#do-notation>`_
|
||||||
## which is more convenient in certain situations.
|
## 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"
|
include "system/inclrtl"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,8 @@ proc unmapMem*(f: var MemFile, p: pointer, size: int) =
|
||||||
|
|
||||||
|
|
||||||
proc open*(filename: string, mode: FileMode = fmRead,
|
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.
|
## opens a memory mapped file. If this fails, ``EOS`` is raised.
|
||||||
##
|
##
|
||||||
## ``newFileSize`` can only be set if the file does not exist and is opened
|
## ``newFileSize`` can only be set if the file does not exist and is opened
|
||||||
|
|
@ -95,6 +96,9 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||||
## ``offset`` must be multiples of the PAGE SIZE of your OS
|
## ``offset`` must be multiples of the PAGE SIZE of your OS
|
||||||
## (usually 4K or 8K but is unique to 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:
|
## Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
|
|
@ -189,11 +193,14 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||||
else: result.size = fileSize.int
|
else: result.size = fileSize.int
|
||||||
|
|
||||||
result.wasOpened = true
|
result.wasOpened = true
|
||||||
|
if not allowRemap and result.fHandle != INVALID_HANDLE_VALUE:
|
||||||
|
if closeHandle(result.fHandle) == 0:
|
||||||
|
result.fHandle = INVALID_HANDLE_VALUE
|
||||||
|
|
||||||
else:
|
else:
|
||||||
template fail(errCode: OSErrorCode, msg: expr) =
|
template fail(errCode: OSErrorCode, msg: expr) =
|
||||||
rollback()
|
rollback()
|
||||||
if result.handle != 0: discard close(result.handle)
|
if result.handle != -1: discard close(result.handle)
|
||||||
raiseOSError(errCode)
|
raiseOSError(errCode)
|
||||||
|
|
||||||
var flags = if readonly: O_RDONLY else: O_RDWR
|
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):
|
if result.mem == cast[pointer](MAP_FAILED):
|
||||||
fail(osLastError(), "file mapping 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) =
|
proc close*(f: var MemFile) =
|
||||||
## closes the memory mapped file `f`. All changes are written back to the
|
## closes the memory mapped file `f`. All changes are written back to the
|
||||||
## file system, if `f` was opened with write access.
|
## file system, if `f` was opened with write access.
|
||||||
|
|
@ -244,15 +255,16 @@ proc close*(f: var MemFile) =
|
||||||
var lastErr: OSErrorCode
|
var lastErr: OSErrorCode
|
||||||
|
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
if f.fHandle != INVALID_HANDLE_VALUE and f.wasOpened:
|
if f.wasOpened:
|
||||||
error = unmapViewOfFile(f.mem) == 0
|
error = unmapViewOfFile(f.mem) == 0
|
||||||
lastErr = osLastError()
|
lastErr = osLastError()
|
||||||
error = (closeHandle(f.mapHandle) == 0) or error
|
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:
|
else:
|
||||||
if f.handle != 0:
|
error = munmap(f.mem, f.size) != 0
|
||||||
error = munmap(f.mem, f.size) != 0
|
lastErr = osLastError()
|
||||||
lastErr = osLastError()
|
if f.handle != -1:
|
||||||
error = (close(f.handle) != 0) or error
|
error = (close(f.handle) != 0) or error
|
||||||
|
|
||||||
f.size = 0
|
f.size = 0
|
||||||
|
|
@ -263,7 +275,7 @@ proc close*(f: var MemFile) =
|
||||||
f.mapHandle = 0
|
f.mapHandle = 0
|
||||||
f.wasOpened = false
|
f.wasOpened = false
|
||||||
else:
|
else:
|
||||||
f.handle = 0
|
f.handle = -1
|
||||||
|
|
||||||
if error: raiseOSError(lastErr)
|
if error: raiseOSError(lastErr)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ when not declared(getEnv) or defined(nimscript):
|
||||||
WriteEnvEffect* = object of WriteIOEffect ## effect that denotes a write
|
WriteEnvEffect* = object of WriteIOEffect ## effect that denotes a write
|
||||||
## to an environment variable
|
## to an environment variable
|
||||||
|
|
||||||
ReadDirEffect* = object of ReadIOEffect ## effect that denotes a write
|
ReadDirEffect* = object of ReadIOEffect ## effect that denotes a read
|
||||||
## operation to the directory
|
## operation from the directory
|
||||||
## structure
|
## structure
|
||||||
WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write
|
WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write
|
||||||
## operation to
|
## operation to
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,8 @@
|
||||||
## var msg = createMessage("Hello from Nim's SMTP",
|
## var msg = createMessage("Hello from Nim's SMTP",
|
||||||
## "Hello!.\n Is this awesome or what?",
|
## "Hello!.\n Is this awesome or what?",
|
||||||
## @["foo@gmail.com"])
|
## @["foo@gmail.com"])
|
||||||
## var 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.auth("username", "password")
|
||||||
## smtpConn.sendmail("username@gmail.com", @["foo@gmail.com"], $msg)
|
## smtpConn.sendmail("username@gmail.com", @["foo@gmail.com"], $msg)
|
||||||
##
|
##
|
||||||
|
|
@ -34,10 +35,6 @@ import asyncnet, asyncdispatch
|
||||||
export Port
|
export Port
|
||||||
|
|
||||||
type
|
type
|
||||||
Smtp* = object
|
|
||||||
sock: Socket
|
|
||||||
debug: bool
|
|
||||||
|
|
||||||
Message* = object
|
Message* = object
|
||||||
msgTo: seq[string]
|
msgTo: seq[string]
|
||||||
msgCc: seq[string]
|
msgCc: seq[string]
|
||||||
|
|
@ -47,37 +44,29 @@ type
|
||||||
|
|
||||||
ReplyError* = object of IOError
|
ReplyError* = object of IOError
|
||||||
|
|
||||||
AsyncSmtp* = ref object
|
SmtpBase[SocketType] = ref object
|
||||||
sock: AsyncSocket
|
sock: SocketType
|
||||||
address: string
|
|
||||||
port: Port
|
|
||||||
useSsl: bool
|
|
||||||
debug: bool
|
debug: bool
|
||||||
|
|
||||||
|
Smtp* = SmtpBase[Socket]
|
||||||
|
AsyncSmtp* = SmtpBase[AsyncSocket]
|
||||||
|
|
||||||
{.deprecated: [EInvalidReply: ReplyError, TMessage: Message, TSMTP: Smtp].}
|
{.deprecated: [EInvalidReply: ReplyError, TMessage: Message, TSMTP: Smtp].}
|
||||||
|
|
||||||
proc debugSend(smtp: Smtp, cmd: string) =
|
proc debugSend(smtp: Smtp | AsyncSmtp, cmd: string) {.multisync.} =
|
||||||
if smtp.debug:
|
if smtp.debug:
|
||||||
echo("C:" & cmd)
|
echo("C:" & cmd)
|
||||||
smtp.sock.send(cmd)
|
await smtp.sock.send(cmd)
|
||||||
|
|
||||||
proc debugRecv(smtp: var Smtp): TaintedString =
|
|
||||||
var line = TaintedString""
|
|
||||||
smtp.sock.readLine(line)
|
|
||||||
|
|
||||||
|
proc debugRecv(smtp: Smtp | AsyncSmtp): Future[TaintedString] {.multisync.} =
|
||||||
|
result = await smtp.sock.recvLine()
|
||||||
if smtp.debug:
|
if smtp.debug:
|
||||||
echo("S:" & line.string)
|
echo("S:" & result.string)
|
||||||
return line
|
|
||||||
|
|
||||||
proc quitExcpt(smtp: Smtp, msg: string) =
|
proc quitExcpt(smtp: Smtp, msg: string) =
|
||||||
smtp.debugSend("QUIT")
|
smtp.debugSend("QUIT")
|
||||||
raise newException(ReplyError, msg)
|
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)
|
const compiledWithSsl = defined(ssl)
|
||||||
|
|
||||||
when not defined(ssl):
|
when not defined(ssl):
|
||||||
|
|
@ -86,63 +75,6 @@ when not defined(ssl):
|
||||||
else:
|
else:
|
||||||
let defaultSSLContext = newContext(verifyMode = CVerifyNone)
|
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],
|
proc createMessage*(mSubject, mBody: string, mTo, mCc: seq[string],
|
||||||
otherHeaders: openarray[tuple[name, value: string]]): Message =
|
otherHeaders: openarray[tuple[name, value: string]]): Message =
|
||||||
## Creates a new MIME compliant message.
|
## Creates a new MIME compliant message.
|
||||||
|
|
@ -178,81 +110,94 @@ proc `$`*(msg: Message): string =
|
||||||
result.add("\c\L")
|
result.add("\c\L")
|
||||||
result.add(msg.msgBody)
|
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 =
|
sslContext = defaultSslContext): AsyncSmtp =
|
||||||
## Creates a new ``AsyncSmtp`` instance.
|
## Creates a new ``AsyncSmtp`` instance.
|
||||||
new result
|
new result
|
||||||
result.address = address
|
result.debug = debug
|
||||||
result.port = port
|
|
||||||
result.useSsl = useSsl
|
|
||||||
|
|
||||||
result.sock = newAsyncSocket()
|
result.sock = newAsyncSocket()
|
||||||
if useSsl:
|
if useSsl:
|
||||||
when compiledWithSsl:
|
when compiledWithSsl:
|
||||||
sslContext.wrapSocket(result.sock)
|
sslContext.wrapSocket(result.sock)
|
||||||
else:
|
else:
|
||||||
raise newException(ESystem,
|
raise newException(SystemError,
|
||||||
"SMTP module compiled without SSL support")
|
"SMTP module compiled without SSL support")
|
||||||
|
|
||||||
proc quitExcpt(smtp: AsyncSmtp, msg: string): Future[void] =
|
proc quitExcpt(smtp: AsyncSmtp, msg: string): Future[void] =
|
||||||
var retFuture = newFuture[void]()
|
var retFuture = newFuture[void]()
|
||||||
var sendFut = smtp.sock.send("QUIT")
|
var sendFut = smtp.debugSend("QUIT")
|
||||||
sendFut.callback =
|
sendFut.callback =
|
||||||
proc () =
|
proc () =
|
||||||
# TODO: Fix this in async procs.
|
# TODO: Fix this in async procs.
|
||||||
raise newException(ReplyError, msg)
|
raise newException(ReplyError, msg)
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
proc checkReply(smtp: AsyncSmtp, reply: string) {.async.} =
|
proc checkReply(smtp: Smtp | AsyncSmtp, reply: string) {.multisync.} =
|
||||||
var line = await smtp.sock.recvLine()
|
var line = await smtp.debugRecv()
|
||||||
if not line.string.startswith(reply):
|
if not line.startswith(reply):
|
||||||
await quitExcpt(smtp, "Expected " & reply & " reply, got: " & line.string)
|
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.
|
## Establishes a connection with a SMTP server.
|
||||||
## May fail with ReplyError or with a socket error.
|
## 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.checkReply("220")
|
||||||
await smtp.sock.send("HELO " & smtp.address & "\c\L")
|
await smtp.debugSend("HELO " & address & "\c\L")
|
||||||
await smtp.checkReply("250")
|
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`
|
## Sends an AUTH command to the server to login as the `username`
|
||||||
## using `password`.
|
## using `password`.
|
||||||
## May fail with ReplyError.
|
## 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:"
|
await smtp.checkReply("334") # TODO: Check whether it's asking for the "Username:"
|
||||||
# i.e "334 VXNlcm5hbWU6"
|
# 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.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.
|
await smtp.checkReply("235") # Check whether the authentification was successful.
|
||||||
|
|
||||||
proc sendMail*(smtp: AsyncSmtp, fromAddr: string,
|
proc sendMail*(smtp: Smtp | AsyncSmtp, fromAddr: string,
|
||||||
toAddrs: seq[string], msg: string) {.async.} =
|
toAddrs: seq[string], msg: string) {.multisync.} =
|
||||||
## Sends ``msg`` from ``fromAddr`` to the addresses specified in ``toAddrs``.
|
## Sends ``msg`` from ``fromAddr`` to the addresses specified in ``toAddrs``.
|
||||||
## Messages may be formed using ``createMessage`` by converting the
|
## Messages may be formed using ``createMessage`` by converting the
|
||||||
## Message into a string.
|
## 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")
|
await smtp.checkReply("250")
|
||||||
for address in items(toAddrs):
|
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")
|
await smtp.checkReply("250")
|
||||||
|
|
||||||
# Send the message
|
# Send the message
|
||||||
await smtp.sock.send("DATA " & "\c\L")
|
await smtp.debugSend("DATA " & "\c\L")
|
||||||
await smtp.checkReply("354")
|
await smtp.checkReply("354")
|
||||||
await smtp.sock.send(msg & "\c\L")
|
await smtp.sock.send(msg & "\c\L")
|
||||||
await smtp.sock.send(".\c\L")
|
await smtp.debugSend(".\c\L")
|
||||||
await smtp.checkReply("250")
|
await smtp.checkReply("250")
|
||||||
|
|
||||||
proc close*(smtp: AsyncSmtp) {.async.} =
|
proc close*(smtp: Smtp | AsyncSmtp) {.multisync.} =
|
||||||
## Disconnects from the SMTP server and closes the socket.
|
## 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()
|
smtp.sock.close()
|
||||||
|
|
||||||
when not defined(testing) and isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
|
|
@ -278,25 +223,24 @@ when not defined(testing) and isMainModule:
|
||||||
|
|
||||||
proc async_test() {.async.} =
|
proc async_test() {.async.} =
|
||||||
let client = newAsyncSmtp(
|
let client = newAsyncSmtp(
|
||||||
conf["smtphost"],
|
conf["use_tls"].parseBool,
|
||||||
conf["port"].parseInt.Port,
|
debug=true
|
||||||
conf["use_tls"].parseBool
|
|
||||||
)
|
)
|
||||||
await client.connect()
|
await client.connect(conf["smtphost"], conf["port"].parseInt.Port)
|
||||||
await client.auth(conf["username"], conf["password"])
|
await client.auth(conf["username"], conf["password"])
|
||||||
await client.sendMail(conf["sender"], @[conf["recipient"]], $msg)
|
await client.sendMail(conf["sender"], @[conf["recipient"]], $msg)
|
||||||
await client.close()
|
await client.close()
|
||||||
echo "async email sent"
|
echo "async email sent"
|
||||||
|
|
||||||
proc sync_test() =
|
proc sync_test() =
|
||||||
var smtpConn = connect(
|
var smtpConn = newSmtp(
|
||||||
conf["smtphost"],
|
|
||||||
conf["port"].parseInt.Port,
|
|
||||||
conf["use_tls"].parseBool,
|
conf["use_tls"].parseBool,
|
||||||
true, # debug
|
debug=true
|
||||||
)
|
)
|
||||||
|
smtpConn.connect(conf["smtphost"], conf["port"].parseInt.Port)
|
||||||
smtpConn.auth(conf["username"], conf["password"])
|
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"
|
echo "sync email sent"
|
||||||
|
|
||||||
waitFor async_test()
|
waitFor async_test()
|
||||||
|
|
|
||||||
|
|
@ -417,7 +417,7 @@ type
|
||||||
## Base exception class.
|
## Base exception class.
|
||||||
##
|
##
|
||||||
## Each exception has to inherit from `Exception`. See the full `exception
|
## 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)
|
parent*: ref Exception ## parent exception (can be used as a stack)
|
||||||
name*: cstring ## The exception's name is its Nim identifier.
|
name*: cstring ## The exception's name is its Nim identifier.
|
||||||
## This field is filled automatically in the
|
## This field is filled automatically in the
|
||||||
|
|
@ -430,51 +430,51 @@ type
|
||||||
SystemError* = object of Exception ## \
|
SystemError* = object of Exception ## \
|
||||||
## Abstract class for exceptions that the runtime system raises.
|
## 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 ## \
|
IOError* = object of SystemError ## \
|
||||||
## Raised if an IO error occurred.
|
## 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 ## \
|
EOFError* = object of IOError ## \
|
||||||
## Raised if an IO "end of file" error occurred.
|
## 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 ## \
|
OSError* = object of SystemError ## \
|
||||||
## Raised if an operating system service failed.
|
## 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.
|
errorCode*: int32 ## OS-defined error code describing this error.
|
||||||
LibraryError* = object of OSError ## \
|
LibraryError* = object of OSError ## \
|
||||||
## Raised if a dynamic library could not be loaded.
|
## 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 ## \
|
ResourceExhaustedError* = object of SystemError ## \
|
||||||
## Raised if a resource request could not be fulfilled.
|
## 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 ## \
|
ArithmeticError* = object of Exception ## \
|
||||||
## Raised if any kind of arithmetic error occurred.
|
## 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 ## \
|
DivByZeroError* = object of ArithmeticError ## \
|
||||||
## Raised for runtime integer divide-by-zero errors.
|
## 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 ## \
|
OverflowError* = object of ArithmeticError ## \
|
||||||
## Raised for runtime integer overflows.
|
## Raised for runtime integer overflows.
|
||||||
##
|
##
|
||||||
## This happens for calculations whose results are too large to fit in the
|
## 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 ## \
|
AccessViolationError* = object of Exception ## \
|
||||||
## Raised for invalid memory access errors
|
## 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 ## \
|
AssertionError* = object of Exception ## \
|
||||||
## Raised when assertion is proved wrong.
|
## Raised when assertion is proved wrong.
|
||||||
##
|
##
|
||||||
## Usually the result of using the `assert() template <#assert>`_. See the
|
## 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 ## \
|
ValueError* = object of Exception ## \
|
||||||
## Raised for string and object conversion errors.
|
## Raised for string and object conversion errors.
|
||||||
KeyError* = object of ValueError ## \
|
KeyError* = object of ValueError ## \
|
||||||
|
|
@ -482,66 +482,66 @@ type
|
||||||
##
|
##
|
||||||
## Mostly used by the `tables <tables.html>`_ module, it can also be raised
|
## Mostly used by the `tables <tables.html>`_ module, it can also be raised
|
||||||
## by other collection modules like `sets <sets.html>`_ or `strtabs
|
## 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 ## \
|
OutOfMemError* = object of SystemError ## \
|
||||||
## Raised for unsuccessful attempts to allocate memory.
|
## 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 ## \
|
IndexError* = object of Exception ## \
|
||||||
## Raised if an array index is out of bounds.
|
## 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 ## \
|
FieldError* = object of Exception ## \
|
||||||
## Raised if a record field is not accessible because its dicriminant's
|
## Raised if a record field is not accessible because its dicriminant's
|
||||||
## value does not fit.
|
## value does not fit.
|
||||||
##
|
##
|
||||||
## See the full `exception hierarchy`_.
|
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||||
RangeError* = object of Exception ## \
|
RangeError* = object of Exception ## \
|
||||||
## Raised if a range check error occurred.
|
## 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 ## \
|
StackOverflowError* = object of SystemError ## \
|
||||||
## Raised if the hardware stack used for subroutine calls overflowed.
|
## 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 ## \
|
ReraiseError* = object of Exception ## \
|
||||||
## Raised if there is no exception to reraise.
|
## 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 ## \
|
ObjectAssignmentError* = object of Exception ## \
|
||||||
## Raised if an object gets assigned to its parent's object.
|
## 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 ## \
|
ObjectConversionError* = object of Exception ## \
|
||||||
## Raised if an object is converted to an incompatible object type.
|
## Raised if an object is converted to an incompatible object type.
|
||||||
## You can use ``of`` operator to check if conversion will succeed.
|
## 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 ## \
|
FloatingPointError* = object of Exception ## \
|
||||||
## Base class for floating point exceptions.
|
## 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 ## \
|
FloatInvalidOpError* = object of FloatingPointError ## \
|
||||||
## Raised by invalid operations according to IEEE.
|
## Raised by invalid operations according to IEEE.
|
||||||
##
|
##
|
||||||
## Raised by ``0.0/0.0``, for example. See the full `exception
|
## Raised by ``0.0/0.0``, for example. See the full `exception
|
||||||
## hierarchy`_.
|
## hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||||
FloatDivByZeroError* = object of FloatingPointError ## \
|
FloatDivByZeroError* = object of FloatingPointError ## \
|
||||||
## Raised by division by zero.
|
## Raised by division by zero.
|
||||||
##
|
##
|
||||||
## Divisor is zero and dividend is a finite nonzero number. See the full
|
## 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 ## \
|
FloatOverflowError* = object of FloatingPointError ## \
|
||||||
## Raised for overflows.
|
## Raised for overflows.
|
||||||
##
|
##
|
||||||
## The operation produced a result that exceeds the range of the exponent.
|
## 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 ## \
|
FloatUnderflowError* = object of FloatingPointError ## \
|
||||||
## Raised for underflows.
|
## Raised for underflows.
|
||||||
##
|
##
|
||||||
## The operation produced a result that is too small to be represented as a
|
## 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 ## \
|
FloatInexactError* = object of FloatingPointError ## \
|
||||||
## Raised for inexact results.
|
## Raised for inexact results.
|
||||||
##
|
##
|
||||||
|
|
@ -549,11 +549,11 @@ type
|
||||||
## precision -- for example: ``2.0 / 3.0, log(1.1)``
|
## precision -- for example: ``2.0 / 3.0, log(1.1)``
|
||||||
##
|
##
|
||||||
## **NOTE**: Nim currently does not detect these! See the full
|
## **NOTE**: Nim currently does not detect these! See the full
|
||||||
## `exception hierarchy`_.
|
## `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||||
DeadThreadError* = object of Exception ## \
|
DeadThreadError* = object of Exception ## \
|
||||||
## Raised if it is attempted to send a message to a dead thread.
|
## 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,
|
{.deprecated: [TObject: RootObj, PObject: RootRef, TEffect: RootEffect,
|
||||||
FTime: TimeEffect, FIO: IOEffect, FReadIO: ReadIOEffect,
|
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
|
import
|
||||||
macros, strutils
|
macros, strutils
|
||||||
|
|
||||||
macro test_macro*(n: stmt): stmt {.immediate.} =
|
macro test_macro*(s: string, n: stmt): stmt {.immediate.} =
|
||||||
result = newNimNode(nnkStmtList)
|
result = newNimNode(nnkStmtList)
|
||||||
var ass : NimNode = newNimNode(nnkAsgn)
|
var ass : NimNode = newNimNode(nnkAsgn)
|
||||||
add(ass, newIdentNode("str"))
|
add(ass, newIdentNode("str"))
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ discard """
|
||||||
|
|
||||||
import macros
|
import macros
|
||||||
|
|
||||||
macro quoteWords(n: expr): expr {.immediate.} =
|
macro quoteWords(n: varargs[expr]): expr {.immediate.} =
|
||||||
let n = callsite()
|
let n = callsite()
|
||||||
result = newNimNode(nnkBracket, n)
|
result = newNimNode(nnkBracket, n)
|
||||||
for i in 1..n.len-1:
|
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()
|
mm.close()
|
||||||
|
|
||||||
# read, change
|
# 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
|
echo "Full read size: ",mm_full.size
|
||||||
p = mm_full.mapMem(fmReadWrite, 20, 0)
|
p = mm_full.mapMem(fmReadWrite, 20, 0)
|
||||||
var p2 = cast[cstring](p)
|
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
|
pointer. Now the hash is calculated from the contents of the string, assuming
|
||||||
``cstring`` is a null-terminated string. Equal ``string`` and ``cstring``
|
``cstring`` is a null-terminated string. Equal ``string`` and ``cstring``
|
||||||
values produce an equal hash value.
|
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
|
Library Additions
|
||||||
-----------------
|
-----------------
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue