mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-17 02:32:11 +00:00
Merge branch 'devel' of github.com:nim-lang/Nim into devel
This commit is contained in:
@@ -460,6 +460,8 @@ type
|
||||
# proc foo(T: typedesc, list: seq[T]): var T
|
||||
# proc foo(L: static[int]): array[L, int]
|
||||
# can be attached to ranges to indicate that the range
|
||||
# can be attached to generic procs with free standing
|
||||
# type parameters: e.g. proc foo[T]()
|
||||
# depends on unresolved static params.
|
||||
tfRetType, # marks return types in proc (used to detect type classes
|
||||
# used as return types for return type inference)
|
||||
|
||||
@@ -67,6 +67,23 @@ proc debug*(n: PSym) {.deprecated.}
|
||||
proc debug*(n: PType) {.deprecated.}
|
||||
proc debug*(n: PNode) {.deprecated.}
|
||||
|
||||
template mdbg*: bool {.dirty.} =
|
||||
when compiles(c.module):
|
||||
c.module.fileIdx == gProjectMainIdx
|
||||
elif compiles(m.c.module):
|
||||
m.c.module.fileIdx == gProjectMainIdx
|
||||
elif compiles(cl.c.module):
|
||||
cl.c.module.fileIdx == gProjectMainIdx
|
||||
elif compiles(p):
|
||||
when compiles(p.lex):
|
||||
p.lex.fileIdx == gProjectMainIdx
|
||||
else:
|
||||
p.module.module.fileIdx == gProjectMainIdx
|
||||
elif compiles(L.fileIdx):
|
||||
L.fileIdx == gProjectMainIdx
|
||||
else:
|
||||
false
|
||||
|
||||
# --------------------------- ident tables ----------------------------------
|
||||
proc idTableGet*(t: TIdTable, key: PIdObj): RootRef
|
||||
proc idTableGet*(t: TIdTable, key: int): RootRef
|
||||
|
||||
@@ -80,9 +80,14 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode =
|
||||
expectedRegularParams = <s.typ.len
|
||||
givenRegularParams = totalParams - genericParams
|
||||
if givenRegularParams < 0: givenRegularParams = 0
|
||||
|
||||
if totalParams > expectedRegularParams + genericParams:
|
||||
globalError(n.info, errWrongNumberOfArguments)
|
||||
|
||||
if totalParams < genericParams:
|
||||
globalError(n.info, errMissingGenericParamsForTemplate,
|
||||
n.renderTree)
|
||||
|
||||
result = newNodeI(nkArgList, n.info)
|
||||
for i in 1 .. givenRegularParams:
|
||||
result.addSon n.sons[i]
|
||||
|
||||
@@ -67,6 +67,10 @@ type
|
||||
TTokTypes* = set[TTokType]
|
||||
|
||||
const
|
||||
weakTokens = {tkComma, tkSemiColon, tkColon,
|
||||
tkParRi, tkParDotRi, tkBracketRi, tkBracketDotRi,
|
||||
tkCurlyRi} # \
|
||||
# tokens that should not be considered for previousToken
|
||||
tokKeywordLow* = succ(tkSymbol)
|
||||
tokKeywordHigh* = pred(tkIntLit)
|
||||
TokTypeToStr*: array[TTokType, string] = ["tkInvalid", "[EOF]",
|
||||
@@ -105,6 +109,9 @@ type
|
||||
# so that it is the correct default value
|
||||
base2, base8, base16
|
||||
|
||||
CursorPosition* {.pure.} = enum ## XXX remove this again
|
||||
None, InToken, BeforeToken, AfterToken
|
||||
|
||||
TToken* = object # a Nim token
|
||||
tokType*: TTokType # the type of the token
|
||||
indent*: int # the indentation; != -1 if the token has been
|
||||
@@ -128,8 +135,11 @@ type
|
||||
# needs so much look-ahead
|
||||
currLineIndent*: int
|
||||
strongSpaces*, allowTabs*: bool
|
||||
cursor*: CursorPosition
|
||||
errorHandler*: TErrorHandler
|
||||
cache*: IdentCache
|
||||
when defined(nimsuggest):
|
||||
previousToken: TLineInfo
|
||||
|
||||
var gLinesCompiled*: int # all lines that have been compiled
|
||||
|
||||
@@ -203,6 +213,8 @@ proc openLexer*(lex: var TLexer, fileIdx: int32, inputstream: PLLStream;
|
||||
lex.currLineIndent = 0
|
||||
inc(lex.lineNumber, inputstream.lineOffset)
|
||||
lex.cache = cache
|
||||
when defined(nimsuggest):
|
||||
lex.previousToken.fileIndex = fileIdx
|
||||
|
||||
proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream;
|
||||
cache: IdentCache) =
|
||||
@@ -235,6 +247,41 @@ proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") =
|
||||
proc matchTwoChars(L: TLexer, first: char, second: set[char]): bool =
|
||||
result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in second)
|
||||
|
||||
template tokenBegin(pos) {.dirty.} =
|
||||
when defined(nimsuggest):
|
||||
var colA = getColNumber(L, pos)
|
||||
|
||||
template tokenEnd(pos) {.dirty.} =
|
||||
when defined(nimsuggest):
|
||||
let colB = getColNumber(L, pos)+1
|
||||
if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
|
||||
L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}:
|
||||
L.cursor = CursorPosition.InToken
|
||||
gTrackPos.col = colA.int16
|
||||
colA = 0
|
||||
|
||||
template tokenEndIgnore(pos) =
|
||||
when defined(nimsuggest):
|
||||
let colB = getColNumber(L, pos)
|
||||
if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
|
||||
L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}:
|
||||
gTrackPos.fileIndex = trackPosInvalidFileIdx
|
||||
gTrackPos.line = -1
|
||||
colA = 0
|
||||
|
||||
template tokenEndPrevious(pos) =
|
||||
when defined(nimsuggest):
|
||||
# when we detect the cursor in whitespace, we attach the track position
|
||||
# to the token that came before that, but only if we haven't detected
|
||||
# the cursor in a string literal or comment:
|
||||
let colB = getColNumber(L, pos)
|
||||
if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
|
||||
L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}:
|
||||
L.cursor = CursorPosition.BeforeToken
|
||||
gTrackPos = L.previousToken
|
||||
gTrackPosAttached = true
|
||||
colA = 0
|
||||
|
||||
{.push overflowChecks: off.}
|
||||
# We need to parse the largest uint literal without overflow checks
|
||||
proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int =
|
||||
@@ -318,6 +365,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
|
||||
result.literal = ""
|
||||
result.base = base10
|
||||
startpos = L.bufpos
|
||||
tokenBegin(startPos)
|
||||
|
||||
# First stage: find out base, make verifications, build token literal string
|
||||
if L.buf[L.bufpos] == '0' and L.buf[L.bufpos + 1] in baseCodeChars + {'O'}:
|
||||
@@ -526,6 +574,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
|
||||
lexMessageLitNum(L, errInvalidNumber, startpos)
|
||||
except OverflowError, RangeError:
|
||||
lexMessageLitNum(L, errNumberOutOfRange, startpos)
|
||||
tokenEnd(postPos-1)
|
||||
L.bufpos = postPos
|
||||
|
||||
proc handleHexChar(L: var TLexer, xi: var int) =
|
||||
@@ -642,21 +691,11 @@ proc handleCRLF(L: var TLexer, pos: int): int =
|
||||
result = nimlexbase.handleLF(L, pos)
|
||||
else: result = pos
|
||||
|
||||
template tokenRange(colA, pos) =
|
||||
when defined(nimsuggest):
|
||||
let colB = getColNumber(L, pos)
|
||||
if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
|
||||
L.lineNumber == gTrackPos.line and gIdeCmd == ideSug:
|
||||
gTrackPos.fileIndex = trackPosInvalidFileIdx
|
||||
gTrackPos.line = -1
|
||||
colA = 0
|
||||
|
||||
proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
|
||||
var pos = L.bufpos + 1 # skip "
|
||||
var buf = L.buf # put `buf` in a register
|
||||
var line = L.lineNumber # save linenumber for better error message
|
||||
when defined(nimsuggest):
|
||||
var colA = getColNumber(L, pos)
|
||||
tokenBegin(pos)
|
||||
if buf[pos] == '\"' and buf[pos+1] == '\"':
|
||||
tok.tokType = tkTripleStrLit # long string literal:
|
||||
inc(pos, 2) # skip ""
|
||||
@@ -672,18 +711,18 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
|
||||
of '\"':
|
||||
if buf[pos+1] == '\"' and buf[pos+2] == '\"' and
|
||||
buf[pos+3] != '\"':
|
||||
tokenRange(colA, pos+2)
|
||||
tokenEndIgnore(pos+2)
|
||||
L.bufpos = pos + 3 # skip the three """
|
||||
break
|
||||
add(tok.literal, '\"')
|
||||
inc(pos)
|
||||
of CR, LF:
|
||||
tokenRange(colA, pos)
|
||||
tokenEndIgnore(pos)
|
||||
pos = handleCRLF(L, pos)
|
||||
buf = L.buf
|
||||
add(tok.literal, tnl)
|
||||
of nimlexbase.EndOfFile:
|
||||
tokenRange(colA, pos)
|
||||
tokenEndIgnore(pos)
|
||||
var line2 = L.lineNumber
|
||||
L.lineNumber = line
|
||||
lexMessagePos(L, errClosingTripleQuoteExpected, L.lineStart)
|
||||
@@ -704,11 +743,11 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
|
||||
inc(pos, 2)
|
||||
add(tok.literal, '"')
|
||||
else:
|
||||
tokenRange(colA, pos)
|
||||
tokenEndIgnore(pos)
|
||||
inc(pos) # skip '"'
|
||||
break
|
||||
elif c in {CR, LF, nimlexbase.EndOfFile}:
|
||||
tokenRange(colA, pos)
|
||||
tokenEndIgnore(pos)
|
||||
lexMessage(L, errClosingQuoteExpected)
|
||||
break
|
||||
elif (c == '\\') and not rawMode:
|
||||
@@ -721,6 +760,7 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
|
||||
L.bufpos = pos
|
||||
|
||||
proc getCharacter(L: var TLexer, tok: var TToken) =
|
||||
tokenBegin(L.bufpos)
|
||||
inc(L.bufpos) # skip '
|
||||
var c = L.buf[L.bufpos]
|
||||
case c
|
||||
@@ -730,12 +770,14 @@ proc getCharacter(L: var TLexer, tok: var TToken) =
|
||||
tok.literal = $c
|
||||
inc(L.bufpos)
|
||||
if L.buf[L.bufpos] != '\'': lexMessage(L, errMissingFinalQuote)
|
||||
tokenEndIgnore(L.bufpos)
|
||||
inc(L.bufpos) # skip '
|
||||
|
||||
proc getSymbol(L: var TLexer, tok: var TToken) =
|
||||
var h: Hash = 0
|
||||
var pos = L.bufpos
|
||||
var buf = L.buf
|
||||
tokenBegin(pos)
|
||||
while true:
|
||||
var c = buf[pos]
|
||||
case c
|
||||
@@ -762,6 +804,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) =
|
||||
inc(pos)
|
||||
|
||||
else: break
|
||||
tokenEnd(pos-1)
|
||||
h = !$h
|
||||
tok.ident = L.cache.getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
|
||||
L.bufpos = pos
|
||||
@@ -782,6 +825,7 @@ proc endOperator(L: var TLexer, tok: var TToken, pos: int,
|
||||
proc getOperator(L: var TLexer, tok: var TToken) =
|
||||
var pos = L.bufpos
|
||||
var buf = L.buf
|
||||
tokenBegin(pos)
|
||||
var h: Hash = 0
|
||||
while true:
|
||||
var c = buf[pos]
|
||||
@@ -789,6 +833,7 @@ proc getOperator(L: var TLexer, tok: var TToken) =
|
||||
h = h !& ord(c)
|
||||
inc(pos)
|
||||
endOperator(L, tok, pos, h)
|
||||
tokenEnd(pos-1)
|
||||
# advance pos but don't store it in L.bufpos so the next token (which might
|
||||
# be an operator too) gets the preceding spaces:
|
||||
tok.strongSpaceB = 0
|
||||
@@ -803,8 +848,7 @@ proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
|
||||
var pos = start
|
||||
var buf = L.buf
|
||||
var toStrip = 0
|
||||
when defined(nimsuggest):
|
||||
var colA = getColNumber(L, pos)
|
||||
tokenBegin(pos)
|
||||
# detect the amount of indentation:
|
||||
if isDoc:
|
||||
toStrip = getColNumber(L, pos)
|
||||
@@ -831,20 +875,20 @@ proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
|
||||
if isDoc:
|
||||
if buf[pos+1] == '#' and buf[pos+2] == '#':
|
||||
if nesting == 0:
|
||||
tokenRange(colA, pos+2)
|
||||
tokenEndIgnore(pos+2)
|
||||
inc(pos, 3)
|
||||
break
|
||||
dec nesting
|
||||
tok.literal.add ']'
|
||||
elif buf[pos+1] == '#':
|
||||
if nesting == 0:
|
||||
tokenRange(colA, pos+1)
|
||||
tokenEndIgnore(pos+1)
|
||||
inc(pos, 2)
|
||||
break
|
||||
dec nesting
|
||||
inc pos
|
||||
of CR, LF:
|
||||
tokenRange(colA, pos)
|
||||
tokenEndIgnore(pos)
|
||||
pos = handleCRLF(L, pos)
|
||||
buf = L.buf
|
||||
# strip leading whitespace:
|
||||
@@ -856,7 +900,7 @@ proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
|
||||
inc pos
|
||||
dec c
|
||||
of nimlexbase.EndOfFile:
|
||||
tokenRange(colA, pos)
|
||||
tokenEndIgnore(pos)
|
||||
lexMessagePos(L, errGenerated, pos, "end of multiline comment expected")
|
||||
break
|
||||
else:
|
||||
@@ -867,8 +911,6 @@ proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
|
||||
proc scanComment(L: var TLexer, tok: var TToken) =
|
||||
var pos = L.bufpos
|
||||
var buf = L.buf
|
||||
when defined(nimsuggest):
|
||||
var colA = getColNumber(L, pos)
|
||||
tok.tokType = tkComment
|
||||
# iNumber contains the number of '\n' in the token
|
||||
tok.iNumber = 0
|
||||
@@ -876,6 +918,7 @@ proc scanComment(L: var TLexer, tok: var TToken) =
|
||||
if buf[pos+2] == '[':
|
||||
skipMultiLineComment(L, tok, pos+3, true)
|
||||
return
|
||||
tokenBegin(pos)
|
||||
inc(pos, 2)
|
||||
|
||||
var toStrip = 0
|
||||
@@ -889,7 +932,7 @@ proc scanComment(L: var TLexer, tok: var TToken) =
|
||||
if buf[pos] == '\\': lastBackslash = pos+1
|
||||
add(tok.literal, buf[pos])
|
||||
inc(pos)
|
||||
tokenRange(colA, pos)
|
||||
tokenEndIgnore(pos)
|
||||
pos = handleCRLF(L, pos)
|
||||
buf = L.buf
|
||||
var indent = 0
|
||||
@@ -908,13 +951,14 @@ proc scanComment(L: var TLexer, tok: var TToken) =
|
||||
else:
|
||||
if buf[pos] > ' ':
|
||||
L.indentAhead = indent
|
||||
tokenRange(colA, pos)
|
||||
tokenEndIgnore(pos)
|
||||
break
|
||||
L.bufpos = pos
|
||||
|
||||
proc skip(L: var TLexer, tok: var TToken) =
|
||||
var pos = L.bufpos
|
||||
var buf = L.buf
|
||||
tokenBegin(pos)
|
||||
tok.strongSpaceA = 0
|
||||
while true:
|
||||
case buf[pos]
|
||||
@@ -925,6 +969,7 @@ proc skip(L: var TLexer, tok: var TToken) =
|
||||
if not L.allowTabs: lexMessagePos(L, errTabulatorsAreNotAllowed, pos)
|
||||
inc(pos)
|
||||
of CR, LF:
|
||||
tokenEndPrevious(pos)
|
||||
pos = handleCRLF(L, pos)
|
||||
buf = L.buf
|
||||
var indent = 0
|
||||
@@ -951,15 +996,24 @@ proc skip(L: var TLexer, tok: var TToken) =
|
||||
pos = L.bufpos
|
||||
buf = L.buf
|
||||
else:
|
||||
when defined(nimsuggest):
|
||||
var colA = getColNumber(L, pos)
|
||||
tokenBegin(pos)
|
||||
while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: inc(pos)
|
||||
tokenRange(colA, pos)
|
||||
tokenEndIgnore(pos+1)
|
||||
else:
|
||||
break # EndOfFile also leaves the loop
|
||||
tokenEndPrevious(pos-1)
|
||||
L.bufpos = pos
|
||||
|
||||
proc rawGetTok*(L: var TLexer, tok: var TToken) =
|
||||
template atTokenEnd() {.dirty.} =
|
||||
when defined(nimsuggest):
|
||||
# we attach the cursor to the last *strong* token
|
||||
if tok.tokType notin weakTokens:
|
||||
L.previousToken.line = tok.line.int16
|
||||
L.previousToken.col = tok.col.int16
|
||||
|
||||
when defined(nimsuggest):
|
||||
L.cursor = CursorPosition.None
|
||||
fillToken(tok)
|
||||
if L.indentAhead >= 0:
|
||||
tok.indent = L.indentAhead
|
||||
@@ -1007,6 +1061,10 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
|
||||
inc(L.bufpos)
|
||||
else:
|
||||
tok.tokType = tkParLe
|
||||
when defined(nimsuggest):
|
||||
if L.fileIdx == gTrackPos.fileIndex and tok.col < gTrackPos.col and
|
||||
tok.line == gTrackPos.line and gIdeCmd == ideCon:
|
||||
gTrackPos.col = tok.col.int16
|
||||
of ')':
|
||||
tok.tokType = tkParRi
|
||||
inc(L.bufpos)
|
||||
@@ -1022,10 +1080,13 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
|
||||
inc(L.bufpos)
|
||||
of '.':
|
||||
when defined(nimsuggest):
|
||||
if L.fileIdx == gTrackPos.fileIndex and tok.col == gTrackPos.col and
|
||||
if L.fileIdx == gTrackPos.fileIndex and tok.col+1 == gTrackPos.col and
|
||||
tok.line == gTrackPos.line and gIdeCmd == ideSug:
|
||||
tok.tokType = tkDot
|
||||
L.cursor = CursorPosition.InToken
|
||||
gTrackPos.col = tok.col.int16
|
||||
inc(L.bufpos)
|
||||
atTokenEnd()
|
||||
return
|
||||
if L.buf[L.bufpos+1] == ']':
|
||||
tok.tokType = tkBracketDotRi
|
||||
@@ -1092,3 +1153,4 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
|
||||
tok.tokType = tkInvalid
|
||||
lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')')
|
||||
inc(L.bufpos)
|
||||
atTokenEnd()
|
||||
|
||||
@@ -64,6 +64,8 @@ type
|
||||
errVarForOutParamNeeded,
|
||||
errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX,
|
||||
errAmbiguousCallXYZ, errWrongNumberOfArguments,
|
||||
errWrongNumberOfArgumentsInCall,
|
||||
errMissingGenericParamsForTemplate,
|
||||
errXCannotBePassedToProcVar,
|
||||
errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProcX, errImplOfXNotAllowed,
|
||||
errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX,
|
||||
@@ -89,6 +91,7 @@ type
|
||||
errMainModuleMustBeSpecified,
|
||||
errXExpected,
|
||||
errTIsNotAConcreteType,
|
||||
errCastToANonConcreteType,
|
||||
errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,
|
||||
errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
|
||||
errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly,
|
||||
@@ -107,6 +110,7 @@ type
|
||||
errCannotInferTypeOfTheLiteral,
|
||||
errCannotInferReturnType,
|
||||
errGenericLambdaNotAllowed,
|
||||
errProcHasNoConcreteType,
|
||||
errCompilerDoesntSupportTarget,
|
||||
errUser,
|
||||
warnCannotOpenFile,
|
||||
@@ -269,6 +273,8 @@ const
|
||||
errButExpectedX: "but expected \'$1\'",
|
||||
errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3",
|
||||
errWrongNumberOfArguments: "wrong number of arguments",
|
||||
errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'",
|
||||
errMissingGenericParamsForTemplate: "'$1' has unspecified generic parameters",
|
||||
errXCannotBePassedToProcVar: "\'$1\' cannot be passed to a procvar",
|
||||
errXCannotBeInParamDecl: "$1 cannot be declared in parameter declaration",
|
||||
errPragmaOnlyInHeaderOfProcX: "pragmas are only allowed in the header of a proc; redefinition of $1",
|
||||
@@ -326,6 +332,7 @@ const
|
||||
errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file",
|
||||
errXExpected: "\'$1\' expected",
|
||||
errTIsNotAConcreteType: "\'$1\' is not a concrete type.",
|
||||
errCastToANonConcreteType: "cannot cast to a non concrete type: \'$1\'",
|
||||
errInvalidSectionStart: "invalid section start",
|
||||
errGridTableNotImplemented: "grid table is not implemented",
|
||||
errGeneralParseError: "general parse error",
|
||||
@@ -369,6 +376,7 @@ const
|
||||
errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " &
|
||||
"it is used as an operand to another routine and the types " &
|
||||
"of the generic paramers can be inferred from the expected signature.",
|
||||
errProcHasNoConcreteType: "'$1' doesn't have a concrete type, due to unspecified generic parameters.",
|
||||
errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target",
|
||||
errUser: "$1",
|
||||
warnCannotOpenFile: "cannot open \'$1\'",
|
||||
@@ -739,6 +747,8 @@ proc `??`* (info: TLineInfo, filename: string): bool =
|
||||
const trackPosInvalidFileIdx* = -2 # special marker so that no suggestions
|
||||
# are produced within comments and string literals
|
||||
var gTrackPos*: TLineInfo
|
||||
var gTrackPosAttached*: bool ## whether the tracking position was attached to some
|
||||
## close token.
|
||||
|
||||
type
|
||||
MsgFlag* = enum ## flags altering msgWriteln behavior
|
||||
@@ -863,6 +873,9 @@ proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) =
|
||||
proc `==`*(a, b: TLineInfo): bool =
|
||||
result = a.line == b.line and a.fileIndex == b.fileIndex
|
||||
|
||||
proc exactEquals*(a, b: TLineInfo): bool =
|
||||
result = a.fileIndex == b.fileIndex and a.line == b.line and a.col == b.col
|
||||
|
||||
proc writeContext(lastinfo: TLineInfo) =
|
||||
var info = lastinfo
|
||||
for i in countup(0, len(msgContext) - 1):
|
||||
|
||||
@@ -392,6 +392,23 @@ proc findModule*(modulename, currentModule: string): string =
|
||||
result = findFile(m)
|
||||
patchModule()
|
||||
|
||||
proc findProjectNimFile*(pkg: string): string =
|
||||
const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"]
|
||||
var candidates: seq[string] = @[]
|
||||
for k, f in os.walkDir(pkg, relative=true):
|
||||
if k == pcFile and f != "config.nims":
|
||||
let (_, name, ext) = splitFile(f)
|
||||
if ext in extensions:
|
||||
let x = changeFileExt(pkg / name, ".nim")
|
||||
if fileExists(x):
|
||||
candidates.add x
|
||||
for c in candidates:
|
||||
# nim-foo foo or foo nfoo
|
||||
if (pkg in c) or (c in pkg): return c
|
||||
if candidates.len >= 1:
|
||||
return candidates[0]
|
||||
return ""
|
||||
|
||||
proc canonDynlibName(s: string): string =
|
||||
let start = if s.startsWith("lib"): 3 else: 0
|
||||
let ende = strutils.find(s, {'(', ')', '.'})
|
||||
@@ -420,11 +437,6 @@ proc binaryStrSearch*(x: openArray[string], y: string): int =
|
||||
return mid
|
||||
result = - 1
|
||||
|
||||
template nimdbg*: untyped = c.module.fileIdx == gProjectMainIdx
|
||||
template cnimdbg*: untyped = p.module.module.fileIdx == gProjectMainIdx
|
||||
template pnimdbg*: untyped = p.lex.fileIdx == gProjectMainIdx
|
||||
template lnimdbg*: untyped = L.fileIdx == gProjectMainIdx
|
||||
|
||||
proc parseIdeCmd*(s: string): IdeCmd =
|
||||
case s:
|
||||
of "sug": ideSug
|
||||
|
||||
@@ -1846,6 +1846,7 @@ proc parseTypeDef(p: var TParser): PNode =
|
||||
else:
|
||||
addSon(result, ast.emptyNode)
|
||||
if p.tok.tokType == tkEquals:
|
||||
result.info = parLineInfo(p)
|
||||
getTok(p)
|
||||
optInd(p, result)
|
||||
addSon(result, parseTypeDefAux(p))
|
||||
@@ -2002,12 +2003,12 @@ proc parseStmt(p: var TParser): PNode =
|
||||
break
|
||||
p.hasProgress = false
|
||||
var a = complexOrSimpleStmt(p)
|
||||
if not p.hasProgress and p.tok.tokType == tkEof: break
|
||||
if a.kind != nkEmpty and p.hasProgress:
|
||||
if a.kind != nkEmpty:
|
||||
addSon(result, a)
|
||||
else:
|
||||
parMessage(p, errExprExpected, p.tok)
|
||||
getTok(p)
|
||||
if not p.hasProgress and p.tok.tokType == tkEof: break
|
||||
else:
|
||||
# the case statement is only needed for better error messages:
|
||||
case p.tok.tokType
|
||||
|
||||
@@ -167,6 +167,8 @@ proc commonType*(x, y: PType): PType =
|
||||
|
||||
proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
|
||||
result = newSym(kind, considerQuotedIdent(n), getCurrOwner(c), n.info)
|
||||
when defined(nimsuggest):
|
||||
suggestDecl(c, n, result)
|
||||
|
||||
proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
|
||||
proc `$`(kind: TSymKind): string = substr(system.`$`(kind), 2).toLowerAscii
|
||||
@@ -191,6 +193,8 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
|
||||
result = newSym(kind, considerQuotedIdent(n), getCurrOwner(c), n.info)
|
||||
#if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule:
|
||||
# incl(result.flags, sfGlobal)
|
||||
when defined(nimsuggest):
|
||||
suggestDecl(c, n, result)
|
||||
|
||||
proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
|
||||
allowed: TSymFlags): PSym
|
||||
@@ -381,6 +385,13 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
|
||||
if sym == c.p.owner:
|
||||
globalError(n.info, errRecursiveDependencyX, sym.name.s)
|
||||
|
||||
let genericParams = if sfImmediate in sym.flags: 0
|
||||
else: sym.ast[genericParamsPos].len
|
||||
let suppliedParams = max(n.safeLen - 1, 0)
|
||||
|
||||
if suppliedParams < genericParams:
|
||||
globalError(n.info, errMissingGenericParamsForTemplate, n.renderTree)
|
||||
|
||||
#if c.evalContext == nil:
|
||||
# c.evalContext = c.createEvalContext(emStatic)
|
||||
result = evalMacroCall(c.module, c.cache, n, nOrig, sym)
|
||||
|
||||
@@ -411,6 +411,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
|
||||
let tm = typeRel(m, formal, arg, true)
|
||||
if tm in {isNone, isConvertible}: return nil
|
||||
var newInst = generateInstance(c, s, m.bindings, n.info)
|
||||
newInst.typ.flags.excl tfUnresolved
|
||||
markUsed(n.info, s, c.graph.usageSym)
|
||||
styleCheckUse(n.info, s)
|
||||
result = newSymNode(newInst, n.info)
|
||||
|
||||
@@ -30,6 +30,8 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
# result = errorNode(c, n)
|
||||
if result.typ != nil:
|
||||
# XXX tyGenericInst here?
|
||||
if result.typ.kind == tyProc and tfUnresolved in result.typ.flags:
|
||||
localError(n.info, errProcHasNoConcreteType, n.renderTree)
|
||||
if result.typ.kind == tyVar: result = newDeref(result)
|
||||
elif {efWantStmt, efAllowStmt} * flags != {}:
|
||||
result.typ = newTypeS(tyVoid, c)
|
||||
@@ -218,13 +220,16 @@ proc semConv(c: PContext, n: PNode): PNode =
|
||||
proc semCast(c: PContext, n: PNode): PNode =
|
||||
## Semantically analyze a casting ("cast[type](param)")
|
||||
checkSonsLen(n, 2)
|
||||
let targetType = semTypeNode(c, n.sons[0], nil)
|
||||
let castedExpr = semExprWithType(c, n.sons[1])
|
||||
if tfHasMeta in targetType.flags:
|
||||
localError(n.sons[0].info, errCastToANonConcreteType, $targetType)
|
||||
if not isCastable(targetType, castedExpr.typ):
|
||||
localError(n.info, errExprCannotBeCastToX, $targetType)
|
||||
result = newNodeI(nkCast, n.info)
|
||||
result.typ = semTypeNode(c, n.sons[0], nil)
|
||||
result.typ = targetType
|
||||
addSon(result, copyTree(n.sons[0]))
|
||||
addSon(result, semExprWithType(c, n.sons[1]))
|
||||
if not isCastable(result.typ, result.sons[1].typ):
|
||||
localError(result.info, errExprCannotBeCastToX,
|
||||
typeToString(result.typ))
|
||||
addSon(result, castedExpr)
|
||||
|
||||
proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
|
||||
const
|
||||
@@ -1054,7 +1059,9 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
# here at all!
|
||||
#if isSymChoice(n.sons[1]): return
|
||||
when defined(nimsuggest):
|
||||
if gCmd == cmdIdeTools: suggestExpr(c, n)
|
||||
if gCmd == cmdIdeTools:
|
||||
suggestExpr(c, n)
|
||||
if exactEquals(gTrackPos, n[1].info): suggestExprNoCheck(c, n)
|
||||
|
||||
var s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared, checkModule})
|
||||
if s != nil:
|
||||
@@ -2234,6 +2241,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
|
||||
# check if it is an expression macro:
|
||||
checkMinSonsLen(n, 1)
|
||||
#when defined(nimsuggest):
|
||||
# if gIdeCmd == ideCon and gTrackPos == n.info: suggestExprNoCheck(c, n)
|
||||
let mode = if nfDotField in n.flags: {} else: {checkUndeclared}
|
||||
var s = qualifiedLookUp(c, n.sons[0], mode)
|
||||
if s != nil:
|
||||
|
||||
@@ -205,7 +205,8 @@ proc semCase(c: PContext, n: PNode): PNode =
|
||||
var typ = commonTypeBegin
|
||||
var hasElse = false
|
||||
var notOrdinal = false
|
||||
case skipTypes(n.sons[0].typ, abstractVarRange-{tyTypeDesc}).kind
|
||||
let caseTyp = skipTypes(n.sons[0].typ, abstractVarRange-{tyTypeDesc})
|
||||
case caseTyp.kind
|
||||
of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool:
|
||||
chckCovered = true
|
||||
of tyFloat..tyFloat128, tyString, tyError:
|
||||
@@ -215,6 +216,9 @@ proc semCase(c: PContext, n: PNode): PNode =
|
||||
return
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
var x = n.sons[i]
|
||||
when defined(nimsuggest):
|
||||
if gIdeCmd == ideSug and exactEquals(gTrackPos, x.info) and caseTyp.kind == tyEnum:
|
||||
suggestEnum(c, x, caseTyp)
|
||||
case x.kind
|
||||
of nkOfBranch:
|
||||
checkMinSonsLen(x, 2)
|
||||
@@ -499,6 +503,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
if hasEmpty(typ):
|
||||
localError(def.info, errCannotInferTypeOfTheLiteral,
|
||||
($typ.kind).substr(2).toLowerAscii)
|
||||
elif typ.kind == tyProc and tfUnresolved in typ.flags:
|
||||
localError(def.info, errProcHasNoConcreteType, def.renderTree)
|
||||
else:
|
||||
if symkind == skLet: localError(a.info, errLetNeedsInit)
|
||||
|
||||
@@ -711,7 +717,11 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
|
||||
# we even look at the type definitions on the right
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
var a = n.sons[i]
|
||||
if gCmd == cmdIdeTools: suggestStmt(c, a)
|
||||
when defined(nimsuggest):
|
||||
if gCmd == cmdIdeTools:
|
||||
inc c.inTypeContext
|
||||
suggestStmt(c, a)
|
||||
dec c.inTypeContext
|
||||
if a.kind == nkCommentStmt: continue
|
||||
if a.kind != nkTypeDef: illFormedAst(a)
|
||||
checkSonsLen(a, 3)
|
||||
|
||||
@@ -1009,8 +1009,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
result.sons[0] = r
|
||||
result.n.typ = r
|
||||
|
||||
if genericParams != nil:
|
||||
if genericParams != nil and genericParams.len > 0:
|
||||
for n in genericParams:
|
||||
if {sfUsed, sfAnon} * n.sym.flags == {}:
|
||||
result.flags.incl tfUnresolved
|
||||
|
||||
if tfWildcard in n.sym.typ.flags:
|
||||
n.sym.kind = skType
|
||||
n.sym.typ.flags.excl tfWildcard
|
||||
|
||||
@@ -1514,6 +1514,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
|
||||
if arg.sons[i].sym.kind in {skProc, skMethod, skConverter, skIterator}:
|
||||
copyCandidate(z, m)
|
||||
z.callee = arg.sons[i].typ
|
||||
if tfUnresolved in z.callee.flags: continue
|
||||
z.calleeSym = arg.sons[i].sym
|
||||
#if arg.sons[i].sym.name.s == "cmp":
|
||||
# ggDebug = true
|
||||
@@ -1650,7 +1651,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||
if a >= formalLen-1 and formal != nil and formal.typ.isVarargsUntyped:
|
||||
incl(marker, formal.position)
|
||||
if container.isNil:
|
||||
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, n.info))
|
||||
container = newNodeIT(nkArgList, n.sons[a].info, arrayConstr(c, n.info))
|
||||
setSon(m.call, formal.position + 1, container)
|
||||
else:
|
||||
incrIndexType(container.typ)
|
||||
@@ -1738,7 +1739,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||
|
||||
if formal.typ.isVarargsUntyped:
|
||||
if container.isNil:
|
||||
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, n.info))
|
||||
container = newNodeIT(nkArgList, n.sons[a].info, arrayConstr(c, n.info))
|
||||
setSon(m.call, formal.position + 1, container)
|
||||
else:
|
||||
incrIndexType(container.typ)
|
||||
|
||||
@@ -63,6 +63,7 @@ type
|
||||
var
|
||||
suggestionResultHook*: proc (result: Suggest) {.closure.}
|
||||
suggestVersion*: int
|
||||
suggestMaxResults* = 10_000
|
||||
|
||||
#template sectionSuggest(): expr = "##begin\n" & getStackTrace() & "##end\n"
|
||||
|
||||
@@ -71,7 +72,7 @@ template origModuleName(m: PSym): string = m.name.s
|
||||
proc findDocComment(n: PNode): PNode =
|
||||
if n == nil: return nil
|
||||
if not isNil(n.comment): return n
|
||||
if n.kind in {nkStmtList, nkStmtListExpr} and n.len > 0:
|
||||
if n.kind in {nkStmtList, nkStmtListExpr, nkObjectTy, nkRecList} and n.len > 0:
|
||||
result = findDocComment(n.sons[0])
|
||||
if result != nil: return
|
||||
if n.len > 1:
|
||||
@@ -104,11 +105,11 @@ proc cmpSuggestions(a, b: Suggest): int =
|
||||
# independent of hashing order:
|
||||
result = cmp(a.name.s, b.name.s)
|
||||
|
||||
proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo;
|
||||
proc symToSuggest(s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo;
|
||||
quality: range[0..100]; prefix: PrefixMatch;
|
||||
inTypeContext: bool; scope: int): Suggest =
|
||||
new(result)
|
||||
result.section = parseIdeCmd(section)
|
||||
result.section = section
|
||||
result.quality = quality
|
||||
result.isGlobal = sfGlobal in s.flags
|
||||
result.tokenLen = s.name.s.len
|
||||
@@ -120,15 +121,10 @@ proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo;
|
||||
result.globalUsages = s.allUsages.len
|
||||
var c = 0
|
||||
for u in s.allUsages:
|
||||
if u.fileIndex == li.fileIndex: inc c
|
||||
if u.fileIndex == info.fileIndex: inc c
|
||||
result.localUsages = c
|
||||
if optIdeTerse in gGlobalOptions:
|
||||
result.symkind = s.kind
|
||||
result.filePath = toFullPath(li)
|
||||
result.line = toLinenumber(li)
|
||||
result.column = toColumn(li)
|
||||
else:
|
||||
result.symkind = s.kind
|
||||
result.symkind = s.kind
|
||||
if optIdeTerse notin gGlobalOptions:
|
||||
result.qualifiedPath = @[]
|
||||
if not isLocal and s.kind != skModule:
|
||||
let ow = s.owner
|
||||
@@ -143,11 +139,12 @@ proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo;
|
||||
result.forth = typeToString(s.typ)
|
||||
else:
|
||||
result.forth = ""
|
||||
result.filePath = toFullPath(li)
|
||||
result.line = toLinenumber(li)
|
||||
result.column = toColumn(li)
|
||||
when not defined(noDocgen):
|
||||
result.doc = s.extractDocComment
|
||||
let infox = if section in {ideUse, ideHighlight, ideOutline}: info else: s.info
|
||||
result.filePath = toFullPath(infox)
|
||||
result.line = toLinenumber(infox)
|
||||
result.column = toColumn(infox)
|
||||
|
||||
proc `$`*(suggest: Suggest): string =
|
||||
result = $suggest.section
|
||||
@@ -181,18 +178,13 @@ proc `$`*(suggest: Suggest): string =
|
||||
result.add(sep)
|
||||
when not defined(noDocgen):
|
||||
result.add(suggest.doc.escape)
|
||||
if suggestVersion == 2:
|
||||
if suggestVersion == 0:
|
||||
result.add(sep)
|
||||
result.add($suggest.quality)
|
||||
if suggest.section == ideSug:
|
||||
result.add(sep)
|
||||
result.add($suggest.prefix)
|
||||
|
||||
proc symToSuggest(s: PSym, isLocal: bool, section: string;
|
||||
quality: range[0..100], prefix: PrefixMatch; inTypeContext: bool;
|
||||
scope: int): Suggest =
|
||||
result = symToSuggest(s, isLocal, section, s.info, quality, prefix, inTypeContext, scope)
|
||||
|
||||
proc suggestResult(s: Suggest) =
|
||||
if not isNil(suggestionResultHook):
|
||||
suggestionResultHook(s)
|
||||
@@ -202,6 +194,10 @@ proc suggestResult(s: Suggest) =
|
||||
proc produceOutput(a: var Suggestions) =
|
||||
if gIdeCmd in {ideSug, ideCon}:
|
||||
a.sort cmpSuggestions
|
||||
when defined(debug):
|
||||
# debug code
|
||||
writeStackTrace()
|
||||
if a.len > suggestMaxResults: a.setLen(suggestMaxResults)
|
||||
if not isNil(suggestionResultHook):
|
||||
for s in a:
|
||||
suggestionResultHook(s)
|
||||
@@ -237,10 +233,10 @@ proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
|
||||
result = true
|
||||
break
|
||||
|
||||
proc suggestField(c: PContext, s: PSym; f: PNode; outputs: var Suggestions) =
|
||||
proc suggestField(c: PContext, s: PSym; f: PNode; info: TLineInfo; outputs: var Suggestions) =
|
||||
var pm: PrefixMatch
|
||||
if filterSym(s, f, pm) and fieldVisible(c, s):
|
||||
outputs.add(symToSuggest(s, isLocal=true, $ideSug, 100, pm, c.inTypeContext > 0, 0))
|
||||
outputs.add(symToSuggest(s, isLocal=true, ideSug, info, 100, pm, c.inTypeContext > 0, 0))
|
||||
|
||||
proc getQuality(s: PSym): range[0..100] =
|
||||
if s.typ != nil and s.typ.len > 1:
|
||||
@@ -259,25 +255,25 @@ template wholeSymTab(cond, section: untyped) =
|
||||
let it {.inject.} = item
|
||||
var pm {.inject.}: PrefixMatch
|
||||
if cond:
|
||||
outputs.add(symToSuggest(it, isLocal = isLocal, section, getQuality(it),
|
||||
outputs.add(symToSuggest(it, isLocal = isLocal, section, info, getQuality(it),
|
||||
pm, c.inTypeContext > 0, scopeN))
|
||||
|
||||
proc suggestSymList(c: PContext, list, f: PNode, outputs: var Suggestions) =
|
||||
proc suggestSymList(c: PContext, list, f: PNode; info: TLineInfo, outputs: var Suggestions) =
|
||||
for i in countup(0, sonsLen(list) - 1):
|
||||
if list.sons[i].kind == nkSym:
|
||||
suggestField(c, list.sons[i].sym, f, outputs)
|
||||
suggestField(c, list.sons[i].sym, f, info, outputs)
|
||||
#else: InternalError(list.info, "getSymFromList")
|
||||
|
||||
proc suggestObject(c: PContext, n, f: PNode, outputs: var Suggestions) =
|
||||
proc suggestObject(c: PContext, n, f: PNode; info: TLineInfo, outputs: var Suggestions) =
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for i in countup(0, sonsLen(n)-1): suggestObject(c, n.sons[i], f, outputs)
|
||||
for i in countup(0, sonsLen(n)-1): suggestObject(c, n.sons[i], f, info, outputs)
|
||||
of nkRecCase:
|
||||
var L = sonsLen(n)
|
||||
if L > 0:
|
||||
suggestObject(c, n.sons[0], f, outputs)
|
||||
for i in countup(1, L-1): suggestObject(c, lastSon(n.sons[i]), f, outputs)
|
||||
of nkSym: suggestField(c, n.sym, f, outputs)
|
||||
suggestObject(c, n.sons[0], f, info, outputs)
|
||||
for i in countup(1, L-1): suggestObject(c, lastSon(n.sons[i]), f, info, outputs)
|
||||
of nkSym: suggestField(c, n.sym, f, info, outputs)
|
||||
else: discard
|
||||
|
||||
proc nameFits(c: PContext, s: PSym, n: PNode): bool =
|
||||
@@ -301,8 +297,9 @@ proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool =
|
||||
result = false
|
||||
|
||||
proc suggestCall(c: PContext, n, nOrig: PNode, outputs: var Suggestions) =
|
||||
let info = n.info
|
||||
wholeSymTab(filterSym(it, nil, pm) and nameFits(c, it, n) and argsFit(c, it, n, nOrig),
|
||||
$ideCon)
|
||||
ideCon)
|
||||
|
||||
proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} =
|
||||
if s.typ != nil and sonsLen(s.typ) > 1 and s.typ.sons[1] != nil:
|
||||
@@ -319,7 +316,8 @@ proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} =
|
||||
|
||||
proc suggestOperations(c: PContext, n, f: PNode, typ: PType, outputs: var Suggestions) =
|
||||
assert typ != nil
|
||||
wholeSymTab(filterSymNoOpr(it, f, pm) and typeFits(c, it, typ), $ideSug)
|
||||
let info = n.info
|
||||
wholeSymTab(filterSymNoOpr(it, f, pm) and typeFits(c, it, typ), ideSug)
|
||||
|
||||
proc suggestEverything(c: PContext, n, f: PNode, outputs: var Suggestions) =
|
||||
# do not produce too many symbols:
|
||||
@@ -331,8 +329,9 @@ proc suggestEverything(c: PContext, n, f: PNode, outputs: var Suggestions) =
|
||||
for it in items(scope.symbols):
|
||||
var pm: PrefixMatch
|
||||
if filterSym(it, f, pm):
|
||||
outputs.add(symToSuggest(it, isLocal = isLocal, $ideSug, 0, pm, c.inTypeContext > 0, scopeN))
|
||||
if scope == c.topLevelScope and f.isNil: break
|
||||
outputs.add(symToSuggest(it, isLocal = isLocal, ideSug, n.info, 0, pm,
|
||||
c.inTypeContext > 0, scopeN))
|
||||
#if scope == c.topLevelScope and f.isNil: break
|
||||
|
||||
proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) =
|
||||
# special code that deals with ``myObj.``. `n` is NOT the nkDotExpr-node, but
|
||||
@@ -340,7 +339,7 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
|
||||
var typ = n.typ
|
||||
var pm: PrefixMatch
|
||||
when defined(nimsuggest):
|
||||
if n.kind == nkSym and n.sym.kind == skError and suggestVersion == 2:
|
||||
if n.kind == nkSym and n.sym.kind == skError and suggestVersion == 0:
|
||||
# consider 'foo.|' where 'foo' is some not imported module.
|
||||
let fullPath = findModule(n.sym.name.s, n.info.toFullPath)
|
||||
if fullPath.len == 0:
|
||||
@@ -352,8 +351,8 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
|
||||
else:
|
||||
for it in items(n.sym.tab):
|
||||
if filterSym(it, field, pm):
|
||||
outputs.add(symToSuggest(it, isLocal=false, $ideSug, 100, pm, c.inTypeContext > 0, -100))
|
||||
outputs.add(symToSuggest(m, isLocal=false, $ideMod, 100, PrefixMatch.None,
|
||||
outputs.add(symToSuggest(it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -100))
|
||||
outputs.add(symToSuggest(m, isLocal=false, ideMod, n.info, 100, PrefixMatch.None,
|
||||
c.inTypeContext > 0, -99))
|
||||
|
||||
if typ == nil:
|
||||
@@ -363,11 +362,11 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
|
||||
# all symbols accessible, because we are in the current module:
|
||||
for it in items(c.topLevelScope.symbols):
|
||||
if filterSym(it, field, pm):
|
||||
outputs.add(symToSuggest(it, isLocal=false, $ideSug, 100, pm, c.inTypeContext > 0, -99))
|
||||
outputs.add(symToSuggest(it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99))
|
||||
else:
|
||||
for it in items(n.sym.tab):
|
||||
if filterSym(it, field, pm):
|
||||
outputs.add(symToSuggest(it, isLocal=false, $ideSug, 100, pm, c.inTypeContext > 0, -99))
|
||||
outputs.add(symToSuggest(it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99))
|
||||
else:
|
||||
# fallback:
|
||||
suggestEverything(c, n, field, outputs)
|
||||
@@ -375,7 +374,7 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
|
||||
# look up if the identifier belongs to the enum:
|
||||
var t = typ
|
||||
while t != nil:
|
||||
suggestSymList(c, t.n, field, outputs)
|
||||
suggestSymList(c, t.n, field, n.info, outputs)
|
||||
t = t.sons[0]
|
||||
suggestOperations(c, n, field, typ, outputs)
|
||||
else:
|
||||
@@ -384,11 +383,11 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
|
||||
if typ.kind == tyObject:
|
||||
var t = typ
|
||||
while true:
|
||||
suggestObject(c, t.n, field, outputs)
|
||||
suggestObject(c, t.n, field, n.info, outputs)
|
||||
if t.sons[0] == nil: break
|
||||
t = skipTypes(t.sons[0], skipPtrs)
|
||||
elif typ.kind == tyTuple and typ.n != nil:
|
||||
suggestSymList(c, typ.n, field, outputs)
|
||||
suggestSymList(c, typ.n, field, n.info, outputs)
|
||||
suggestOperations(c, n, field, orig, outputs)
|
||||
if typ != orig:
|
||||
suggestOperations(c, n, field, typ, outputs)
|
||||
@@ -405,36 +404,12 @@ proc inCheckpoint*(current: TLineInfo): TCheckPointResult =
|
||||
if current.line >= gTrackPos.line:
|
||||
return cpFuzzy
|
||||
|
||||
proc findClosestDot(n: PNode): PNode =
|
||||
if n.kind == nkDotExpr and inCheckpoint(n.info) == cpExact:
|
||||
result = n
|
||||
else:
|
||||
for i in 0.. <safeLen(n):
|
||||
result = findClosestDot(n.sons[i])
|
||||
if result != nil: return
|
||||
|
||||
proc findClosestCall(n: PNode): PNode =
|
||||
if n.kind in nkCallKinds and inCheckpoint(n.info) == cpExact:
|
||||
result = n
|
||||
else:
|
||||
for i in 0.. <safeLen(n):
|
||||
result = findClosestCall(n.sons[i])
|
||||
if result != nil: return
|
||||
|
||||
proc isTracked*(current: TLineInfo, tokenLen: int): bool =
|
||||
if current.fileIndex==gTrackPos.fileIndex and current.line==gTrackPos.line:
|
||||
let col = gTrackPos.col
|
||||
if col >= current.col and col <= current.col+tokenLen-1:
|
||||
return true
|
||||
|
||||
proc findClosestSym(n: PNode): PNode =
|
||||
if n.kind == nkSym and inCheckpoint(n.info) == cpExact:
|
||||
result = n
|
||||
elif n.kind notin {nkNone..nkNilLit}:
|
||||
for i in 0.. <sonsLen(n):
|
||||
result = findClosestSym(n.sons[i])
|
||||
if result != nil: return
|
||||
|
||||
when defined(nimsuggest):
|
||||
# Since TLineInfo defined a == operator that doesn't include the column,
|
||||
# we map TLineInfo to a unique int here for this lookup table:
|
||||
@@ -453,26 +428,26 @@ var
|
||||
lastLineInfo*: TLineInfo
|
||||
|
||||
proc findUsages(info: TLineInfo; s: PSym; usageSym: var PSym) =
|
||||
if suggestVersion < 2:
|
||||
if suggestVersion == 1:
|
||||
if usageSym == nil and isTracked(info, s.name.s.len):
|
||||
usageSym = s
|
||||
suggestResult(symToSuggest(s, isLocal=false, $ideUse, 100, PrefixMatch.None, false, 0))
|
||||
suggestResult(symToSuggest(s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
|
||||
elif s == usageSym:
|
||||
if lastLineInfo != info:
|
||||
suggestResult(symToSuggest(s, isLocal=false, $ideUse, info, 100, PrefixMatch.None, false, 0))
|
||||
suggestResult(symToSuggest(s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
|
||||
lastLineInfo = info
|
||||
|
||||
when defined(nimsuggest):
|
||||
proc listUsages*(s: PSym) =
|
||||
#echo "usages ", len(s.allUsages)
|
||||
for info in s.allUsages:
|
||||
let x = if info == s.info and info.col == s.info.col: "def" else: "use"
|
||||
let x = if info == s.info and info.col == s.info.col: ideDef else: ideUse
|
||||
suggestResult(symToSuggest(s, isLocal=false, x, info, 100, PrefixMatch.None, false, 0))
|
||||
|
||||
proc findDefinition(info: TLineInfo; s: PSym) =
|
||||
if s.isNil: return
|
||||
if isTracked(info, s.name.s.len):
|
||||
suggestResult(symToSuggest(s, isLocal=false, $ideDef, 100, PrefixMatch.None, false, 0))
|
||||
suggestResult(symToSuggest(s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
|
||||
suggestQuit()
|
||||
|
||||
proc ensureIdx[T](x: var T, y: int) =
|
||||
@@ -484,7 +459,7 @@ proc ensureSeq[T](x: var seq[T]) =
|
||||
proc suggestSym*(info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.inline.} =
|
||||
## misnamed: should be 'symDeclared'
|
||||
when defined(nimsuggest):
|
||||
if suggestVersion == 2:
|
||||
if suggestVersion == 0:
|
||||
if s.allUsages.isNil:
|
||||
s.allUsages = @[info]
|
||||
else:
|
||||
@@ -496,13 +471,13 @@ proc suggestSym*(info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.in
|
||||
findDefinition(info, s)
|
||||
elif gIdeCmd == ideDus and s != nil:
|
||||
if isTracked(info, s.name.s.len):
|
||||
suggestResult(symToSuggest(s, isLocal=false, $ideDef, 100, PrefixMatch.None, false, 0))
|
||||
suggestResult(symToSuggest(s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
|
||||
findUsages(info, s, usageSym)
|
||||
elif gIdeCmd == ideHighlight and info.fileIndex == gTrackPos.fileIndex:
|
||||
suggestResult(symToSuggest(s, isLocal=false, $ideHighlight, info, 100, PrefixMatch.None, false, 0))
|
||||
suggestResult(symToSuggest(s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0))
|
||||
elif gIdeCmd == ideOutline and info.fileIndex == gTrackPos.fileIndex and
|
||||
isDecl:
|
||||
suggestResult(symToSuggest(s, isLocal=false, $ideOutline, info, 100, PrefixMatch.None, false, 0))
|
||||
suggestResult(symToSuggest(s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0))
|
||||
|
||||
proc markUsed(info: TLineInfo; s: PSym; usageSym: var PSym) =
|
||||
incl(s.flags, sfUsed)
|
||||
@@ -525,40 +500,31 @@ proc safeSemExpr*(c: PContext, n: PNode): PNode =
|
||||
except ERecoverableError:
|
||||
result = ast.emptyNode
|
||||
|
||||
proc suggestExpr*(c: PContext, node: PNode) =
|
||||
if gTrackPos.line < 0: return
|
||||
var cp = inCheckpoint(node.info)
|
||||
if cp == cpNone: return
|
||||
proc sugExpr(c: PContext, n: PNode, outputs: var Suggestions) =
|
||||
if n.kind == nkDotExpr:
|
||||
var obj = safeSemExpr(c, n.sons[0])
|
||||
# it can happen that errnously we have collected the fieldname
|
||||
# of the next line, so we check the 'field' is actually on the same
|
||||
# line as the object to prevent this from happening:
|
||||
let prefix = if n.len == 2 and n[1].info.line == n[0].info.line and
|
||||
not gTrackPosAttached: n[1] else: nil
|
||||
suggestFieldAccess(c, obj, prefix, outputs)
|
||||
|
||||
#if optIdeDebug in gGlobalOptions:
|
||||
# echo "expression ", renderTree(obj), " has type ", typeToString(obj.typ)
|
||||
#writeStackTrace()
|
||||
else:
|
||||
let prefix = if gTrackPosAttached: nil else: n
|
||||
suggestEverything(c, n, prefix, outputs)
|
||||
|
||||
proc suggestExprNoCheck*(c: PContext, n: PNode) =
|
||||
# This keeps semExpr() from coming here recursively:
|
||||
if c.compilesContextId > 0: return
|
||||
inc(c.compilesContextId)
|
||||
|
||||
var outputs: Suggestions = @[]
|
||||
if gIdeCmd == ideSug:
|
||||
var n = findClosestDot(node)
|
||||
if n == nil: n = node
|
||||
if n.kind == nkDotExpr:
|
||||
var obj = safeSemExpr(c, n.sons[0])
|
||||
# it can happen that errnously we have collected the fieldname
|
||||
# of the next line, so we check the 'field' is actually on the same
|
||||
# line as the object to prevent this from happening:
|
||||
let prefix = if n.len == 2 and n[1].info.line == n[0].info.line: n[1] else: nil
|
||||
suggestFieldAccess(c, obj, prefix, outputs)
|
||||
|
||||
#if optIdeDebug in gGlobalOptions:
|
||||
# echo "expression ", renderTree(obj), " has type ", typeToString(obj.typ)
|
||||
#writeStackTrace()
|
||||
else:
|
||||
#let m = findClosestSym(node)
|
||||
#if m != nil:
|
||||
# suggestPrefix(c, m, outputs)
|
||||
#else:
|
||||
let prefix = if cp == cpExact: n else: nil
|
||||
suggestEverything(c, n, prefix, outputs)
|
||||
|
||||
sugExpr(c, n, outputs)
|
||||
elif gIdeCmd == ideCon:
|
||||
var n = findClosestCall(node)
|
||||
if n == nil: n = node
|
||||
if n.kind in nkCallKinds:
|
||||
var a = copyNode(n)
|
||||
var x = safeSemExpr(c, n.sons[0])
|
||||
@@ -576,16 +542,32 @@ proc suggestExpr*(c: PContext, node: PNode) =
|
||||
produceOutput(outputs)
|
||||
suggestQuit()
|
||||
|
||||
proc suggestExpr*(c: PContext, n: PNode) =
|
||||
if exactEquals(gTrackPos, n.info): suggestExprNoCheck(c, n)
|
||||
|
||||
proc suggestDecl*(c: PContext, n: PNode; s: PSym) =
|
||||
let attached = gTrackPosAttached
|
||||
if attached: inc(c.inTypeContext)
|
||||
defer:
|
||||
if attached: dec(c.inTypeContext)
|
||||
suggestExpr(c, n)
|
||||
|
||||
proc suggestStmt*(c: PContext, n: PNode) =
|
||||
suggestExpr(c, n)
|
||||
|
||||
proc suggestEnum*(c: PContext; n: PNode; t: PType) =
|
||||
var outputs: Suggestions = @[]
|
||||
suggestSymList(c, t.n, nil, n.info, outputs)
|
||||
produceOutput(outputs)
|
||||
if outputs.len > 0: suggestQuit()
|
||||
|
||||
proc suggestSentinel*(c: PContext) =
|
||||
if gIdeCmd != ideSug or c.module.position != gTrackPos.fileIndex: return
|
||||
if c.compilesContextId > 0: return
|
||||
inc(c.compilesContextId)
|
||||
var outputs: Suggestions = @[]
|
||||
# suggest everything:
|
||||
var isLocal = true
|
||||
var outputs: Suggestions = @[]
|
||||
var scopeN = 0
|
||||
for scope in walkScopes(c.currentScope):
|
||||
if scope == c.topLevelScope: isLocal = false
|
||||
@@ -593,7 +575,7 @@ proc suggestSentinel*(c: PContext) =
|
||||
for it in items(scope.symbols):
|
||||
var pm: PrefixMatch
|
||||
if filterSymNoOpr(it, nil, pm):
|
||||
outputs.add(symToSuggest(it, isLocal = isLocal, $ideSug, 0, PrefixMatch.None, false, scopeN))
|
||||
outputs.add(symToSuggest(it, isLocal = isLocal, ideSug, newLineInfo(gTrackPos.fileIndex, -1, -1), 0, PrefixMatch.None, false, scopeN))
|
||||
|
||||
produceOutput(outputs)
|
||||
dec(c.compilesContextId)
|
||||
produceOutput(outputs)
|
||||
|
||||
@@ -291,10 +291,13 @@ proc transformBreak(c: PTransf, n: PNode): PTransNode =
|
||||
else:
|
||||
result = newTransNode(n.kind, n.info, 1)
|
||||
result[0] = lablCopy.PTransNode
|
||||
else:
|
||||
elif c.breakSyms.len > 0:
|
||||
# this check can fail for 'nim check'
|
||||
let labl = c.breakSyms[c.breakSyms.high]
|
||||
result = transformSons(c, n)
|
||||
result[0] = newSymNode(labl).PTransNode
|
||||
else:
|
||||
result = n.PTransNode
|
||||
|
||||
proc unpackTuple(c: PTransf, n: PNode, father: PTransNode) =
|
||||
# XXX: BUG: what if `n` is an expression with side-effects?
|
||||
|
||||
@@ -20,6 +20,7 @@ type
|
||||
preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg
|
||||
|
||||
proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string
|
||||
template `$`*(typ: PType): string = typeToString(typ)
|
||||
|
||||
proc base*(t: PType): PType =
|
||||
result = t.sons[0]
|
||||
@@ -547,7 +548,9 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||
if prefer != preferExported:
|
||||
result.add("(" & typeToString(t.sons[0]) & ")")
|
||||
of tyProc:
|
||||
result = if tfIterator in t.flags: "iterator (" else: "proc ("
|
||||
result = if tfIterator in t.flags: "iterator " else: "proc "
|
||||
if tfUnresolved in t.flags: result.add "[*missing parameters*]"
|
||||
result.add "("
|
||||
for i in countup(1, sonsLen(t) - 1):
|
||||
if t.n != nil and i < t.n.len and t.n[i].kind == nkSym:
|
||||
add(result, t.n[i].sym.name.s)
|
||||
|
||||
@@ -84,7 +84,7 @@ Collections and algorithms
|
||||
Efficient implementation of a set of ints as a sparse bit set.
|
||||
* `critbits <critbits.html>`_
|
||||
This module implements a *crit bit tree* which is an efficient
|
||||
container for a set or a mapping of strings.
|
||||
container for a sorted set of strings, or for a sorted mapping of strings.
|
||||
* `sequtils <sequtils.html>`_
|
||||
This module implements operations for the built-in seq type
|
||||
which were inspired by functional programming languages.
|
||||
|
||||
@@ -2,7 +2,7 @@ Procedures
|
||||
==========
|
||||
|
||||
What most programming languages call `methods`:idx: or `functions`:idx: are
|
||||
called `procedures`:idx: in Nim (which is the correct terminology). A procedure
|
||||
called `procedures`:idx: in Nim. A procedure
|
||||
declaration consists of an identifier, zero or more formal parameters, a return
|
||||
value type and a block of code. Formal parameters are declared as a list of
|
||||
identifiers separated by either comma or semicolon. A parameter is given a type
|
||||
|
||||
174
doc/tut1.rst
174
doc/tut1.rst
@@ -393,7 +393,7 @@ Since counting up occurs so often in programs, Nim also has a `..
|
||||
for i in 1..10:
|
||||
...
|
||||
|
||||
Zero-indexed counting have two shortcuts ``..<`` and ``..^`` to simplify counting to one less then the higher index:
|
||||
Zero-indexed counting have two shortcuts ``..<`` and ``..^`` to simplify counting to one less than the higher index:
|
||||
|
||||
.. code-block:: nim
|
||||
for i in 0..<10:
|
||||
@@ -411,8 +411,8 @@ Other useful iterators for collections (like arrays and sequences) are
|
||||
* ``pairs`` and ``mpairs`` which provides the element and an index number (immutable and mutable respectively)
|
||||
|
||||
.. code-block:: nim
|
||||
for indx, itm in ["a","b"].pairs:
|
||||
echo itm, " at index ", indx
|
||||
for index, item in ["a","b"].pairs:
|
||||
echo item, " at index ", index
|
||||
# => a at index 0
|
||||
# => b at index 1
|
||||
|
||||
@@ -489,10 +489,10 @@ Example:
|
||||
else:
|
||||
echo "unknown operating system"
|
||||
|
||||
The ``when`` statement is almost identical to the ``if`` statement with some
|
||||
The ``when`` statement is almost identical to the ``if`` statement, but with these
|
||||
differences:
|
||||
|
||||
* Each condition has to be a constant expression since it is evaluated by the
|
||||
* Each condition must be a constant expression since it is evaluated by the
|
||||
compiler.
|
||||
* The statements within a branch do not open a new scope.
|
||||
* The compiler checks the semantics and produces code *only* for the statements
|
||||
@@ -516,8 +516,8 @@ In Nim there is a distinction between *simple statements* and *complex
|
||||
statements*. *Simple statements* cannot contain other statements:
|
||||
Assignment, procedure calls or the ``return`` statement belong to the simple
|
||||
statements. *Complex statements* like ``if``, ``when``, ``for``, ``while`` can
|
||||
contain other statements. To avoid ambiguities, complex statements always have
|
||||
to be indented, but single simple statements do not:
|
||||
contain other statements. To avoid ambiguities, complex statements must always
|
||||
be indented, but single simple statements do not:
|
||||
|
||||
.. code-block:: nim
|
||||
# no indentation needed for single assignment statement:
|
||||
@@ -586,9 +586,9 @@ false if they answered "no" (or something similar). A ``return`` statement
|
||||
leaves the procedure (and therefore the while loop) immediately. The
|
||||
``(question: string): bool`` syntax describes that the procedure expects a
|
||||
parameter named ``question`` of type ``string`` and returns a value of type
|
||||
``bool``. ``Bool`` is a built-in type: the only valid values for ``bool`` are
|
||||
``bool``. The ``bool`` type is built-in: the only valid values for ``bool`` are
|
||||
``true`` and ``false``.
|
||||
The conditions in if or while statements should be of the type ``bool``.
|
||||
The conditions in if or while statements must be of type ``bool``.
|
||||
|
||||
Some terminology: in the example ``question`` is called a (formal) *parameter*,
|
||||
``"Should I..."`` is called an *argument* that is passed to this parameter.
|
||||
@@ -658,8 +658,8 @@ a tuple as a return value instead of using var parameters.
|
||||
Discard statement
|
||||
-----------------
|
||||
To call a procedure that returns a value just for its side effects and ignoring
|
||||
its return value, a ``discard`` statement **has** to be used. Nim does not
|
||||
allow to silently throw away a return value:
|
||||
its return value, a ``discard`` statement **must** be used. Nim does not
|
||||
allow silently throwing away a return value:
|
||||
|
||||
.. code-block:: nim
|
||||
discard yes("May I ask a pointless question?")
|
||||
@@ -708,7 +708,7 @@ The compiler checks that each parameter receives exactly one argument.
|
||||
Default values
|
||||
--------------
|
||||
To make the ``createWindow`` proc easier to use it should provide `default
|
||||
values`, these are values that are used as arguments if the caller does not
|
||||
values`; these are values that are used as arguments if the caller does not
|
||||
specify them:
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -750,19 +750,19 @@ algorithm. Ambiguous calls are reported as errors.
|
||||
Operators
|
||||
---------
|
||||
The Nim library makes heavy use of overloading - one reason for this is that
|
||||
each operator like ``+`` is a just an overloaded proc. The parser lets you
|
||||
each operator like ``+`` is just an overloaded proc. The parser lets you
|
||||
use operators in `infix notation` (``a + b``) or `prefix notation` (``+ a``).
|
||||
An infix operator always receives two arguments, a prefix operator always one.
|
||||
Postfix operators are not possible, because this would be ambiguous: does
|
||||
(Postfix operators are not possible, because this would be ambiguous: does
|
||||
``a @ @ b`` mean ``(a) @ (@b)`` or ``(a@) @ (b)``? It always means
|
||||
``(a) @ (@b)``, because there are no postfix operators in Nim.
|
||||
``(a) @ (@b)``, because there are no postfix operators in Nim.)
|
||||
|
||||
Apart from a few built-in keyword operators such as ``and``, ``or``, ``not``,
|
||||
operators always consist of these characters:
|
||||
``+ - * \ / < > = @ $ ~ & % ! ? ^ . |``
|
||||
|
||||
User defined operators are allowed. Nothing stops you from defining your own
|
||||
``@!?+~`` operator, but readability can suffer.
|
||||
``@!?+~`` operator, but doing so may reduce readability.
|
||||
|
||||
The operator's precedence is determined by its first character. The details
|
||||
can be found in the manual.
|
||||
@@ -785,7 +785,7 @@ Forward declarations
|
||||
--------------------
|
||||
|
||||
Every variable, procedure, etc. needs to be declared before it can be used.
|
||||
(The reason for this is that it is non-trivial to do better than that in a
|
||||
(The reason for this is that it is non-trivial to avoid this need in a
|
||||
language that supports meta programming as extensively as Nim does.)
|
||||
However, this cannot be done for mutually recursive procedures:
|
||||
|
||||
@@ -822,7 +822,7 @@ whose value is then returned implicitly.
|
||||
Iterators
|
||||
=========
|
||||
|
||||
Let's return to the boring counting example:
|
||||
Let's return to the simple counting example:
|
||||
|
||||
.. code-block:: nim
|
||||
echo "Counting to ten: "
|
||||
@@ -843,7 +843,7 @@ However, this does not work. The problem is that the procedure should not
|
||||
only ``return``, but return and **continue** after an iteration has
|
||||
finished. This *return and continue* is called a `yield` statement. Now
|
||||
the only thing left to do is to replace the ``proc`` keyword by ``iterator``
|
||||
and there it is - our first iterator:
|
||||
and here it is - our first iterator:
|
||||
|
||||
.. code-block:: nim
|
||||
iterator countup(a, b: int): int =
|
||||
@@ -856,8 +856,8 @@ Iterators look very similar to procedures, but there are several
|
||||
important differences:
|
||||
|
||||
* Iterators can only be called from for loops.
|
||||
* Iterators cannot contain a ``return`` statement and procs cannot contain a
|
||||
``yield`` statement.
|
||||
* Iterators cannot contain a ``return`` statement (and procs cannot contain a
|
||||
``yield`` statement).
|
||||
* Iterators have no implicit ``result`` variable.
|
||||
* Iterators do not support recursion.
|
||||
* Iterators cannot be forward declared, because the compiler must be able
|
||||
@@ -866,8 +866,8 @@ important differences:
|
||||
|
||||
However, you can also use a ``closure`` iterator to get a different set of
|
||||
restrictions. See `first class iterators <manual.html#first-class-iterators>`_
|
||||
for details. Iterators can have the same name and parameters as a proc,
|
||||
essentially they have their own namespace. Therefore it is common practice to
|
||||
for details. Iterators can have the same name and parameters as a proc, since
|
||||
essentially they have their own namespaces. Therefore it is common practice to
|
||||
wrap iterators in procs of the same name which accumulate the result of the
|
||||
iterator and return it as a sequence, like ``split`` from the `strutils module
|
||||
<strutils.html>`_.
|
||||
@@ -882,13 +882,13 @@ that are available for them in detail.
|
||||
Booleans
|
||||
--------
|
||||
|
||||
The boolean type is named ``bool`` in Nim and consists of the two
|
||||
Nim's boolean type is called ``bool`` and consists of the two
|
||||
pre-defined values ``true`` and ``false``. Conditions in while,
|
||||
if, elif, when statements need to be of type bool.
|
||||
if, elif, and when statements must be of type bool.
|
||||
|
||||
The operators ``not, and, or, xor, <, <=, >, >=, !=, ==`` are defined
|
||||
for the bool type. The ``and`` and ``or`` operators perform short-cut
|
||||
evaluation. Example:
|
||||
for the bool type. The ``and`` and ``or`` operators perform short-circuit
|
||||
evaluation. For example:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
@@ -899,8 +899,8 @@ evaluation. Example:
|
||||
|
||||
Characters
|
||||
----------
|
||||
The `character type` is named ``char`` in Nim. Its size is one byte.
|
||||
Thus it cannot represent an UTF-8 character, but a part of it.
|
||||
The `character type` is called ``char``. Its size is always one byte, so
|
||||
it cannot represent most UTF-8 characters; but it *can* represent one of the bytes that makes up a multi-byte UTF-8 character.
|
||||
The reason for this is efficiency: for the overwhelming majority of use-cases,
|
||||
the resulting programs will still handle UTF-8 properly as UTF-8 was specially
|
||||
designed for this.
|
||||
@@ -914,11 +914,11 @@ Converting from an integer to a ``char`` is done with the ``chr`` proc.
|
||||
|
||||
Strings
|
||||
-------
|
||||
String variables in Nim are **mutable**, so appending to a string
|
||||
is quite efficient. Strings in Nim are both zero-terminated and have a
|
||||
length field. One can retrieve a string's length with the builtin ``len``
|
||||
String variables are **mutable**, so appending to a string
|
||||
is possible, and quite efficient. Strings in Nim are both zero-terminated and have a
|
||||
length field. A string's length can be retrieved with the builtin ``len``
|
||||
procedure; the length never counts the terminating zero. Accessing the
|
||||
terminating zero is no error and often leads to simpler code:
|
||||
terminating zero is not an error and often leads to simpler code:
|
||||
|
||||
.. code-block:: nim
|
||||
if s[i] == 'a' and s[i+1] == 'b':
|
||||
@@ -928,15 +928,15 @@ terminating zero is no error and often leads to simpler code:
|
||||
The assignment operator for strings copies the string. You can use the ``&``
|
||||
operator to concatenate strings and ``add`` to append to a string.
|
||||
|
||||
Strings are compared by their lexicographical order. All comparison operators
|
||||
are available. Per convention, all strings are UTF-8 strings, but this is not
|
||||
Strings are compared using their lexicographical order. All the comparison operators
|
||||
are supported. By convention, all strings are UTF-8 encoded, but this is not
|
||||
enforced. For example, when reading strings from binary files, they are merely
|
||||
a sequence of bytes. The index operation ``s[i]`` means the i-th *char* of
|
||||
``s``, not the i-th *unichar*.
|
||||
|
||||
String variables are initialized with a special value, called ``nil``. However,
|
||||
most string operations cannot deal with ``nil`` (leading to an exception being
|
||||
raised) for performance reasons. One should use empty strings ``""``
|
||||
raised) for performance reasons. It is best to use empty strings ``""``
|
||||
rather than ``nil`` as the *empty* value. But ``""`` often creates a string
|
||||
object on the heap, so there is a trade-off to be made here.
|
||||
|
||||
@@ -947,7 +947,7 @@ Nim has these integer types built-in:
|
||||
``int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64``.
|
||||
|
||||
The default integer type is ``int``. Integer literals can have a *type suffix*
|
||||
to mark them to be of another integer type:
|
||||
to specify a non-default integer type:
|
||||
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -961,18 +961,18 @@ Most often integers are used for counting objects that reside in memory, so
|
||||
``int`` has the same size as a pointer.
|
||||
|
||||
The common operators ``+ - * div mod < <= == != > >=`` are defined for
|
||||
integers. The ``and or xor not`` operators are defined for integers too and
|
||||
integers. The ``and or xor not`` operators are also defined for integers, and
|
||||
provide *bitwise* operations. Left bit shifting is done with the ``shl``, right
|
||||
shifting with the ``shr`` operator. Bit shifting operators always treat their
|
||||
arguments as *unsigned*. For `arithmetic bit shifts`:idx: ordinary
|
||||
multiplication or division can be used.
|
||||
|
||||
Unsigned operations all wrap around; they cannot lead to over- or underflow
|
||||
Unsigned operations all wrap around; they cannot lead to over- or under-flow
|
||||
errors.
|
||||
|
||||
`Automatic type conversion`:idx: is performed in expressions where different
|
||||
Lossless `Automatic type conversion`:idx: is performed in expressions where different
|
||||
kinds of integer types are used. However, if the type conversion
|
||||
loses information, the `EOutOfRange`:idx: exception is raised (if the error
|
||||
would cause loss of information, the `EOutOfRange`:idx: exception is raised (if the error
|
||||
cannot be detected at compile time).
|
||||
|
||||
|
||||
@@ -981,9 +981,9 @@ Floats
|
||||
Nim has these floating point types built-in: ``float float32 float64``.
|
||||
|
||||
The default float type is ``float``. In the current implementation,
|
||||
``float`` is always 64 bit wide.
|
||||
``float`` is always 64-bits.
|
||||
|
||||
Float literals can have a *type suffix* to mark them to be of another float
|
||||
Float literals can have a *type suffix* to specify a non-default float
|
||||
type:
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -993,18 +993,18 @@ type:
|
||||
z = 0.0'f64 # z is of type ``float64``
|
||||
|
||||
The common operators ``+ - * / < <= == != > >=`` are defined for
|
||||
floats and follow the IEEE standard.
|
||||
floats and follow the IEEE-754 standard.
|
||||
|
||||
Automatic type conversion in expressions with different kinds of floating
|
||||
point types is performed: the smaller type is converted to the larger. Integer
|
||||
types are **not** converted to floating point types automatically and vice
|
||||
versa. The `toInt <system.html#toInt>`_ and `toFloat <system.html#toFloat>`_
|
||||
procs can be used for these conversions.
|
||||
types are **not** converted to floating point types automatically, nor vice
|
||||
versa. Use the `toInt <system.html#toInt>`_ and `toFloat <system.html#toFloat>`_
|
||||
procs for these conversions.
|
||||
|
||||
|
||||
Type Conversion
|
||||
---------------
|
||||
Conversion between basic types in nim is performed by using the
|
||||
Conversion between basic types is performed by using the
|
||||
type as a function:
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -1019,9 +1019,9 @@ Internal type representation
|
||||
============================
|
||||
|
||||
As mentioned earlier, the built-in `$ <system.html#$>`_ (stringify) operator
|
||||
turns any basic type into a string, which you can then print to the screen
|
||||
with the ``echo`` proc. However, advanced types, or types you may define
|
||||
yourself won't work with the ``$`` operator until you define one for them.
|
||||
turns any basic type into a string, which you can then print to the console
|
||||
using the ``echo`` proc. However, advanced types, and your own custom types,
|
||||
won't work with the ``$`` operator until you define it for them.
|
||||
Sometimes you just want to debug the current value of a complex type without
|
||||
having to write its ``$`` operator. You can use then the `repr
|
||||
<system.html#repr>`_ proc which works with any type and even complex data
|
||||
@@ -1057,16 +1057,16 @@ In Nim new types can be defined within a ``type`` statement:
|
||||
biggestInt = int64 # biggest integer type that is available
|
||||
biggestFloat = float64 # biggest float type that is available
|
||||
|
||||
Enumeration and object types cannot be defined on the fly, but only within a
|
||||
Enumeration and object types may only be defined within a
|
||||
``type`` statement.
|
||||
|
||||
|
||||
Enumerations
|
||||
------------
|
||||
A variable of an enumeration type can only be assigned a value of a
|
||||
limited set. This set consists of ordered symbols. Each symbol is mapped
|
||||
A variable of an enumeration type can only be assigned one of the enumeration's specified values.
|
||||
These values are a set of ordered symbols. Each symbol is mapped
|
||||
to an integer value internally. The first symbol is represented
|
||||
at runtime by 0, the second by 1 and so on. Example:
|
||||
at runtime by 0, the second by 1 and so on. For example:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
@@ -1077,17 +1077,17 @@ at runtime by 0, the second by 1 and so on. Example:
|
||||
var x = south # `x` is of type `Direction`; its value is `south`
|
||||
echo x # writes "south" to `stdout`
|
||||
|
||||
All comparison operators can be used with enumeration types.
|
||||
All the comparison operators can be used with enumeration types.
|
||||
|
||||
An enumeration's symbol can be qualified to avoid ambiguities:
|
||||
``Direction.south``.
|
||||
|
||||
The ``$`` operator can convert any enumeration value to its name, the ``ord``
|
||||
proc to its underlying integer value.
|
||||
The ``$`` operator can convert any enumeration value to its name, and the ``ord``
|
||||
proc can convert it to its underlying integer value.
|
||||
|
||||
For better interfacing to other programming languages, the symbols of enum
|
||||
types can be assigned an explicit ordinal value. However, the ordinal values
|
||||
have to be in ascending order. A symbol whose ordinal value is not
|
||||
must be in ascending order. A symbol whose ordinal value is not
|
||||
explicitly given is assigned the value of the previous symbol + 1.
|
||||
|
||||
An explicit ordered enum can have *holes*:
|
||||
@@ -1142,8 +1142,8 @@ subrange types (and vice versa) are allowed.
|
||||
|
||||
The ``system`` module defines the important `Natural <system.html#Natural>`_
|
||||
type as ``range[0..high(int)]`` (`high <system.html#high>`_ returns the
|
||||
maximal value). Other programming languages mandate the usage of unsigned
|
||||
integers for natural numbers. This is often **wrong**: you don't want unsigned
|
||||
maximal value). Other programming languages may suggest the use of unsigned
|
||||
integers for natural numbers. This is often **unwise**: you don't want unsigned
|
||||
arithmetic (which wraps around) just because the numbers cannot be negative.
|
||||
Nim's ``Natural`` type helps to avoid this common programming error.
|
||||
|
||||
@@ -1156,9 +1156,9 @@ Sets
|
||||
Arrays
|
||||
------
|
||||
An array is a simple fixed length container. Each element in
|
||||
the array has the same type. The array's index type can be any ordinal type.
|
||||
an array has the same type. The array's index type can be any ordinal type.
|
||||
|
||||
Arrays can be constructed via ``[]``:
|
||||
Arrays can be constructed using ``[]``:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
@@ -1222,7 +1222,7 @@ subdivided in height levels accessed through their integer index:
|
||||
#tower[0][1] = on
|
||||
|
||||
Note how the built-in ``len`` proc returns only the array's first dimension
|
||||
length. Another way of defining the ``LightTower`` to show better its
|
||||
length. Another way of defining the ``LightTower`` to better illustrate its
|
||||
nested nature would be to omit the previous definition of the ``LevelSetting``
|
||||
type and instead write it embedded directly as the type of the first dimension:
|
||||
|
||||
@@ -1230,7 +1230,7 @@ type and instead write it embedded directly as the type of the first dimension:
|
||||
type
|
||||
LightTower = array[1..10, array[north..west, BlinkLights]]
|
||||
|
||||
It is quite frequent to have arrays start at zero, so there's a shortcut syntax
|
||||
It is quite common to have arrays start at zero, so there's a shortcut syntax
|
||||
to specify a range from zero to the specified index minus one:
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -1288,8 +1288,8 @@ value. Here the ``for`` statement is looping over the results from the
|
||||
<system.html>`_ module. Examples:
|
||||
|
||||
.. code-block:: nim
|
||||
for i in @[3, 4, 5]:
|
||||
echo i
|
||||
for value in @[3, 4, 5]:
|
||||
echo value
|
||||
# --> 3
|
||||
# --> 4
|
||||
# --> 5
|
||||
@@ -1320,7 +1320,7 @@ type does not matter.
|
||||
|
||||
fruits = @[] # creates an empty sequence on the heap that will be referenced by 'fruits'
|
||||
|
||||
capitals = ["New York", "London", "Berlin"] # array 'capitals' allows only assignment of three elements
|
||||
capitals = ["New York", "London", "Berlin"] # array 'capitals' allows assignment of only three elements
|
||||
fruits.add("Banana") # sequence 'fruits' is dynamically expandable during runtime
|
||||
fruits.add("Mango")
|
||||
|
||||
@@ -1406,7 +1406,7 @@ the same type and of the same name in the same order.
|
||||
|
||||
The assignment operator for tuples copies each component. The notation
|
||||
``t.field`` is used to access a tuple's field. Another notation is
|
||||
``t[i]`` to access the ``i``'th field. Here ``i`` needs to be a constant
|
||||
``t[i]`` to access the ``i``'th field. Here ``i`` must be a constant
|
||||
integer.
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -1449,10 +1449,10 @@ Tuples can be *unpacked* during variable assignment (and only then!). This can
|
||||
be handy to assign directly the fields of the tuples to individually named
|
||||
variables. An example of this is the `splitFile <os.html#splitFile>`_ proc
|
||||
from the `os module <os.html>`_ which returns the directory, name and
|
||||
extension of a path at the same time. For tuple unpacking to work you have to
|
||||
use parenthesis around the values you want to assign the unpacking to,
|
||||
extension of a path at the same time. For tuple unpacking to work you must
|
||||
use parentheses around the values you want to assign the unpacking to,
|
||||
otherwise you will be assigning the same value to all the individual
|
||||
variables! Example:
|
||||
variables! For example:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
@@ -1494,12 +1494,12 @@ point to and modify the same location in memory.
|
||||
|
||||
Nim distinguishes between `traced`:idx: and `untraced`:idx: references.
|
||||
Untraced references are also called *pointers*. Traced references point to
|
||||
objects of a garbage collected heap, untraced references point to
|
||||
manually allocated objects or to objects somewhere else in memory. Thus
|
||||
objects in a garbage collected heap, untraced references point to
|
||||
manually allocated objects or to objects elsewhere in memory. Thus
|
||||
untraced references are *unsafe*. However for certain low-level operations
|
||||
(accessing the hardware) untraced references are unavoidable.
|
||||
(e.g., accessing the hardware), untraced references are necessary.
|
||||
|
||||
Traced references are declared with the **ref** keyword, untraced references
|
||||
Traced references are declared with the **ref** keyword; untraced references
|
||||
are declared with the **ptr** keyword.
|
||||
|
||||
The empty ``[]`` subscript notation can be used to *derefer* a reference,
|
||||
@@ -1520,10 +1520,10 @@ operators perform implicit dereferencing operations for reference types:
|
||||
n.data = 9
|
||||
# no need to write n[].data; in fact n[].data is highly discouraged!
|
||||
|
||||
To allocate a new traced object, the built-in procedure ``new`` has to be used.
|
||||
To allocate a new traced object, the built-in procedure ``new`` must be used.
|
||||
To deal with untraced memory, the procedures ``alloc``, ``dealloc`` and
|
||||
``realloc`` can be used. The documentation of the `system <system.html>`_
|
||||
module contains further information.
|
||||
``realloc`` can be used. The `system <system.html>`_
|
||||
module's documentation contains further details.
|
||||
|
||||
If a reference points to *nothing*, it has the value ``nil``.
|
||||
|
||||
@@ -1555,8 +1555,8 @@ listed in the `manual <manual.html#types-procedural-type>`_.
|
||||
|
||||
Distinct type
|
||||
-------------
|
||||
A Distinct type allows for the creation of new type that "does not imply a subtype relationship between it and its base type".
|
||||
You must EXPLICITLY define all behaviour for the distinct type.
|
||||
A Distinct type allows for the creation of new type that "does not imply a subtype relationship between it and its base type".
|
||||
You must **explicitly** define all behaviour for the distinct type.
|
||||
To help with this, both the distinct type and its base type can cast from one type to the other.
|
||||
Examples are provided in the `manual <manual.html#types-distinct-type>`_.
|
||||
|
||||
@@ -1564,8 +1564,8 @@ Modules
|
||||
=======
|
||||
Nim supports splitting a program into pieces with a module concept.
|
||||
Each module is in its own file. Modules enable `information hiding`:idx: and
|
||||
`separate compilation`:idx:. A module may gain access to symbols of another
|
||||
module by the `import`:idx: statement. Only top-level symbols that are marked
|
||||
`separate compilation`:idx:. A module may gain access to the symbols of another
|
||||
module by using the `import`:idx: statement. Only top-level symbols that are marked
|
||||
with an asterisk (``*``) are exported:
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -1585,7 +1585,7 @@ with an asterisk (``*``) are exported:
|
||||
|
||||
The above module exports ``x`` and ``*``, but not ``y``.
|
||||
|
||||
The top-level statements of a module are executed at the start of the program.
|
||||
A module's top-level statements are executed at the start of the program.
|
||||
This can be used to initialize complex data structures for example.
|
||||
|
||||
Each module has a special magic constant ``isMainModule`` that is true if the
|
||||
@@ -1625,8 +1625,8 @@ This is best illustrated by an example:
|
||||
result = x + 1
|
||||
|
||||
|
||||
A symbol of a module *can* be *qualified* with the ``module.symbol`` syntax. If
|
||||
the symbol is ambiguous, it even *has* to be qualified. A symbol is ambiguous
|
||||
A symbol of a module *can* be *qualified* with the ``module.symbol`` syntax. And if
|
||||
a symbol is ambiguous, it *must* be qualified. A symbol is ambiguous
|
||||
if it is defined in two (or more) different modules and both modules are
|
||||
imported by a third one:
|
||||
|
||||
@@ -1642,7 +1642,7 @@ imported by a third one:
|
||||
# Module C
|
||||
import A, B
|
||||
write(stdout, x) # error: x is ambiguous
|
||||
write(stdout, A.x) # no error: qualifier used
|
||||
write(stdout, A.x) # okay: qualifier used
|
||||
|
||||
var x = 4
|
||||
write(stdout, x) # not ambiguous: uses the module C's x
|
||||
|
||||
17
koch.nim
17
koch.nim
@@ -214,9 +214,9 @@ proc buildNimble(latest: bool) =
|
||||
|
||||
proc bundleNimsuggest(buildExe: bool) =
|
||||
if buildExe:
|
||||
nimexec("c --noNimblePath -d:release -p:compiler tools/nimsuggest/nimsuggest.nim")
|
||||
copyExe("tools/nimsuggest/nimsuggest".exe, "bin/nimsuggest".exe)
|
||||
removeFile("tools/nimsuggest/nimsuggest".exe)
|
||||
nimexec("c --noNimblePath -d:release -p:compiler nimsuggest/nimsuggest.nim")
|
||||
copyExe("nimsuggest/nimsuggest".exe, "bin/nimsuggest".exe)
|
||||
removeFile("nimsuggest/nimsuggest".exe)
|
||||
|
||||
proc bundleWinTools() =
|
||||
nimexec("c tools/finish.nim")
|
||||
@@ -253,7 +253,7 @@ proc buildTool(toolname, args: string) =
|
||||
proc buildTools(latest: bool) =
|
||||
let nimsugExe = "bin/nimsuggest".exe
|
||||
nimexec "c --noNimblePath -p:compiler -d:release -o:" & nimsugExe &
|
||||
" tools/nimsuggest/nimsuggest.nim"
|
||||
" nimsuggest/nimsuggest.nim"
|
||||
|
||||
let nimgrepExe = "bin/nimgrep".exe
|
||||
nimexec "c -o:" & nimgrepExe & " tools/nimgrep.nim"
|
||||
@@ -512,6 +512,14 @@ proc pushCsources() =
|
||||
finally:
|
||||
setCurrentDir(cwd)
|
||||
|
||||
proc valgrind(cmd: string) =
|
||||
exec("nim c " & cmd)
|
||||
var i = cmd.len-1
|
||||
while i >= 0 and cmd[i] != ' ': dec i
|
||||
let file = if i >= 0: substr(cmd, i+1) else: cmd
|
||||
let supp = getAppDir() / "tools" / "nimgrind.supp"
|
||||
exec("valgrind --suppressions=" & supp & " " & changeFileExt(file, ""))
|
||||
|
||||
proc showHelp() =
|
||||
quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)),
|
||||
CompileDate, CompileTime], QuitSuccess)
|
||||
@@ -548,5 +556,6 @@ of cmdArgument:
|
||||
of "nimsuggest": bundleNimsuggest(buildExe=true)
|
||||
of "tools": buildTools(existsDir(".git"))
|
||||
of "pushcsource", "pushcsources": pushCsources()
|
||||
of "valgrind": valgrind(op.cmdLineRest)
|
||||
else: showHelp()
|
||||
of cmdEnd: showHelp()
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
#
|
||||
|
||||
## This module implements a `crit bit tree`:idx: which is an efficient
|
||||
## container for a set or a mapping of strings. Based on the excellent paper
|
||||
## container for a sorted set of strings, or for a sorted mapping of strings. Based on the excellent paper
|
||||
## by Adam Langley.
|
||||
## (A crit bit tree is a form of `radix tree`:idx: or `patricia trie`:idx:.)
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
|
||||
@@ -16,9 +16,6 @@
|
||||
## <manual.html#anonymous-procs>`_ to procs like ``filter`` to reduce typing.
|
||||
## Anonymous procs can use `the special do notation <manual.html#do-notation>`_
|
||||
## which is more convenient in certain situations.
|
||||
##
|
||||
## **Note**: This interface will change as soon as the compiler supports
|
||||
## closures and proper coroutines.
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
|
||||
@@ -83,7 +83,8 @@ proc unmapMem*(f: var MemFile, p: pointer, size: int) =
|
||||
|
||||
|
||||
proc open*(filename: string, mode: FileMode = fmRead,
|
||||
mappedSize = -1, offset = 0, newFileSize = -1): MemFile =
|
||||
mappedSize = -1, offset = 0, newFileSize = -1,
|
||||
allowRemap = false): MemFile =
|
||||
## opens a memory mapped file. If this fails, ``EOS`` is raised.
|
||||
##
|
||||
## ``newFileSize`` can only be set if the file does not exist and is opened
|
||||
@@ -95,6 +96,9 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||
## ``offset`` must be multiples of the PAGE SIZE of your OS
|
||||
## (usually 4K or 8K but is unique to your OS)
|
||||
##
|
||||
## ``allowRemap`` only needs to be true if you want to call ``mapMem`` on
|
||||
## the resulting MemFile; else file handles are not kept open.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
@@ -189,11 +193,14 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||
else: result.size = fileSize.int
|
||||
|
||||
result.wasOpened = true
|
||||
if not allowRemap and result.fHandle != INVALID_HANDLE_VALUE:
|
||||
if closeHandle(result.fHandle) == 0:
|
||||
result.fHandle = INVALID_HANDLE_VALUE
|
||||
|
||||
else:
|
||||
template fail(errCode: OSErrorCode, msg: expr) =
|
||||
rollback()
|
||||
if result.handle != 0: discard close(result.handle)
|
||||
if result.handle != -1: discard close(result.handle)
|
||||
raiseOSError(errCode)
|
||||
|
||||
var flags = if readonly: O_RDONLY else: O_RDWR
|
||||
@@ -236,6 +243,10 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||
if result.mem == cast[pointer](MAP_FAILED):
|
||||
fail(osLastError(), "file mapping failed")
|
||||
|
||||
if not allowRemap and result.handle != -1:
|
||||
if close(result.handle) == 0:
|
||||
result.handle = -1
|
||||
|
||||
proc close*(f: var MemFile) =
|
||||
## closes the memory mapped file `f`. All changes are written back to the
|
||||
## file system, if `f` was opened with write access.
|
||||
@@ -244,15 +255,16 @@ proc close*(f: var MemFile) =
|
||||
var lastErr: OSErrorCode
|
||||
|
||||
when defined(windows):
|
||||
if f.fHandle != INVALID_HANDLE_VALUE and f.wasOpened:
|
||||
if f.wasOpened:
|
||||
error = unmapViewOfFile(f.mem) == 0
|
||||
lastErr = osLastError()
|
||||
error = (closeHandle(f.mapHandle) == 0) or error
|
||||
error = (closeHandle(f.fHandle) == 0) or error
|
||||
if f.fHandle != INVALID_HANDLE_VALUE:
|
||||
error = (closeHandle(f.fHandle) == 0) or error
|
||||
else:
|
||||
if f.handle != 0:
|
||||
error = munmap(f.mem, f.size) != 0
|
||||
lastErr = osLastError()
|
||||
error = munmap(f.mem, f.size) != 0
|
||||
lastErr = osLastError()
|
||||
if f.handle != -1:
|
||||
error = (close(f.handle) != 0) or error
|
||||
|
||||
f.size = 0
|
||||
@@ -263,7 +275,7 @@ proc close*(f: var MemFile) =
|
||||
f.mapHandle = 0
|
||||
f.wasOpened = false
|
||||
else:
|
||||
f.handle = 0
|
||||
f.handle = -1
|
||||
|
||||
if error: raiseOSError(lastErr)
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ when not declared(getEnv) or defined(nimscript):
|
||||
WriteEnvEffect* = object of WriteIOEffect ## effect that denotes a write
|
||||
## to an environment variable
|
||||
|
||||
ReadDirEffect* = object of ReadIOEffect ## effect that denotes a write
|
||||
## operation to the directory
|
||||
ReadDirEffect* = object of ReadIOEffect ## effect that denotes a read
|
||||
## operation from the directory
|
||||
## structure
|
||||
WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write
|
||||
## operation to
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
## var msg = createMessage("Hello from Nim's SMTP",
|
||||
## "Hello!.\n Is this awesome or what?",
|
||||
## @["foo@gmail.com"])
|
||||
## var smtpConn = connect("smtp.gmail.com", Port 465, true, true)
|
||||
## let smtpConn = newSmtp(useSsl = true, debug=true)
|
||||
## smtpConn.connect("smtp.gmail.com", Port 465)
|
||||
## smtpConn.auth("username", "password")
|
||||
## smtpConn.sendmail("username@gmail.com", @["foo@gmail.com"], $msg)
|
||||
##
|
||||
@@ -34,10 +35,6 @@ import asyncnet, asyncdispatch
|
||||
export Port
|
||||
|
||||
type
|
||||
Smtp* = object
|
||||
sock: Socket
|
||||
debug: bool
|
||||
|
||||
Message* = object
|
||||
msgTo: seq[string]
|
||||
msgCc: seq[string]
|
||||
@@ -47,37 +44,29 @@ type
|
||||
|
||||
ReplyError* = object of IOError
|
||||
|
||||
AsyncSmtp* = ref object
|
||||
sock: AsyncSocket
|
||||
address: string
|
||||
port: Port
|
||||
useSsl: bool
|
||||
SmtpBase[SocketType] = ref object
|
||||
sock: SocketType
|
||||
debug: bool
|
||||
|
||||
Smtp* = SmtpBase[Socket]
|
||||
AsyncSmtp* = SmtpBase[AsyncSocket]
|
||||
|
||||
{.deprecated: [EInvalidReply: ReplyError, TMessage: Message, TSMTP: Smtp].}
|
||||
|
||||
proc debugSend(smtp: Smtp, cmd: string) =
|
||||
proc debugSend(smtp: Smtp | AsyncSmtp, cmd: string) {.multisync.} =
|
||||
if smtp.debug:
|
||||
echo("C:" & cmd)
|
||||
smtp.sock.send(cmd)
|
||||
|
||||
proc debugRecv(smtp: var Smtp): TaintedString =
|
||||
var line = TaintedString""
|
||||
smtp.sock.readLine(line)
|
||||
await smtp.sock.send(cmd)
|
||||
|
||||
proc debugRecv(smtp: Smtp | AsyncSmtp): Future[TaintedString] {.multisync.} =
|
||||
result = await smtp.sock.recvLine()
|
||||
if smtp.debug:
|
||||
echo("S:" & line.string)
|
||||
return line
|
||||
echo("S:" & result.string)
|
||||
|
||||
proc quitExcpt(smtp: Smtp, msg: string) =
|
||||
smtp.debugSend("QUIT")
|
||||
raise newException(ReplyError, msg)
|
||||
|
||||
proc checkReply(smtp: var Smtp, reply: string) =
|
||||
var line = smtp.debugRecv()
|
||||
if not line.string.startswith(reply):
|
||||
quitExcpt(smtp, "Expected " & reply & " reply, got: " & line.string)
|
||||
|
||||
const compiledWithSsl = defined(ssl)
|
||||
|
||||
when not defined(ssl):
|
||||
@@ -86,63 +75,6 @@ when not defined(ssl):
|
||||
else:
|
||||
let defaultSSLContext = newContext(verifyMode = CVerifyNone)
|
||||
|
||||
proc connect*(address: string, port = Port(25),
|
||||
ssl = false, debug = false,
|
||||
sslContext = defaultSSLContext): Smtp =
|
||||
## Establishes a connection with a SMTP server.
|
||||
## May fail with ReplyError or with a socket error.
|
||||
result.sock = newSocket()
|
||||
if ssl:
|
||||
when compiledWithSsl:
|
||||
sslContext.wrapSocket(result.sock)
|
||||
else:
|
||||
raise newException(ESystem,
|
||||
"SMTP module compiled without SSL support")
|
||||
result.sock.connect(address, port)
|
||||
result.debug = debug
|
||||
|
||||
result.checkReply("220")
|
||||
result.debugSend("HELO " & address & "\c\L")
|
||||
result.checkReply("250")
|
||||
|
||||
proc auth*(smtp: var Smtp, username, password: string) =
|
||||
## Sends an AUTH command to the server to login as the `username`
|
||||
## using `password`.
|
||||
## May fail with ReplyError.
|
||||
|
||||
smtp.debugSend("AUTH LOGIN\c\L")
|
||||
smtp.checkReply("334") # TODO: Check whether it's asking for the "Username:"
|
||||
# i.e "334 VXNlcm5hbWU6"
|
||||
smtp.debugSend(encode(username) & "\c\L")
|
||||
smtp.checkReply("334") # TODO: Same as above, only "Password:" (I think?)
|
||||
|
||||
smtp.debugSend(encode(password) & "\c\L")
|
||||
smtp.checkReply("235") # Check whether the authentification was successful.
|
||||
|
||||
proc sendmail*(smtp: var Smtp, fromaddr: string,
|
||||
toaddrs: seq[string], msg: string) =
|
||||
## Sends `msg` from `fromaddr` to `toaddr`.
|
||||
## Messages may be formed using ``createMessage`` by converting the
|
||||
## Message into a string.
|
||||
|
||||
smtp.debugSend("MAIL FROM:<" & fromaddr & ">\c\L")
|
||||
smtp.checkReply("250")
|
||||
for address in items(toaddrs):
|
||||
smtp.debugSend("RCPT TO:<" & address & ">\c\L")
|
||||
smtp.checkReply("250")
|
||||
|
||||
# Send the message
|
||||
smtp.debugSend("DATA " & "\c\L")
|
||||
smtp.checkReply("354")
|
||||
smtp.debugSend(msg & "\c\L")
|
||||
smtp.debugSend(".\c\L")
|
||||
smtp.checkReply("250")
|
||||
|
||||
proc close*(smtp: Smtp) =
|
||||
## Disconnects from the SMTP server and closes the socket.
|
||||
smtp.debugSend("QUIT\c\L")
|
||||
smtp.sock.close()
|
||||
|
||||
proc createMessage*(mSubject, mBody: string, mTo, mCc: seq[string],
|
||||
otherHeaders: openarray[tuple[name, value: string]]): Message =
|
||||
## Creates a new MIME compliant message.
|
||||
@@ -178,81 +110,94 @@ proc `$`*(msg: Message): string =
|
||||
result.add("\c\L")
|
||||
result.add(msg.msgBody)
|
||||
|
||||
proc newAsyncSmtp*(address: string, port: Port, useSsl = false,
|
||||
proc newSmtp*(useSsl = false, debug=false,
|
||||
sslContext = defaultSslContext): Smtp =
|
||||
## Creates a new ``Smtp`` instance.
|
||||
new result
|
||||
result.debug = debug
|
||||
|
||||
result.sock = newSocket()
|
||||
if useSsl:
|
||||
when compiledWithSsl:
|
||||
sslContext.wrapSocket(result.sock)
|
||||
else:
|
||||
raise newException(SystemError,
|
||||
"SMTP module compiled without SSL support")
|
||||
|
||||
proc newAsyncSmtp*(useSsl = false, debug=false,
|
||||
sslContext = defaultSslContext): AsyncSmtp =
|
||||
## Creates a new ``AsyncSmtp`` instance.
|
||||
new result
|
||||
result.address = address
|
||||
result.port = port
|
||||
result.useSsl = useSsl
|
||||
result.debug = debug
|
||||
|
||||
result.sock = newAsyncSocket()
|
||||
if useSsl:
|
||||
when compiledWithSsl:
|
||||
sslContext.wrapSocket(result.sock)
|
||||
else:
|
||||
raise newException(ESystem,
|
||||
raise newException(SystemError,
|
||||
"SMTP module compiled without SSL support")
|
||||
|
||||
proc quitExcpt(smtp: AsyncSmtp, msg: string): Future[void] =
|
||||
var retFuture = newFuture[void]()
|
||||
var sendFut = smtp.sock.send("QUIT")
|
||||
var sendFut = smtp.debugSend("QUIT")
|
||||
sendFut.callback =
|
||||
proc () =
|
||||
# TODO: Fix this in async procs.
|
||||
raise newException(ReplyError, msg)
|
||||
return retFuture
|
||||
|
||||
proc checkReply(smtp: AsyncSmtp, reply: string) {.async.} =
|
||||
var line = await smtp.sock.recvLine()
|
||||
if not line.string.startswith(reply):
|
||||
await quitExcpt(smtp, "Expected " & reply & " reply, got: " & line.string)
|
||||
proc checkReply(smtp: Smtp | AsyncSmtp, reply: string) {.multisync.} =
|
||||
var line = await smtp.debugRecv()
|
||||
if not line.startswith(reply):
|
||||
await quitExcpt(smtp, "Expected " & reply & " reply, got: " & line)
|
||||
|
||||
proc connect*(smtp: AsyncSmtp) {.async.} =
|
||||
proc connect*(smtp: Smtp | AsyncSmtp,
|
||||
address: string, port: Port) {.multisync.} =
|
||||
## Establishes a connection with a SMTP server.
|
||||
## May fail with ReplyError or with a socket error.
|
||||
await smtp.sock.connect(smtp.address, smtp.port)
|
||||
await smtp.sock.connect(address, port)
|
||||
|
||||
await smtp.checkReply("220")
|
||||
await smtp.sock.send("HELO " & smtp.address & "\c\L")
|
||||
await smtp.debugSend("HELO " & address & "\c\L")
|
||||
await smtp.checkReply("250")
|
||||
|
||||
proc auth*(smtp: AsyncSmtp, username, password: string) {.async.} =
|
||||
proc auth*(smtp: Smtp | AsyncSmtp, username, password: string) {.multisync.} =
|
||||
## Sends an AUTH command to the server to login as the `username`
|
||||
## using `password`.
|
||||
## May fail with ReplyError.
|
||||
|
||||
await smtp.sock.send("AUTH LOGIN\c\L")
|
||||
await smtp.debugSend("AUTH LOGIN\c\L")
|
||||
await smtp.checkReply("334") # TODO: Check whether it's asking for the "Username:"
|
||||
# i.e "334 VXNlcm5hbWU6"
|
||||
await smtp.sock.send(encode(username) & "\c\L")
|
||||
await smtp.debugSend(encode(username) & "\c\L")
|
||||
await smtp.checkReply("334") # TODO: Same as above, only "Password:" (I think?)
|
||||
|
||||
await smtp.sock.send(encode(password) & "\c\L")
|
||||
await smtp.debugSend(encode(password) & "\c\L")
|
||||
await smtp.checkReply("235") # Check whether the authentification was successful.
|
||||
|
||||
proc sendMail*(smtp: AsyncSmtp, fromAddr: string,
|
||||
toAddrs: seq[string], msg: string) {.async.} =
|
||||
proc sendMail*(smtp: Smtp | AsyncSmtp, fromAddr: string,
|
||||
toAddrs: seq[string], msg: string) {.multisync.} =
|
||||
## Sends ``msg`` from ``fromAddr`` to the addresses specified in ``toAddrs``.
|
||||
## Messages may be formed using ``createMessage`` by converting the
|
||||
## Message into a string.
|
||||
|
||||
await smtp.sock.send("MAIL FROM:<" & fromAddr & ">\c\L")
|
||||
await smtp.debugSend("MAIL FROM:<" & fromAddr & ">\c\L")
|
||||
await smtp.checkReply("250")
|
||||
for address in items(toAddrs):
|
||||
await smtp.sock.send("RCPT TO:<" & address & ">\c\L")
|
||||
await smtp.debugSend("RCPT TO:<" & address & ">\c\L")
|
||||
await smtp.checkReply("250")
|
||||
|
||||
# Send the message
|
||||
await smtp.sock.send("DATA " & "\c\L")
|
||||
await smtp.debugSend("DATA " & "\c\L")
|
||||
await smtp.checkReply("354")
|
||||
await smtp.sock.send(msg & "\c\L")
|
||||
await smtp.sock.send(".\c\L")
|
||||
await smtp.debugSend(".\c\L")
|
||||
await smtp.checkReply("250")
|
||||
|
||||
proc close*(smtp: AsyncSmtp) {.async.} =
|
||||
proc close*(smtp: Smtp | AsyncSmtp) {.multisync.} =
|
||||
## Disconnects from the SMTP server and closes the socket.
|
||||
await smtp.sock.send("QUIT\c\L")
|
||||
await smtp.debugSend("QUIT\c\L")
|
||||
smtp.sock.close()
|
||||
|
||||
when not defined(testing) and isMainModule:
|
||||
@@ -278,25 +223,24 @@ when not defined(testing) and isMainModule:
|
||||
|
||||
proc async_test() {.async.} =
|
||||
let client = newAsyncSmtp(
|
||||
conf["smtphost"],
|
||||
conf["port"].parseInt.Port,
|
||||
conf["use_tls"].parseBool
|
||||
conf["use_tls"].parseBool,
|
||||
debug=true
|
||||
)
|
||||
await client.connect()
|
||||
await client.connect(conf["smtphost"], conf["port"].parseInt.Port)
|
||||
await client.auth(conf["username"], conf["password"])
|
||||
await client.sendMail(conf["sender"], @[conf["recipient"]], $msg)
|
||||
await client.close()
|
||||
echo "async email sent"
|
||||
|
||||
proc sync_test() =
|
||||
var smtpConn = connect(
|
||||
conf["smtphost"],
|
||||
conf["port"].parseInt.Port,
|
||||
var smtpConn = newSmtp(
|
||||
conf["use_tls"].parseBool,
|
||||
true, # debug
|
||||
debug=true
|
||||
)
|
||||
smtpConn.connect(conf["smtphost"], conf["port"].parseInt.Port)
|
||||
smtpConn.auth(conf["username"], conf["password"])
|
||||
smtpConn.sendmail(conf["sender"], @[conf["recipient"]], $msg)
|
||||
smtpConn.sendMail(conf["sender"], @[conf["recipient"]], $msg)
|
||||
smtpConn.close()
|
||||
echo "sync email sent"
|
||||
|
||||
waitFor async_test()
|
||||
|
||||
@@ -417,7 +417,7 @@ type
|
||||
## Base exception class.
|
||||
##
|
||||
## Each exception has to inherit from `Exception`. See the full `exception
|
||||
## hierarchy`_.
|
||||
## hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
parent*: ref Exception ## parent exception (can be used as a stack)
|
||||
name*: cstring ## The exception's name is its Nim identifier.
|
||||
## This field is filled automatically in the
|
||||
@@ -430,51 +430,51 @@ type
|
||||
SystemError* = object of Exception ## \
|
||||
## Abstract class for exceptions that the runtime system raises.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
IOError* = object of SystemError ## \
|
||||
## Raised if an IO error occurred.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
EOFError* = object of IOError ## \
|
||||
## Raised if an IO "end of file" error occurred.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
OSError* = object of SystemError ## \
|
||||
## Raised if an operating system service failed.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
errorCode*: int32 ## OS-defined error code describing this error.
|
||||
LibraryError* = object of OSError ## \
|
||||
## Raised if a dynamic library could not be loaded.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
ResourceExhaustedError* = object of SystemError ## \
|
||||
## Raised if a resource request could not be fulfilled.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
ArithmeticError* = object of Exception ## \
|
||||
## Raised if any kind of arithmetic error occurred.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
DivByZeroError* = object of ArithmeticError ## \
|
||||
## Raised for runtime integer divide-by-zero errors.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
|
||||
OverflowError* = object of ArithmeticError ## \
|
||||
## Raised for runtime integer overflows.
|
||||
##
|
||||
## This happens for calculations whose results are too large to fit in the
|
||||
## provided bits. See the full `exception hierarchy`_.
|
||||
## provided bits. See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
AccessViolationError* = object of Exception ## \
|
||||
## Raised for invalid memory access errors
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
AssertionError* = object of Exception ## \
|
||||
## Raised when assertion is proved wrong.
|
||||
##
|
||||
## Usually the result of using the `assert() template <#assert>`_. See the
|
||||
## full `exception hierarchy`_.
|
||||
## full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
ValueError* = object of Exception ## \
|
||||
## Raised for string and object conversion errors.
|
||||
KeyError* = object of ValueError ## \
|
||||
@@ -482,66 +482,66 @@ type
|
||||
##
|
||||
## Mostly used by the `tables <tables.html>`_ module, it can also be raised
|
||||
## by other collection modules like `sets <sets.html>`_ or `strtabs
|
||||
## <strtabs.html>`_. See the full `exception hierarchy`_.
|
||||
## <strtabs.html>`_. See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
OutOfMemError* = object of SystemError ## \
|
||||
## Raised for unsuccessful attempts to allocate memory.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
IndexError* = object of Exception ## \
|
||||
## Raised if an array index is out of bounds.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
|
||||
FieldError* = object of Exception ## \
|
||||
## Raised if a record field is not accessible because its dicriminant's
|
||||
## value does not fit.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
RangeError* = object of Exception ## \
|
||||
## Raised if a range check error occurred.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
StackOverflowError* = object of SystemError ## \
|
||||
## Raised if the hardware stack used for subroutine calls overflowed.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
ReraiseError* = object of Exception ## \
|
||||
## Raised if there is no exception to reraise.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
ObjectAssignmentError* = object of Exception ## \
|
||||
## Raised if an object gets assigned to its parent's object.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
ObjectConversionError* = object of Exception ## \
|
||||
## Raised if an object is converted to an incompatible object type.
|
||||
## You can use ``of`` operator to check if conversion will succeed.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
FloatingPointError* = object of Exception ## \
|
||||
## Base class for floating point exceptions.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
FloatInvalidOpError* = object of FloatingPointError ## \
|
||||
## Raised by invalid operations according to IEEE.
|
||||
##
|
||||
## Raised by ``0.0/0.0``, for example. See the full `exception
|
||||
## hierarchy`_.
|
||||
## hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
FloatDivByZeroError* = object of FloatingPointError ## \
|
||||
## Raised by division by zero.
|
||||
##
|
||||
## Divisor is zero and dividend is a finite nonzero number. See the full
|
||||
## `exception hierarchy`_.
|
||||
## `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
FloatOverflowError* = object of FloatingPointError ## \
|
||||
## Raised for overflows.
|
||||
##
|
||||
## The operation produced a result that exceeds the range of the exponent.
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
FloatUnderflowError* = object of FloatingPointError ## \
|
||||
## Raised for underflows.
|
||||
##
|
||||
## The operation produced a result that is too small to be represented as a
|
||||
## normal number. See the full `exception hierarchy`_.
|
||||
## normal number. See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
FloatInexactError* = object of FloatingPointError ## \
|
||||
## Raised for inexact results.
|
||||
##
|
||||
@@ -549,11 +549,11 @@ type
|
||||
## precision -- for example: ``2.0 / 3.0, log(1.1)``
|
||||
##
|
||||
## **NOTE**: Nim currently does not detect these! See the full
|
||||
## `exception hierarchy`_.
|
||||
## `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
DeadThreadError* = object of Exception ## \
|
||||
## Raised if it is attempted to send a message to a dead thread.
|
||||
##
|
||||
## See the full `exception hierarchy`_.
|
||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||
|
||||
{.deprecated: [TObject: RootObj, PObject: RootRef, TEffect: RootEffect,
|
||||
FTime: TimeEffect, FIO: IOEffect, FReadIO: ReadIOEffect,
|
||||
|
||||
@@ -38,10 +38,10 @@ Options:
|
||||
--epc use emacs epc mode
|
||||
--debug enable debug output
|
||||
--log enable verbose logging to nimsuggest.log file
|
||||
--v2 use version 2 of the protocol; more features and
|
||||
much faster
|
||||
--v1 use version 1 of the protocol; for backwards compatibility
|
||||
--refresh perform automatic refreshes to keep the analysis precise
|
||||
--tester implies --v2 and --stdin and outputs a line
|
||||
--maxresults:N limit the number of suggestions to N
|
||||
--tester implies --stdin and outputs a line
|
||||
'""" & DummyEof & """' for the tester
|
||||
|
||||
The server then listens to the connection and takes line-based commands.
|
||||
@@ -50,7 +50,7 @@ In addition, all command line options of Nim that do not affect code generation
|
||||
are supported.
|
||||
"""
|
||||
type
|
||||
Mode = enum mstdin, mtcp, mepc, mcmdline
|
||||
Mode = enum mstdin, mtcp, mepc, mcmdsug, mcmdcon
|
||||
CachedMsg = object
|
||||
info: TLineInfo
|
||||
msg: string
|
||||
@@ -62,7 +62,7 @@ var
|
||||
gAddress = ""
|
||||
gMode: Mode
|
||||
gEmitEof: bool # whether we write '!EOF!' dummy lines
|
||||
gLogging = false
|
||||
gLogging = defined(logging)
|
||||
gRefresh: bool
|
||||
|
||||
requests: Channel[string]
|
||||
@@ -79,6 +79,9 @@ proc errorHook(info: TLineInfo; msg: string; sev: Severity) =
|
||||
line: toLinenumber(info), column: toColumn(info), doc: msg,
|
||||
forth: $sev))
|
||||
|
||||
proc myLog(s: string) =
|
||||
if gLogging: log(s)
|
||||
|
||||
const
|
||||
seps = {':', ';', ' ', '\t'}
|
||||
Help = "usage: sug|con|def|use|dus|chk|mod|highlight|outline|known file.nim[;dirtyfile.nim]:line:col\n" &
|
||||
@@ -155,17 +158,16 @@ proc symFromInfo(graph: ModuleGraph; gTrackPos: TLineInfo): PSym =
|
||||
|
||||
proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int;
|
||||
graph: ModuleGraph; cache: IdentCache) =
|
||||
if gLogging:
|
||||
log("cmd: " & $cmd & ", file: " & file & ", dirtyFile: " & dirtyfile &
|
||||
"[" & $line & ":" & $col & "]")
|
||||
myLog("cmd: " & $cmd & ", file: " & file & ", dirtyFile: " & dirtyfile &
|
||||
"[" & $line & ":" & $col & "]")
|
||||
gIdeCmd = cmd
|
||||
if cmd == ideChk:
|
||||
msgs.structuredErrorHook = errorHook
|
||||
msgs.writelnHook = proc (s: string) = discard
|
||||
msgs.writelnHook = myLog
|
||||
else:
|
||||
msgs.structuredErrorHook = nil
|
||||
msgs.writelnHook = proc (s: string) = discard
|
||||
if cmd == ideUse and suggestVersion != 2:
|
||||
msgs.writelnHook = myLog
|
||||
if cmd == ideUse and suggestVersion != 0:
|
||||
graph.resetAllModules()
|
||||
var isKnownFile = true
|
||||
let dirtyIdx = file.fileInfoIdx(isKnownFile)
|
||||
@@ -174,12 +176,13 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int;
|
||||
else: msgs.setDirtyFile(dirtyIdx, nil)
|
||||
|
||||
gTrackPos = newLineInfo(dirtyIdx, line, col)
|
||||
gTrackPosAttached = false
|
||||
gErrorCounter = 0
|
||||
if suggestVersion < 2:
|
||||
if suggestVersion == 1:
|
||||
graph.usageSym = nil
|
||||
if not isKnownFile:
|
||||
graph.compileProject(cache)
|
||||
if suggestVersion == 2 and gIdeCmd in {ideUse, ideDus} and
|
||||
if suggestVersion == 0 and gIdeCmd in {ideUse, ideDus} and
|
||||
dirtyfile.len == 0:
|
||||
discard "no need to recompile anything"
|
||||
else:
|
||||
@@ -189,7 +192,7 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int;
|
||||
if gIdeCmd != ideMod:
|
||||
graph.compileProject(cache, modIdx)
|
||||
if gIdeCmd in {ideUse, ideDus}:
|
||||
let u = if suggestVersion >= 2: graph.symFromInfo(gTrackPos) else: graph.usageSym
|
||||
let u = if suggestVersion != 1: graph.symFromInfo(gTrackPos) else: graph.usageSym
|
||||
if u != nil:
|
||||
listUsages(u)
|
||||
else:
|
||||
@@ -352,8 +355,7 @@ proc replEpc(x: ThreadParams) {.thread.} =
|
||||
setVerbosity(0)
|
||||
else: discard
|
||||
let cmd = $gIdeCmd & " " & args.argsToStr
|
||||
if gLogging:
|
||||
log "MSG CMD: " & cmd
|
||||
myLog "MSG CMD: " & cmd
|
||||
requests.send(cmd)
|
||||
toEpc(client, uid)
|
||||
of "methods":
|
||||
@@ -424,7 +426,7 @@ proc execCmd(cmd: string; graph: ModuleGraph; cache: IdentCache; cachedMsgs: Cac
|
||||
else:
|
||||
if gIdeCmd == ideChk:
|
||||
for cm in cachedMsgs: errorHook(cm.info, cm.msg, cm.sev)
|
||||
execute(gIdeCmd, orig, dirtyfile, line, col-1, graph, cache)
|
||||
execute(gIdeCmd, orig, dirtyfile, line, col, graph, cache)
|
||||
sentinel()
|
||||
|
||||
proc recompileFullProject(graph: ModuleGraph; cache: IdentCache) =
|
||||
@@ -502,8 +504,10 @@ proc mainCommand(graph: ModuleGraph; cache: IdentCache) =
|
||||
of mstdin: createThread(inputThread, replStdin, (gPort, gAddress))
|
||||
of mtcp: createThread(inputThread, replTcp, (gPort, gAddress))
|
||||
of mepc: createThread(inputThread, replEpc, (gPort, gAddress))
|
||||
of mcmdline: createThread(inputThread, replCmdline,
|
||||
of mcmdsug: createThread(inputThread, replCmdline,
|
||||
(gPort, "sug \"" & options.gProjectFull & "\":" & gAddress))
|
||||
of mcmdcon: createThread(inputThread, replCmdline,
|
||||
(gPort, "con \"" & options.gProjectFull & "\":" & gAddress))
|
||||
mainThread(graph, cache)
|
||||
joinThread(inputThread)
|
||||
close(requests)
|
||||
@@ -524,17 +528,21 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
|
||||
gAddress = p.val
|
||||
gMode = mtcp
|
||||
of "stdin": gMode = mstdin
|
||||
of "cmdline":
|
||||
gMode = mcmdline
|
||||
suggestVersion = 2
|
||||
of "cmdsug":
|
||||
gMode = mcmdsug
|
||||
gAddress = p.val
|
||||
incl(gGlobalOptions, optIdeDebug)
|
||||
of "cmdcon":
|
||||
gMode = mcmdcon
|
||||
gAddress = p.val
|
||||
incl(gGlobalOptions, optIdeDebug)
|
||||
of "epc":
|
||||
gMode = mepc
|
||||
gVerbosity = 0 # Port number gotta be first.
|
||||
of "debug": incl(gGlobalOptions, optIdeDebug)
|
||||
of "v2": suggestVersion = 2
|
||||
of "v2": suggestVersion = 0
|
||||
of "v1": suggestVersion = 1
|
||||
of "tester":
|
||||
suggestVersion = 2
|
||||
gMode = mstdin
|
||||
gEmitEof = true
|
||||
gRefresh = false
|
||||
@@ -544,9 +552,17 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
|
||||
gRefresh = parseBool(p.val)
|
||||
else:
|
||||
gRefresh = true
|
||||
of "maxresults":
|
||||
suggestMaxResults = parseInt(p.val)
|
||||
else: processSwitch(pass, p)
|
||||
of cmdArgument:
|
||||
options.gProjectName = unixToNativePath(p.key)
|
||||
let a = unixToNativePath(p.key)
|
||||
if dirExists(a) and not fileExists(a.addFileExt("nim")):
|
||||
options.gProjectName = findProjectNimFile(a)
|
||||
# don't make it worse, report the error the old way:
|
||||
if options.gProjectName.len == 0: options.gProjectName = a
|
||||
else:
|
||||
options.gProjectName = a
|
||||
# if processArgument(pass, p, argsCount): break
|
||||
|
||||
proc handleCmdLine(cache: IdentCache; config: ConfigRef) =
|
||||
@@ -574,8 +590,7 @@ proc handleCmdLine(cache: IdentCache; config: ConfigRef) =
|
||||
"Cannot find Nim standard library: Nim compiler not in PATH")
|
||||
gPrefixDir = binaryPath.splitPath().head.parentDir()
|
||||
#msgs.writelnHook = proc (line: string) = log(line)
|
||||
if gLogging:
|
||||
log("START " & gProjectFull)
|
||||
myLog("START " & gProjectFull)
|
||||
|
||||
loadConfigs(DefaultConfig, cache, config) # load all config files
|
||||
# now process command line arguments again, because some options in the
|
||||
@@ -22,4 +22,4 @@ define:nimsuggest
|
||||
--path:"$nim"
|
||||
--threads:on
|
||||
--noNimblePath
|
||||
--path:"../../compiler"
|
||||
--path:"../compiler"
|
||||
@@ -30,8 +30,8 @@ proc parseTest(filename: string; epcMode=false): Test =
|
||||
var markers = newSeq[string]()
|
||||
var i = 1
|
||||
for x in lines(filename):
|
||||
let marker = x.find(cursorMarker)+1
|
||||
if marker > 0:
|
||||
let marker = x.find(cursorMarker)
|
||||
if marker >= 0:
|
||||
if epcMode:
|
||||
markers.add "(\"" & filename & "\" " & $i & " " & $marker & " \"" & result.dest & "\")"
|
||||
else:
|
||||
@@ -304,8 +304,7 @@ proc runTest(filename: string): int =
|
||||
proc main() =
|
||||
var failures = 0
|
||||
if os.paramCount() > 0:
|
||||
let f = os.paramStr(1)
|
||||
let x = getAppDir() / f
|
||||
let x = os.paramStr(1)
|
||||
let xx = expandFilename x
|
||||
failures += runTest(xx)
|
||||
failures += runEpcTest(xx)
|
||||
17
nimsuggest/tests/tcase.nim
Normal file
17
nimsuggest/tests/tcase.nim
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
type
|
||||
MyEnum = enum
|
||||
nkIf, nkElse, nkElif
|
||||
|
||||
proc test(a: MyEnum) =
|
||||
case a
|
||||
of nkElse: discard
|
||||
of #[!]#
|
||||
|
||||
discard """
|
||||
$nimsuggest --tester $file
|
||||
>sug $1
|
||||
sug;;skEnumField;;nkElse;;MyEnum;;$file;;4;;10;;"";;100;;None
|
||||
sug;;skEnumField;;nkElif;;MyEnum;;$file;;4;;18;;"";;100;;None
|
||||
sug;;skEnumField;;nkIf;;MyEnum;;$file;;4;;4;;"";;100;;None
|
||||
"""
|
||||
13
nimsuggest/tests/tcon1.nim
Normal file
13
nimsuggest/tests/tcon1.nim
Normal file
@@ -0,0 +1,13 @@
|
||||
proc test(s: string; a: int) = discard
|
||||
proc testB(a, b: string) = discard
|
||||
test("hello here", #[!]#)
|
||||
testB(#[!]#
|
||||
|
||||
|
||||
discard """
|
||||
$nimsuggest --tester $file
|
||||
>con $1
|
||||
con;;skProc;;tcon1.test;;proc (s: string, a: int);;$file;;1;;5;;"";;100
|
||||
>con $2
|
||||
con;;skProc;;tcon1.testB;;proc (a: string, b: string);;$file;;2;;5;;"";;100
|
||||
"""
|
||||
@@ -11,4 +11,4 @@ type
|
||||
x, y: int
|
||||
|
||||
proc main(f: Foo) =
|
||||
f.#[!]#
|
||||
if f.#[!]#:
|
||||
16
nimsuggest/tests/tdot4.nim
Normal file
16
nimsuggest/tests/tdot4.nim
Normal file
@@ -0,0 +1,16 @@
|
||||
discard """
|
||||
$nimsuggest --tester --maxresults:2 $file
|
||||
>sug $1
|
||||
sug;;skProc;;tdot4.main;;proc (inp: string): string;;$file;;10;;5;;"";;100;;None
|
||||
sug;;skProc;;strutils.replace;;proc (s: string, sub: string, by: string): string{.noSideEffect, gcsafe, locks: 0.};;$lib/pure/strutils.nim;;1497;;5;;"Replaces `sub` in `s` by the string `by`.";;100;;None
|
||||
"""
|
||||
|
||||
import strutils
|
||||
|
||||
proc main(inp: string): string =
|
||||
# use replace here and see if it occurs in the result, it should gain
|
||||
# priority:
|
||||
result = inp.replace(" ", "a").replace("b", "c")
|
||||
|
||||
|
||||
echo "string literal here".#[!]#
|
||||
@@ -1,7 +1,7 @@
|
||||
discard """
|
||||
$nimsuggest --tester compiler/nim.nim
|
||||
>def compiler/semexprs.nim:13:50
|
||||
def;;skType;;ast.PSym;;PSym;;*ast.nim;;669;;2;;"";;100
|
||||
def;;skType;;ast.PSym;;PSym;;*ast.nim;;671;;2;;"";;100
|
||||
>def compiler/semexprs.nim:13:50
|
||||
def;;skType;;ast.PSym;;PSym;;*ast.nim;;669;;2;;"";;100
|
||||
def;;skType;;ast.PSym;;PSym;;*ast.nim;;671;;2;;"";;100
|
||||
"""
|
||||
15
nimsuggest/tests/ttype_decl.nim
Normal file
15
nimsuggest/tests/ttype_decl.nim
Normal file
@@ -0,0 +1,15 @@
|
||||
discard """
|
||||
$nimsuggest --tester --maxresults:3 $file
|
||||
>sug $1
|
||||
sug;;skType;;ttype_decl.Other;;Other;;$file;;10;;2;;"";;0;;None
|
||||
sug;;skType;;system.int;;int;;$lib/system.nim;;25;;2;;"";;0;;None
|
||||
sug;;skType;;system.string;;string;;$lib/system.nim;;48;;2;;"";;0;;None
|
||||
"""
|
||||
import strutils
|
||||
type
|
||||
Other = object ## My other object.
|
||||
Foo = #[!]#
|
||||
|
||||
proc main(f: Foo) =
|
||||
|
||||
# XXX why no doc comments?
|
||||
17
tests/errmsgs/t5167_1.nim
Normal file
17
tests/errmsgs/t5167_1.nim
Normal file
@@ -0,0 +1,17 @@
|
||||
discard """
|
||||
errormsg: "'bar' doesn't have a concrete type, due to unspecified generic parameters."
|
||||
line: 16
|
||||
"""
|
||||
|
||||
proc foo[T]() =
|
||||
var y1 = foo[string]
|
||||
var y2 = foo[T]
|
||||
|
||||
proc bar[T]() =
|
||||
let x = 0
|
||||
|
||||
let good1 = foo[int]
|
||||
let good2 = bar[int]
|
||||
|
||||
let err = bar
|
||||
|
||||
12
tests/errmsgs/t5167_2.nim
Normal file
12
tests/errmsgs/t5167_2.nim
Normal file
@@ -0,0 +1,12 @@
|
||||
discard """
|
||||
cmd: "nim c --threads:on $file"
|
||||
errormsg: "'threadFunc' doesn't have a concrete type, due to unspecified generic parameters."
|
||||
line: 11
|
||||
"""
|
||||
|
||||
proc threadFunc[T]() {.thread.} =
|
||||
let x = 0
|
||||
|
||||
var thr: Thread[void]
|
||||
thr.createThread(threadFunc)
|
||||
|
||||
25
tests/errmsgs/t5167_3.nim
Normal file
25
tests/errmsgs/t5167_3.nim
Normal file
@@ -0,0 +1,25 @@
|
||||
discard """
|
||||
cmd: "nim c --threads:on $file"
|
||||
errormsg: "type mismatch"
|
||||
line: 24
|
||||
"""
|
||||
|
||||
type
|
||||
TGeneric[T] = object
|
||||
x: int
|
||||
|
||||
proc foo1[A, B, C, D](x: proc (a: A, b: B, c: C, d: D)) =
|
||||
echo "foo1"
|
||||
|
||||
proc foo2(x: proc(x: int)) =
|
||||
echo "foo2"
|
||||
|
||||
# The goal of this test is to verify that none of the generic parameters of the
|
||||
# proc will be marked as unused. The error message should be "type mismatch" instead
|
||||
# of "'bar' doesn't have a concrete type, due to unspecified generic parameters".
|
||||
proc bar[A, B, C, D](x: A, y: seq[B], z: array[4, TGeneric[C]], r: TGeneric[D]) =
|
||||
echo "bar"
|
||||
|
||||
foo1[int, seq[int], array[4, TGeneric[float]], TGeneric[string]] bar
|
||||
foo2 bar
|
||||
|
||||
20
tests/errmsgs/t5167_4.nim
Normal file
20
tests/errmsgs/t5167_4.nim
Normal file
@@ -0,0 +1,20 @@
|
||||
discard """
|
||||
errormsg: "type mismatch: got (proc [*missing parameters*](x: int) | proc (x: string){.gcsafe, locks: 0.})"
|
||||
line: 19
|
||||
"""
|
||||
|
||||
type
|
||||
TGeneric[T] = object
|
||||
x: int
|
||||
|
||||
proc foo[B](x: int) =
|
||||
echo "foo1"
|
||||
|
||||
proc foo(x: string) =
|
||||
echo "foo2"
|
||||
|
||||
proc bar(x: proc (x: int)) =
|
||||
echo "bar"
|
||||
|
||||
bar foo
|
||||
|
||||
25
tests/errmsgs/t5167_5.nim
Normal file
25
tests/errmsgs/t5167_5.nim
Normal file
@@ -0,0 +1,25 @@
|
||||
discard """
|
||||
cmd: "nim check $file"
|
||||
errormsg: "'m' has unspecified generic parameters"
|
||||
nimout: '''
|
||||
t5167_5.nim(20, 9) Error: 't' has unspecified generic parameters
|
||||
t5167_5.nim(21, 5) Error: 't' has unspecified generic parameters
|
||||
t5167_5.nim(23, 9) Error: 'm' has unspecified generic parameters
|
||||
t5167_5.nim(24, 5) Error: 'm' has unspecified generic parameters
|
||||
'''
|
||||
"""
|
||||
|
||||
template t[B]() =
|
||||
echo "foo1"
|
||||
|
||||
macro m[T]: stmt = nil
|
||||
|
||||
proc bar(x: proc (x: int)) =
|
||||
echo "bar"
|
||||
|
||||
let x = t
|
||||
bar t
|
||||
|
||||
let y = m
|
||||
bar m
|
||||
|
||||
47
tests/errmsgs/tnon_concrete_cast.nim
Normal file
47
tests/errmsgs/tnon_concrete_cast.nim
Normal file
@@ -0,0 +1,47 @@
|
||||
discard """
|
||||
errormsg: "cannot cast to a non concrete type: 'ptr SomeNumber'"
|
||||
line: 36
|
||||
"""
|
||||
|
||||
# https://github.com/nim-lang/Nim/issues/5428
|
||||
|
||||
type
|
||||
MemFile = object
|
||||
mem: pointer
|
||||
|
||||
proc memfileopen(filename: string, newFileSize: int): MemFile =
|
||||
# just a memfile mock
|
||||
return
|
||||
|
||||
type
|
||||
MyData = object
|
||||
member1: seq[int]
|
||||
member2: int
|
||||
|
||||
type
|
||||
MyReadWrite = object
|
||||
memfile: MemFile
|
||||
offset: int
|
||||
|
||||
# Here, SomeNumber is bound to a concrete type, and that's OK
|
||||
proc write(rw: var MyReadWrite; value: SomeNumber): void =
|
||||
(cast[ptr SomeNumber](cast[uint](rw.memfile.mem) + rw.offset.uint))[] = value
|
||||
rw.offset += sizeof(SomeNumber)
|
||||
|
||||
# Here, we try to use SomeNumber without binding it to a type. This should
|
||||
# produce an error message for now. It's also possible to relax the rules
|
||||
# and allow for type-class based type inference in such situations.
|
||||
proc write[T](rw: var MyReadWrite; value: seq[T]): void =
|
||||
rw.write value.len
|
||||
let dst = cast[ptr SomeNumber](cast[uint](rw.memfile.mem) + uint(rw.offset))
|
||||
let src = cast[pointer](value[0].unsafeAddr)
|
||||
let size = sizeof(T) * value.len
|
||||
copyMem(dst, src, size)
|
||||
rw.offset += size
|
||||
|
||||
proc saveBinFile(arg: var MyData, filename: string): void =
|
||||
var rw: MyReadWrite
|
||||
rw.memfile = memfileOpen(filename, newFileSize = rw.offset)
|
||||
rw.offset = 0
|
||||
rw.write arg.member1
|
||||
|
||||
@@ -5,7 +5,7 @@ discard """
|
||||
import
|
||||
macros, strutils
|
||||
|
||||
macro test_macro*(n: stmt): stmt {.immediate.} =
|
||||
macro test_macro*(s: string, n: stmt): stmt {.immediate.} =
|
||||
result = newNimNode(nnkStmtList)
|
||||
var ass : NimNode = newNimNode(nnkAsgn)
|
||||
add(ass, newIdentNode("str"))
|
||||
|
||||
@@ -6,7 +6,7 @@ discard """
|
||||
|
||||
import macros
|
||||
|
||||
macro quoteWords(n: expr): expr {.immediate.} =
|
||||
macro quoteWords(n: varargs[expr]): expr {.immediate.} =
|
||||
let n = callsite()
|
||||
result = newNimNode(nnkBracket, n)
|
||||
for i in 1..n.len-1:
|
||||
|
||||
37
tests/overload/tparam_forwarding.nim
Normal file
37
tests/overload/tparam_forwarding.nim
Normal file
@@ -0,0 +1,37 @@
|
||||
discard """
|
||||
output: '''baz
|
||||
10
|
||||
100
|
||||
1000
|
||||
a
|
||||
b
|
||||
c
|
||||
'''
|
||||
"""
|
||||
|
||||
type
|
||||
Foo = object
|
||||
x: int
|
||||
|
||||
proc stringVarargs*(strings: varargs[string, `$`]): void =
|
||||
for s in strings: echo s
|
||||
|
||||
proc fooVarargs*(foos: varargs[Foo]) =
|
||||
for f in foos: echo f.x
|
||||
|
||||
template templateForwarding*(callable: untyped,
|
||||
condition: bool,
|
||||
forwarded: varargs[untyped]): untyped =
|
||||
if condition:
|
||||
callable(forwarded)
|
||||
|
||||
proc procForwarding(args: varargs[string]) =
|
||||
stringVarargs(args)
|
||||
|
||||
templateForwarding stringVarargs, 17 + 4 < 21, "foo", "bar", 100
|
||||
templateForwarding stringVarargs, 10 < 21, "baz"
|
||||
|
||||
templateForwarding fooVarargs, "test".len > 3, Foo(x: 10), Foo(x: 100), Foo(x: 1000)
|
||||
|
||||
procForwarding "a", "b", "c"
|
||||
|
||||
@@ -18,7 +18,7 @@ mm = memfiles.open(fn, mode = fmReadWrite, newFileSize = 20)
|
||||
mm.close()
|
||||
|
||||
# read, change
|
||||
mm_full = memfiles.open(fn, mode = fmWrite, mappedSize = -1)
|
||||
mm_full = memfiles.open(fn, mode = fmWrite, mappedSize = -1, allowRemap = true)
|
||||
echo "Full read size: ",mm_full.size
|
||||
p = mm_full.mapMem(fmReadWrite, 20, 0)
|
||||
var p2 = cast[cstring](p)
|
||||
|
||||
14
tools/nimgrind.supp
Normal file
14
tools/nimgrind.supp
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
markstackandregisters_Cond
|
||||
Memcheck:Cond
|
||||
...
|
||||
fun:markStackAndRegisters*
|
||||
...
|
||||
}
|
||||
{
|
||||
markstackandregisters_Value8
|
||||
Memcheck:Value8
|
||||
...
|
||||
fun:markStackAndRegisters*
|
||||
...
|
||||
}
|
||||
@@ -23,6 +23,12 @@ Changes affecting backwards compatibility
|
||||
pointer. Now the hash is calculated from the contents of the string, assuming
|
||||
``cstring`` is a null-terminated string. Equal ``string`` and ``cstring``
|
||||
values produce an equal hash value.
|
||||
- Macros accepting `varargs` arguments will now receive a node having the
|
||||
`nkArgList` node kind. Previous code expecting the node kind to be `nkBracket`
|
||||
may have to be updated.
|
||||
- ``memfiles.open`` now closes file handleds/fds by default. Passing
|
||||
``allowRemap=true`` to ``memfiles.open`` recovers the old behavior. The old
|
||||
behavior is only needed to call ``mapMem`` on the resulting ``MemFile``.
|
||||
|
||||
Library Additions
|
||||
-----------------
|
||||
|
||||
Reference in New Issue
Block a user