Merge branch 'devel' of github.com:nim-lang/Nim into devel

This commit is contained in:
Araq 2017-03-14 15:56:08 +01:00
commit b1c494a150
62 changed files with 818 additions and 442 deletions

View file

@ -460,6 +460,8 @@ type
# proc foo(T: typedesc, list: seq[T]): var T # proc foo(T: typedesc, list: seq[T]): var T
# proc foo(L: static[int]): array[L, int] # proc foo(L: static[int]): array[L, int]
# can be attached to ranges to indicate that the range # can be attached to ranges to indicate that the range
# can be attached to generic procs with free standing
# type parameters: e.g. proc foo[T]()
# depends on unresolved static params. # depends on unresolved static params.
tfRetType, # marks return types in proc (used to detect type classes tfRetType, # marks return types in proc (used to detect type classes
# used as return types for return type inference) # used as return types for return type inference)

View file

@ -67,6 +67,23 @@ proc debug*(n: PSym) {.deprecated.}
proc debug*(n: PType) {.deprecated.} proc debug*(n: PType) {.deprecated.}
proc debug*(n: PNode) {.deprecated.} proc debug*(n: PNode) {.deprecated.}
template mdbg*: bool {.dirty.} =
when compiles(c.module):
c.module.fileIdx == gProjectMainIdx
elif compiles(m.c.module):
m.c.module.fileIdx == gProjectMainIdx
elif compiles(cl.c.module):
cl.c.module.fileIdx == gProjectMainIdx
elif compiles(p):
when compiles(p.lex):
p.lex.fileIdx == gProjectMainIdx
else:
p.module.module.fileIdx == gProjectMainIdx
elif compiles(L.fileIdx):
L.fileIdx == gProjectMainIdx
else:
false
# --------------------------- ident tables ---------------------------------- # --------------------------- ident tables ----------------------------------
proc idTableGet*(t: TIdTable, key: PIdObj): RootRef proc idTableGet*(t: TIdTable, key: PIdObj): RootRef
proc idTableGet*(t: TIdTable, key: int): RootRef proc idTableGet*(t: TIdTable, key: int): RootRef

View file

@ -80,9 +80,14 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode =
expectedRegularParams = <s.typ.len expectedRegularParams = <s.typ.len
givenRegularParams = totalParams - genericParams givenRegularParams = totalParams - genericParams
if givenRegularParams < 0: givenRegularParams = 0 if givenRegularParams < 0: givenRegularParams = 0
if totalParams > expectedRegularParams + genericParams: if totalParams > expectedRegularParams + genericParams:
globalError(n.info, errWrongNumberOfArguments) globalError(n.info, errWrongNumberOfArguments)
if totalParams < genericParams:
globalError(n.info, errMissingGenericParamsForTemplate,
n.renderTree)
result = newNodeI(nkArgList, n.info) result = newNodeI(nkArgList, n.info)
for i in 1 .. givenRegularParams: for i in 1 .. givenRegularParams:
result.addSon n.sons[i] result.addSon n.sons[i]

View file

@ -67,6 +67,10 @@ type
TTokTypes* = set[TTokType] TTokTypes* = set[TTokType]
const const
weakTokens = {tkComma, tkSemiColon, tkColon,
tkParRi, tkParDotRi, tkBracketRi, tkBracketDotRi,
tkCurlyRi} # \
# tokens that should not be considered for previousToken
tokKeywordLow* = succ(tkSymbol) tokKeywordLow* = succ(tkSymbol)
tokKeywordHigh* = pred(tkIntLit) tokKeywordHigh* = pred(tkIntLit)
TokTypeToStr*: array[TTokType, string] = ["tkInvalid", "[EOF]", TokTypeToStr*: array[TTokType, string] = ["tkInvalid", "[EOF]",
@ -105,6 +109,9 @@ type
# so that it is the correct default value # so that it is the correct default value
base2, base8, base16 base2, base8, base16
CursorPosition* {.pure.} = enum ## XXX remove this again
None, InToken, BeforeToken, AfterToken
TToken* = object # a Nim token TToken* = object # a Nim token
tokType*: TTokType # the type of the token tokType*: TTokType # the type of the token
indent*: int # the indentation; != -1 if the token has been indent*: int # the indentation; != -1 if the token has been
@ -128,8 +135,11 @@ type
# needs so much look-ahead # needs so much look-ahead
currLineIndent*: int currLineIndent*: int
strongSpaces*, allowTabs*: bool strongSpaces*, allowTabs*: bool
cursor*: CursorPosition
errorHandler*: TErrorHandler errorHandler*: TErrorHandler
cache*: IdentCache cache*: IdentCache
when defined(nimsuggest):
previousToken: TLineInfo
var gLinesCompiled*: int # all lines that have been compiled 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 lex.currLineIndent = 0
inc(lex.lineNumber, inputstream.lineOffset) inc(lex.lineNumber, inputstream.lineOffset)
lex.cache = cache lex.cache = cache
when defined(nimsuggest):
lex.previousToken.fileIndex = fileIdx
proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream; proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream;
cache: IdentCache) = 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 = proc matchTwoChars(L: TLexer, first: char, second: set[char]): bool =
result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in second) 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.} {.push overflowChecks: off.}
# We need to parse the largest uint literal without overflow checks # We need to parse the largest uint literal without overflow checks
proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int = 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.literal = ""
result.base = base10 result.base = base10
startpos = L.bufpos startpos = L.bufpos
tokenBegin(startPos)
# First stage: find out base, make verifications, build token literal string # 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'}: 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) lexMessageLitNum(L, errInvalidNumber, startpos)
except OverflowError, RangeError: except OverflowError, RangeError:
lexMessageLitNum(L, errNumberOutOfRange, startpos) lexMessageLitNum(L, errNumberOutOfRange, startpos)
tokenEnd(postPos-1)
L.bufpos = postPos L.bufpos = postPos
proc handleHexChar(L: var TLexer, xi: var int) = 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) result = nimlexbase.handleLF(L, pos)
else: result = 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) = proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
var pos = L.bufpos + 1 # skip " var pos = L.bufpos + 1 # skip "
var buf = L.buf # put `buf` in a register var buf = L.buf # put `buf` in a register
var line = L.lineNumber # save linenumber for better error message var line = L.lineNumber # save linenumber for better error message
when defined(nimsuggest): tokenBegin(pos)
var colA = getColNumber(L, pos)
if buf[pos] == '\"' and buf[pos+1] == '\"': if buf[pos] == '\"' and buf[pos+1] == '\"':
tok.tokType = tkTripleStrLit # long string literal: tok.tokType = tkTripleStrLit # long string literal:
inc(pos, 2) # skip "" inc(pos, 2) # skip ""
@ -672,18 +711,18 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
of '\"': of '\"':
if buf[pos+1] == '\"' and buf[pos+2] == '\"' and if buf[pos+1] == '\"' and buf[pos+2] == '\"' and
buf[pos+3] != '\"': buf[pos+3] != '\"':
tokenRange(colA, pos+2) tokenEndIgnore(pos+2)
L.bufpos = pos + 3 # skip the three """ L.bufpos = pos + 3 # skip the three """
break break
add(tok.literal, '\"') add(tok.literal, '\"')
inc(pos) inc(pos)
of CR, LF: of CR, LF:
tokenRange(colA, pos) tokenEndIgnore(pos)
pos = handleCRLF(L, pos) pos = handleCRLF(L, pos)
buf = L.buf buf = L.buf
add(tok.literal, tnl) add(tok.literal, tnl)
of nimlexbase.EndOfFile: of nimlexbase.EndOfFile:
tokenRange(colA, pos) tokenEndIgnore(pos)
var line2 = L.lineNumber var line2 = L.lineNumber
L.lineNumber = line L.lineNumber = line
lexMessagePos(L, errClosingTripleQuoteExpected, L.lineStart) lexMessagePos(L, errClosingTripleQuoteExpected, L.lineStart)
@ -704,11 +743,11 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
inc(pos, 2) inc(pos, 2)
add(tok.literal, '"') add(tok.literal, '"')
else: else:
tokenRange(colA, pos) tokenEndIgnore(pos)
inc(pos) # skip '"' inc(pos) # skip '"'
break break
elif c in {CR, LF, nimlexbase.EndOfFile}: elif c in {CR, LF, nimlexbase.EndOfFile}:
tokenRange(colA, pos) tokenEndIgnore(pos)
lexMessage(L, errClosingQuoteExpected) lexMessage(L, errClosingQuoteExpected)
break break
elif (c == '\\') and not rawMode: elif (c == '\\') and not rawMode:
@ -721,6 +760,7 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
L.bufpos = pos L.bufpos = pos
proc getCharacter(L: var TLexer, tok: var TToken) = proc getCharacter(L: var TLexer, tok: var TToken) =
tokenBegin(L.bufpos)
inc(L.bufpos) # skip ' inc(L.bufpos) # skip '
var c = L.buf[L.bufpos] var c = L.buf[L.bufpos]
case c case c
@ -730,12 +770,14 @@ proc getCharacter(L: var TLexer, tok: var TToken) =
tok.literal = $c tok.literal = $c
inc(L.bufpos) inc(L.bufpos)
if L.buf[L.bufpos] != '\'': lexMessage(L, errMissingFinalQuote) if L.buf[L.bufpos] != '\'': lexMessage(L, errMissingFinalQuote)
tokenEndIgnore(L.bufpos)
inc(L.bufpos) # skip ' inc(L.bufpos) # skip '
proc getSymbol(L: var TLexer, tok: var TToken) = proc getSymbol(L: var TLexer, tok: var TToken) =
var h: Hash = 0 var h: Hash = 0
var pos = L.bufpos var pos = L.bufpos
var buf = L.buf var buf = L.buf
tokenBegin(pos)
while true: while true:
var c = buf[pos] var c = buf[pos]
case c case c
@ -762,6 +804,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) =
inc(pos) inc(pos)
else: break else: break
tokenEnd(pos-1)
h = !$h h = !$h
tok.ident = L.cache.getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h) tok.ident = L.cache.getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
L.bufpos = pos 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) = proc getOperator(L: var TLexer, tok: var TToken) =
var pos = L.bufpos var pos = L.bufpos
var buf = L.buf var buf = L.buf
tokenBegin(pos)
var h: Hash = 0 var h: Hash = 0
while true: while true:
var c = buf[pos] var c = buf[pos]
@ -789,6 +833,7 @@ proc getOperator(L: var TLexer, tok: var TToken) =
h = h !& ord(c) h = h !& ord(c)
inc(pos) inc(pos)
endOperator(L, tok, pos, h) endOperator(L, tok, pos, h)
tokenEnd(pos-1)
# advance pos but don't store it in L.bufpos so the next token (which might # advance pos but don't store it in L.bufpos so the next token (which might
# be an operator too) gets the preceding spaces: # be an operator too) gets the preceding spaces:
tok.strongSpaceB = 0 tok.strongSpaceB = 0
@ -803,8 +848,7 @@ proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
var pos = start var pos = start
var buf = L.buf var buf = L.buf
var toStrip = 0 var toStrip = 0
when defined(nimsuggest): tokenBegin(pos)
var colA = getColNumber(L, pos)
# detect the amount of indentation: # detect the amount of indentation:
if isDoc: if isDoc:
toStrip = getColNumber(L, pos) toStrip = getColNumber(L, pos)
@ -831,20 +875,20 @@ proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
if isDoc: if isDoc:
if buf[pos+1] == '#' and buf[pos+2] == '#': if buf[pos+1] == '#' and buf[pos+2] == '#':
if nesting == 0: if nesting == 0:
tokenRange(colA, pos+2) tokenEndIgnore(pos+2)
inc(pos, 3) inc(pos, 3)
break break
dec nesting dec nesting
tok.literal.add ']' tok.literal.add ']'
elif buf[pos+1] == '#': elif buf[pos+1] == '#':
if nesting == 0: if nesting == 0:
tokenRange(colA, pos+1) tokenEndIgnore(pos+1)
inc(pos, 2) inc(pos, 2)
break break
dec nesting dec nesting
inc pos inc pos
of CR, LF: of CR, LF:
tokenRange(colA, pos) tokenEndIgnore(pos)
pos = handleCRLF(L, pos) pos = handleCRLF(L, pos)
buf = L.buf buf = L.buf
# strip leading whitespace: # strip leading whitespace:
@ -856,7 +900,7 @@ proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
inc pos inc pos
dec c dec c
of nimlexbase.EndOfFile: of nimlexbase.EndOfFile:
tokenRange(colA, pos) tokenEndIgnore(pos)
lexMessagePos(L, errGenerated, pos, "end of multiline comment expected") lexMessagePos(L, errGenerated, pos, "end of multiline comment expected")
break break
else: else:
@ -867,8 +911,6 @@ proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
proc scanComment(L: var TLexer, tok: var TToken) = proc scanComment(L: var TLexer, tok: var TToken) =
var pos = L.bufpos var pos = L.bufpos
var buf = L.buf var buf = L.buf
when defined(nimsuggest):
var colA = getColNumber(L, pos)
tok.tokType = tkComment tok.tokType = tkComment
# iNumber contains the number of '\n' in the token # iNumber contains the number of '\n' in the token
tok.iNumber = 0 tok.iNumber = 0
@ -876,6 +918,7 @@ proc scanComment(L: var TLexer, tok: var TToken) =
if buf[pos+2] == '[': if buf[pos+2] == '[':
skipMultiLineComment(L, tok, pos+3, true) skipMultiLineComment(L, tok, pos+3, true)
return return
tokenBegin(pos)
inc(pos, 2) inc(pos, 2)
var toStrip = 0 var toStrip = 0
@ -889,7 +932,7 @@ proc scanComment(L: var TLexer, tok: var TToken) =
if buf[pos] == '\\': lastBackslash = pos+1 if buf[pos] == '\\': lastBackslash = pos+1
add(tok.literal, buf[pos]) add(tok.literal, buf[pos])
inc(pos) inc(pos)
tokenRange(colA, pos) tokenEndIgnore(pos)
pos = handleCRLF(L, pos) pos = handleCRLF(L, pos)
buf = L.buf buf = L.buf
var indent = 0 var indent = 0
@ -908,13 +951,14 @@ proc scanComment(L: var TLexer, tok: var TToken) =
else: else:
if buf[pos] > ' ': if buf[pos] > ' ':
L.indentAhead = indent L.indentAhead = indent
tokenRange(colA, pos) tokenEndIgnore(pos)
break break
L.bufpos = pos L.bufpos = pos
proc skip(L: var TLexer, tok: var TToken) = proc skip(L: var TLexer, tok: var TToken) =
var pos = L.bufpos var pos = L.bufpos
var buf = L.buf var buf = L.buf
tokenBegin(pos)
tok.strongSpaceA = 0 tok.strongSpaceA = 0
while true: while true:
case buf[pos] case buf[pos]
@ -925,6 +969,7 @@ proc skip(L: var TLexer, tok: var TToken) =
if not L.allowTabs: lexMessagePos(L, errTabulatorsAreNotAllowed, pos) if not L.allowTabs: lexMessagePos(L, errTabulatorsAreNotAllowed, pos)
inc(pos) inc(pos)
of CR, LF: of CR, LF:
tokenEndPrevious(pos)
pos = handleCRLF(L, pos) pos = handleCRLF(L, pos)
buf = L.buf buf = L.buf
var indent = 0 var indent = 0
@ -951,15 +996,24 @@ proc skip(L: var TLexer, tok: var TToken) =
pos = L.bufpos pos = L.bufpos
buf = L.buf buf = L.buf
else: else:
when defined(nimsuggest): tokenBegin(pos)
var colA = getColNumber(L, pos)
while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: inc(pos) while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: inc(pos)
tokenRange(colA, pos) tokenEndIgnore(pos+1)
else: else:
break # EndOfFile also leaves the loop break # EndOfFile also leaves the loop
tokenEndPrevious(pos-1)
L.bufpos = pos L.bufpos = pos
proc rawGetTok*(L: var TLexer, tok: var TToken) = 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) fillToken(tok)
if L.indentAhead >= 0: if L.indentAhead >= 0:
tok.indent = L.indentAhead tok.indent = L.indentAhead
@ -1007,6 +1061,10 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
inc(L.bufpos) inc(L.bufpos)
else: else:
tok.tokType = tkParLe 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 ')': of ')':
tok.tokType = tkParRi tok.tokType = tkParRi
inc(L.bufpos) inc(L.bufpos)
@ -1022,10 +1080,13 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
inc(L.bufpos) inc(L.bufpos)
of '.': of '.':
when defined(nimsuggest): when defined(nimsuggest):
if L.fileIdx == gTrackPos.fileIndex and tok.col == gTrackPos.col and if L.fileIdx == gTrackPos.fileIndex and tok.col+1 == gTrackPos.col and
tok.line == gTrackPos.line and gIdeCmd == ideSug: tok.line == gTrackPos.line and gIdeCmd == ideSug:
tok.tokType = tkDot tok.tokType = tkDot
L.cursor = CursorPosition.InToken
gTrackPos.col = tok.col.int16
inc(L.bufpos) inc(L.bufpos)
atTokenEnd()
return return
if L.buf[L.bufpos+1] == ']': if L.buf[L.bufpos+1] == ']':
tok.tokType = tkBracketDotRi tok.tokType = tkBracketDotRi
@ -1092,3 +1153,4 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
tok.tokType = tkInvalid tok.tokType = tkInvalid
lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')') lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')')
inc(L.bufpos) inc(L.bufpos)
atTokenEnd()

View file

@ -64,6 +64,8 @@ type
errVarForOutParamNeeded, errVarForOutParamNeeded,
errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX, errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX,
errAmbiguousCallXYZ, errWrongNumberOfArguments, errAmbiguousCallXYZ, errWrongNumberOfArguments,
errWrongNumberOfArgumentsInCall,
errMissingGenericParamsForTemplate,
errXCannotBePassedToProcVar, errXCannotBePassedToProcVar,
errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProcX, errImplOfXNotAllowed, errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProcX, errImplOfXNotAllowed,
errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX, errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX,
@ -89,6 +91,7 @@ type
errMainModuleMustBeSpecified, errMainModuleMustBeSpecified,
errXExpected, errXExpected,
errTIsNotAConcreteType, errTIsNotAConcreteType,
errCastToANonConcreteType,
errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError, errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,
errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly, errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly,
@ -107,6 +110,7 @@ type
errCannotInferTypeOfTheLiteral, errCannotInferTypeOfTheLiteral,
errCannotInferReturnType, errCannotInferReturnType,
errGenericLambdaNotAllowed, errGenericLambdaNotAllowed,
errProcHasNoConcreteType,
errCompilerDoesntSupportTarget, errCompilerDoesntSupportTarget,
errUser, errUser,
warnCannotOpenFile, warnCannotOpenFile,
@ -269,6 +273,8 @@ const
errButExpectedX: "but expected \'$1\'", errButExpectedX: "but expected \'$1\'",
errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3", errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3",
errWrongNumberOfArguments: "wrong number of arguments", errWrongNumberOfArguments: "wrong number of arguments",
errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'",
errMissingGenericParamsForTemplate: "'$1' has unspecified generic parameters",
errXCannotBePassedToProcVar: "\'$1\' cannot be passed to a procvar", errXCannotBePassedToProcVar: "\'$1\' cannot be passed to a procvar",
errXCannotBeInParamDecl: "$1 cannot be declared in parameter declaration", errXCannotBeInParamDecl: "$1 cannot be declared in parameter declaration",
errPragmaOnlyInHeaderOfProcX: "pragmas are only allowed in the header of a proc; redefinition of $1", errPragmaOnlyInHeaderOfProcX: "pragmas are only allowed in the header of a proc; redefinition of $1",
@ -326,6 +332,7 @@ const
errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file", errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file",
errXExpected: "\'$1\' expected", errXExpected: "\'$1\' expected",
errTIsNotAConcreteType: "\'$1\' is not a concrete type.", errTIsNotAConcreteType: "\'$1\' is not a concrete type.",
errCastToANonConcreteType: "cannot cast to a non concrete type: \'$1\'",
errInvalidSectionStart: "invalid section start", errInvalidSectionStart: "invalid section start",
errGridTableNotImplemented: "grid table is not implemented", errGridTableNotImplemented: "grid table is not implemented",
errGeneralParseError: "general parse error", errGeneralParseError: "general parse error",
@ -369,6 +376,7 @@ const
errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " & errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " &
"it is used as an operand to another routine and the types " & "it is used as an operand to another routine and the types " &
"of the generic paramers can be inferred from the expected signature.", "of the generic paramers can be inferred from the expected signature.",
errProcHasNoConcreteType: "'$1' doesn't have a concrete type, due to unspecified generic parameters.",
errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target", errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target",
errUser: "$1", errUser: "$1",
warnCannotOpenFile: "cannot open \'$1\'", warnCannotOpenFile: "cannot open \'$1\'",
@ -739,6 +747,8 @@ proc `??`* (info: TLineInfo, filename: string): bool =
const trackPosInvalidFileIdx* = -2 # special marker so that no suggestions const trackPosInvalidFileIdx* = -2 # special marker so that no suggestions
# are produced within comments and string literals # are produced within comments and string literals
var gTrackPos*: TLineInfo var gTrackPos*: TLineInfo
var gTrackPosAttached*: bool ## whether the tracking position was attached to some
## close token.
type type
MsgFlag* = enum ## flags altering msgWriteln behavior MsgFlag* = enum ## flags altering msgWriteln behavior
@ -863,6 +873,9 @@ proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) =
proc `==`*(a, b: TLineInfo): bool = proc `==`*(a, b: TLineInfo): bool =
result = a.line == b.line and a.fileIndex == b.fileIndex 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) = proc writeContext(lastinfo: TLineInfo) =
var info = lastinfo var info = lastinfo
for i in countup(0, len(msgContext) - 1): for i in countup(0, len(msgContext) - 1):

View file

@ -392,6 +392,23 @@ proc findModule*(modulename, currentModule: string): string =
result = findFile(m) result = findFile(m)
patchModule() 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 = proc canonDynlibName(s: string): string =
let start = if s.startsWith("lib"): 3 else: 0 let start = if s.startsWith("lib"): 3 else: 0
let ende = strutils.find(s, {'(', ')', '.'}) let ende = strutils.find(s, {'(', ')', '.'})
@ -420,11 +437,6 @@ proc binaryStrSearch*(x: openArray[string], y: string): int =
return mid return mid
result = - 1 result = - 1
template nimdbg*: untyped = c.module.fileIdx == gProjectMainIdx
template cnimdbg*: untyped = p.module.module.fileIdx == gProjectMainIdx
template pnimdbg*: untyped = p.lex.fileIdx == gProjectMainIdx
template lnimdbg*: untyped = L.fileIdx == gProjectMainIdx
proc parseIdeCmd*(s: string): IdeCmd = proc parseIdeCmd*(s: string): IdeCmd =
case s: case s:
of "sug": ideSug of "sug": ideSug

View file

@ -1846,6 +1846,7 @@ proc parseTypeDef(p: var TParser): PNode =
else: else:
addSon(result, ast.emptyNode) addSon(result, ast.emptyNode)
if p.tok.tokType == tkEquals: if p.tok.tokType == tkEquals:
result.info = parLineInfo(p)
getTok(p) getTok(p)
optInd(p, result) optInd(p, result)
addSon(result, parseTypeDefAux(p)) addSon(result, parseTypeDefAux(p))
@ -2002,12 +2003,12 @@ proc parseStmt(p: var TParser): PNode =
break break
p.hasProgress = false p.hasProgress = false
var a = complexOrSimpleStmt(p) var a = complexOrSimpleStmt(p)
if not p.hasProgress and p.tok.tokType == tkEof: break if a.kind != nkEmpty:
if a.kind != nkEmpty and p.hasProgress:
addSon(result, a) addSon(result, a)
else: else:
parMessage(p, errExprExpected, p.tok) parMessage(p, errExprExpected, p.tok)
getTok(p) getTok(p)
if not p.hasProgress and p.tok.tokType == tkEof: break
else: else:
# the case statement is only needed for better error messages: # the case statement is only needed for better error messages:
case p.tok.tokType case p.tok.tokType

View file

@ -167,6 +167,8 @@ proc commonType*(x, y: PType): PType =
proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym = proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
result = newSym(kind, considerQuotedIdent(n), getCurrOwner(c), n.info) 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 newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
proc `$`(kind: TSymKind): string = substr(system.`$`(kind), 2).toLowerAscii 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) result = newSym(kind, considerQuotedIdent(n), getCurrOwner(c), n.info)
#if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule: #if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule:
# incl(result.flags, sfGlobal) # incl(result.flags, sfGlobal)
when defined(nimsuggest):
suggestDecl(c, n, result)
proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
allowed: TSymFlags): PSym allowed: TSymFlags): PSym
@ -381,6 +385,13 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
if sym == c.p.owner: if sym == c.p.owner:
globalError(n.info, errRecursiveDependencyX, sym.name.s) globalError(n.info, errRecursiveDependencyX, sym.name.s)
let genericParams = if sfImmediate in sym.flags: 0
else: sym.ast[genericParamsPos].len
let suppliedParams = max(n.safeLen - 1, 0)
if suppliedParams < genericParams:
globalError(n.info, errMissingGenericParamsForTemplate, n.renderTree)
#if c.evalContext == nil: #if c.evalContext == nil:
# c.evalContext = c.createEvalContext(emStatic) # c.evalContext = c.createEvalContext(emStatic)
result = evalMacroCall(c.module, c.cache, n, nOrig, sym) result = evalMacroCall(c.module, c.cache, n, nOrig, sym)

View file

@ -411,6 +411,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
let tm = typeRel(m, formal, arg, true) let tm = typeRel(m, formal, arg, true)
if tm in {isNone, isConvertible}: return nil if tm in {isNone, isConvertible}: return nil
var newInst = generateInstance(c, s, m.bindings, n.info) var newInst = generateInstance(c, s, m.bindings, n.info)
newInst.typ.flags.excl tfUnresolved
markUsed(n.info, s, c.graph.usageSym) markUsed(n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s) styleCheckUse(n.info, s)
result = newSymNode(newInst, n.info) result = newSymNode(newInst, n.info)

View file

@ -30,6 +30,8 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
# result = errorNode(c, n) # result = errorNode(c, n)
if result.typ != nil: if result.typ != nil:
# XXX tyGenericInst here? # XXX tyGenericInst here?
if result.typ.kind == tyProc and tfUnresolved in result.typ.flags:
localError(n.info, errProcHasNoConcreteType, n.renderTree)
if result.typ.kind == tyVar: result = newDeref(result) if result.typ.kind == tyVar: result = newDeref(result)
elif {efWantStmt, efAllowStmt} * flags != {}: elif {efWantStmt, efAllowStmt} * flags != {}:
result.typ = newTypeS(tyVoid, c) result.typ = newTypeS(tyVoid, c)
@ -218,13 +220,16 @@ proc semConv(c: PContext, n: PNode): PNode =
proc semCast(c: PContext, n: PNode): PNode = proc semCast(c: PContext, n: PNode): PNode =
## Semantically analyze a casting ("cast[type](param)") ## Semantically analyze a casting ("cast[type](param)")
checkSonsLen(n, 2) checkSonsLen(n, 2)
let targetType = semTypeNode(c, n.sons[0], nil)
let castedExpr = semExprWithType(c, n.sons[1])
if tfHasMeta in targetType.flags:
localError(n.sons[0].info, errCastToANonConcreteType, $targetType)
if not isCastable(targetType, castedExpr.typ):
localError(n.info, errExprCannotBeCastToX, $targetType)
result = newNodeI(nkCast, n.info) result = newNodeI(nkCast, n.info)
result.typ = semTypeNode(c, n.sons[0], nil) result.typ = targetType
addSon(result, copyTree(n.sons[0])) addSon(result, copyTree(n.sons[0]))
addSon(result, semExprWithType(c, n.sons[1])) addSon(result, castedExpr)
if not isCastable(result.typ, result.sons[1].typ):
localError(result.info, errExprCannotBeCastToX,
typeToString(result.typ))
proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
const const
@ -1054,7 +1059,9 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
# here at all! # here at all!
#if isSymChoice(n.sons[1]): return #if isSymChoice(n.sons[1]): return
when defined(nimsuggest): 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}) var s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared, checkModule})
if s != nil: if s != nil:
@ -2234,6 +2241,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
# check if it is an expression macro: # check if it is an expression macro:
checkMinSonsLen(n, 1) 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} let mode = if nfDotField in n.flags: {} else: {checkUndeclared}
var s = qualifiedLookUp(c, n.sons[0], mode) var s = qualifiedLookUp(c, n.sons[0], mode)
if s != nil: if s != nil:

View file

@ -205,7 +205,8 @@ proc semCase(c: PContext, n: PNode): PNode =
var typ = commonTypeBegin var typ = commonTypeBegin
var hasElse = false var hasElse = false
var notOrdinal = 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: of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool:
chckCovered = true chckCovered = true
of tyFloat..tyFloat128, tyString, tyError: of tyFloat..tyFloat128, tyString, tyError:
@ -215,6 +216,9 @@ proc semCase(c: PContext, n: PNode): PNode =
return return
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
var x = n.sons[i] 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 case x.kind
of nkOfBranch: of nkOfBranch:
checkMinSonsLen(x, 2) checkMinSonsLen(x, 2)
@ -499,6 +503,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
if hasEmpty(typ): if hasEmpty(typ):
localError(def.info, errCannotInferTypeOfTheLiteral, localError(def.info, errCannotInferTypeOfTheLiteral,
($typ.kind).substr(2).toLowerAscii) ($typ.kind).substr(2).toLowerAscii)
elif typ.kind == tyProc and tfUnresolved in typ.flags:
localError(def.info, errProcHasNoConcreteType, def.renderTree)
else: else:
if symkind == skLet: localError(a.info, errLetNeedsInit) if symkind == skLet: localError(a.info, errLetNeedsInit)
@ -711,7 +717,11 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
# we even look at the type definitions on the right # we even look at the type definitions on the right
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i] 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 == nkCommentStmt: continue
if a.kind != nkTypeDef: illFormedAst(a) if a.kind != nkTypeDef: illFormedAst(a)
checkSonsLen(a, 3) checkSonsLen(a, 3)

View file

@ -1009,8 +1009,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
result.sons[0] = r result.sons[0] = r
result.n.typ = r result.n.typ = r
if genericParams != nil: if genericParams != nil and genericParams.len > 0:
for n in genericParams: for n in genericParams:
if {sfUsed, sfAnon} * n.sym.flags == {}:
result.flags.incl tfUnresolved
if tfWildcard in n.sym.typ.flags: if tfWildcard in n.sym.typ.flags:
n.sym.kind = skType n.sym.kind = skType
n.sym.typ.flags.excl tfWildcard n.sym.typ.flags.excl tfWildcard

View file

@ -1514,6 +1514,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
if arg.sons[i].sym.kind in {skProc, skMethod, skConverter, skIterator}: if arg.sons[i].sym.kind in {skProc, skMethod, skConverter, skIterator}:
copyCandidate(z, m) copyCandidate(z, m)
z.callee = arg.sons[i].typ z.callee = arg.sons[i].typ
if tfUnresolved in z.callee.flags: continue
z.calleeSym = arg.sons[i].sym z.calleeSym = arg.sons[i].sym
#if arg.sons[i].sym.name.s == "cmp": #if arg.sons[i].sym.name.s == "cmp":
# ggDebug = true # ggDebug = true
@ -1650,7 +1651,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
if a >= formalLen-1 and formal != nil and formal.typ.isVarargsUntyped: if a >= formalLen-1 and formal != nil and formal.typ.isVarargsUntyped:
incl(marker, formal.position) incl(marker, formal.position)
if container.isNil: if container.isNil:
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, n.info)) container = newNodeIT(nkArgList, n.sons[a].info, arrayConstr(c, n.info))
setSon(m.call, formal.position + 1, container) setSon(m.call, formal.position + 1, container)
else: else:
incrIndexType(container.typ) incrIndexType(container.typ)
@ -1738,7 +1739,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
if formal.typ.isVarargsUntyped: if formal.typ.isVarargsUntyped:
if container.isNil: if container.isNil:
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, n.info)) container = newNodeIT(nkArgList, n.sons[a].info, arrayConstr(c, n.info))
setSon(m.call, formal.position + 1, container) setSon(m.call, formal.position + 1, container)
else: else:
incrIndexType(container.typ) incrIndexType(container.typ)

View file

@ -63,6 +63,7 @@ type
var var
suggestionResultHook*: proc (result: Suggest) {.closure.} suggestionResultHook*: proc (result: Suggest) {.closure.}
suggestVersion*: int suggestVersion*: int
suggestMaxResults* = 10_000
#template sectionSuggest(): expr = "##begin\n" & getStackTrace() & "##end\n" #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 = proc findDocComment(n: PNode): PNode =
if n == nil: return nil if n == nil: return nil
if not isNil(n.comment): return n 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]) result = findDocComment(n.sons[0])
if result != nil: return if result != nil: return
if n.len > 1: if n.len > 1:
@ -104,11 +105,11 @@ proc cmpSuggestions(a, b: Suggest): int =
# independent of hashing order: # independent of hashing order:
result = cmp(a.name.s, b.name.s) 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; quality: range[0..100]; prefix: PrefixMatch;
inTypeContext: bool; scope: int): Suggest = inTypeContext: bool; scope: int): Suggest =
new(result) new(result)
result.section = parseIdeCmd(section) result.section = section
result.quality = quality result.quality = quality
result.isGlobal = sfGlobal in s.flags result.isGlobal = sfGlobal in s.flags
result.tokenLen = s.name.s.len 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 result.globalUsages = s.allUsages.len
var c = 0 var c = 0
for u in s.allUsages: for u in s.allUsages:
if u.fileIndex == li.fileIndex: inc c if u.fileIndex == info.fileIndex: inc c
result.localUsages = c result.localUsages = c
if optIdeTerse in gGlobalOptions: result.symkind = s.kind
result.symkind = s.kind if optIdeTerse notin gGlobalOptions:
result.filePath = toFullPath(li)
result.line = toLinenumber(li)
result.column = toColumn(li)
else:
result.symkind = s.kind
result.qualifiedPath = @[] result.qualifiedPath = @[]
if not isLocal and s.kind != skModule: if not isLocal and s.kind != skModule:
let ow = s.owner let ow = s.owner
@ -143,11 +139,12 @@ proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo;
result.forth = typeToString(s.typ) result.forth = typeToString(s.typ)
else: else:
result.forth = "" result.forth = ""
result.filePath = toFullPath(li)
result.line = toLinenumber(li)
result.column = toColumn(li)
when not defined(noDocgen): when not defined(noDocgen):
result.doc = s.extractDocComment 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 = proc `$`*(suggest: Suggest): string =
result = $suggest.section result = $suggest.section
@ -181,18 +178,13 @@ proc `$`*(suggest: Suggest): string =
result.add(sep) result.add(sep)
when not defined(noDocgen): when not defined(noDocgen):
result.add(suggest.doc.escape) result.add(suggest.doc.escape)
if suggestVersion == 2: if suggestVersion == 0:
result.add(sep) result.add(sep)
result.add($suggest.quality) result.add($suggest.quality)
if suggest.section == ideSug: if suggest.section == ideSug:
result.add(sep) result.add(sep)
result.add($suggest.prefix) 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) = proc suggestResult(s: Suggest) =
if not isNil(suggestionResultHook): if not isNil(suggestionResultHook):
suggestionResultHook(s) suggestionResultHook(s)
@ -202,6 +194,10 @@ proc suggestResult(s: Suggest) =
proc produceOutput(a: var Suggestions) = proc produceOutput(a: var Suggestions) =
if gIdeCmd in {ideSug, ideCon}: if gIdeCmd in {ideSug, ideCon}:
a.sort cmpSuggestions a.sort cmpSuggestions
when defined(debug):
# debug code
writeStackTrace()
if a.len > suggestMaxResults: a.setLen(suggestMaxResults)
if not isNil(suggestionResultHook): if not isNil(suggestionResultHook):
for s in a: for s in a:
suggestionResultHook(s) suggestionResultHook(s)
@ -237,10 +233,10 @@ proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
result = true result = true
break 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 var pm: PrefixMatch
if filterSym(s, f, pm) and fieldVisible(c, s): 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] = proc getQuality(s: PSym): range[0..100] =
if s.typ != nil and s.typ.len > 1: if s.typ != nil and s.typ.len > 1:
@ -259,25 +255,25 @@ template wholeSymTab(cond, section: untyped) =
let it {.inject.} = item let it {.inject.} = item
var pm {.inject.}: PrefixMatch var pm {.inject.}: PrefixMatch
if cond: 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)) 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): for i in countup(0, sonsLen(list) - 1):
if list.sons[i].kind == nkSym: 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") #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 case n.kind
of nkRecList: 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: of nkRecCase:
var L = sonsLen(n) var L = sonsLen(n)
if L > 0: if L > 0:
suggestObject(c, n.sons[0], f, outputs) suggestObject(c, n.sons[0], f, info, outputs)
for i in countup(1, L-1): suggestObject(c, lastSon(n.sons[i]), f, outputs) for i in countup(1, L-1): suggestObject(c, lastSon(n.sons[i]), f, info, outputs)
of nkSym: suggestField(c, n.sym, f, outputs) of nkSym: suggestField(c, n.sym, f, info, outputs)
else: discard else: discard
proc nameFits(c: PContext, s: PSym, n: PNode): bool = 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 result = false
proc suggestCall(c: PContext, n, nOrig: PNode, outputs: var Suggestions) = 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), 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.} = 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: 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) = proc suggestOperations(c: PContext, n, f: PNode, typ: PType, outputs: var Suggestions) =
assert typ != nil 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) = proc suggestEverything(c: PContext, n, f: PNode, outputs: var Suggestions) =
# do not produce too many symbols: # 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): for it in items(scope.symbols):
var pm: PrefixMatch var pm: PrefixMatch
if filterSym(it, f, pm): if filterSym(it, f, pm):
outputs.add(symToSuggest(it, isLocal = isLocal, $ideSug, 0, pm, c.inTypeContext > 0, scopeN)) outputs.add(symToSuggest(it, isLocal = isLocal, ideSug, n.info, 0, pm,
if scope == c.topLevelScope and f.isNil: break c.inTypeContext > 0, scopeN))
#if scope == c.topLevelScope and f.isNil: break
proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) = proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) =
# special code that deals with ``myObj.``. `n` is NOT the nkDotExpr-node, but # 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 typ = n.typ
var pm: PrefixMatch var pm: PrefixMatch
when defined(nimsuggest): 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. # consider 'foo.|' where 'foo' is some not imported module.
let fullPath = findModule(n.sym.name.s, n.info.toFullPath) let fullPath = findModule(n.sym.name.s, n.info.toFullPath)
if fullPath.len == 0: if fullPath.len == 0:
@ -352,8 +351,8 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
else: else:
for it in items(n.sym.tab): for it in items(n.sym.tab):
if filterSym(it, field, pm): if filterSym(it, field, pm):
outputs.add(symToSuggest(it, isLocal=false, $ideSug, 100, pm, c.inTypeContext > 0, -100)) outputs.add(symToSuggest(it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -100))
outputs.add(symToSuggest(m, isLocal=false, $ideMod, 100, PrefixMatch.None, outputs.add(symToSuggest(m, isLocal=false, ideMod, n.info, 100, PrefixMatch.None,
c.inTypeContext > 0, -99)) c.inTypeContext > 0, -99))
if typ == nil: 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: # all symbols accessible, because we are in the current module:
for it in items(c.topLevelScope.symbols): for it in items(c.topLevelScope.symbols):
if filterSym(it, field, pm): 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: else:
for it in items(n.sym.tab): for it in items(n.sym.tab):
if filterSym(it, field, pm): 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: else:
# fallback: # fallback:
suggestEverything(c, n, field, outputs) 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: # look up if the identifier belongs to the enum:
var t = typ var t = typ
while t != nil: while t != nil:
suggestSymList(c, t.n, field, outputs) suggestSymList(c, t.n, field, n.info, outputs)
t = t.sons[0] t = t.sons[0]
suggestOperations(c, n, field, typ, outputs) suggestOperations(c, n, field, typ, outputs)
else: else:
@ -384,11 +383,11 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
if typ.kind == tyObject: if typ.kind == tyObject:
var t = typ var t = typ
while true: while true:
suggestObject(c, t.n, field, outputs) suggestObject(c, t.n, field, n.info, outputs)
if t.sons[0] == nil: break if t.sons[0] == nil: break
t = skipTypes(t.sons[0], skipPtrs) t = skipTypes(t.sons[0], skipPtrs)
elif typ.kind == tyTuple and typ.n != nil: 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) suggestOperations(c, n, field, orig, outputs)
if typ != orig: if typ != orig:
suggestOperations(c, n, field, typ, outputs) suggestOperations(c, n, field, typ, outputs)
@ -405,36 +404,12 @@ proc inCheckpoint*(current: TLineInfo): TCheckPointResult =
if current.line >= gTrackPos.line: if current.line >= gTrackPos.line:
return cpFuzzy 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 = proc isTracked*(current: TLineInfo, tokenLen: int): bool =
if current.fileIndex==gTrackPos.fileIndex and current.line==gTrackPos.line: if current.fileIndex==gTrackPos.fileIndex and current.line==gTrackPos.line:
let col = gTrackPos.col let col = gTrackPos.col
if col >= current.col and col <= current.col+tokenLen-1: if col >= current.col and col <= current.col+tokenLen-1:
return true 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): when defined(nimsuggest):
# Since TLineInfo defined a == operator that doesn't include the column, # Since TLineInfo defined a == operator that doesn't include the column,
# we map TLineInfo to a unique int here for this lookup table: # we map TLineInfo to a unique int here for this lookup table:
@ -453,26 +428,26 @@ var
lastLineInfo*: TLineInfo lastLineInfo*: TLineInfo
proc findUsages(info: TLineInfo; s: PSym; usageSym: var PSym) = 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): if usageSym == nil and isTracked(info, s.name.s.len):
usageSym = s 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: elif s == usageSym:
if lastLineInfo != info: 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 lastLineInfo = info
when defined(nimsuggest): when defined(nimsuggest):
proc listUsages*(s: PSym) = proc listUsages*(s: PSym) =
#echo "usages ", len(s.allUsages) #echo "usages ", len(s.allUsages)
for info in 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)) suggestResult(symToSuggest(s, isLocal=false, x, info, 100, PrefixMatch.None, false, 0))
proc findDefinition(info: TLineInfo; s: PSym) = proc findDefinition(info: TLineInfo; s: PSym) =
if s.isNil: return if s.isNil: return
if isTracked(info, s.name.s.len): 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() suggestQuit()
proc ensureIdx[T](x: var T, y: int) = 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.} = proc suggestSym*(info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.inline.} =
## misnamed: should be 'symDeclared' ## misnamed: should be 'symDeclared'
when defined(nimsuggest): when defined(nimsuggest):
if suggestVersion == 2: if suggestVersion == 0:
if s.allUsages.isNil: if s.allUsages.isNil:
s.allUsages = @[info] s.allUsages = @[info]
else: else:
@ -496,13 +471,13 @@ proc suggestSym*(info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.in
findDefinition(info, s) findDefinition(info, s)
elif gIdeCmd == ideDus and s != nil: elif gIdeCmd == ideDus and s != nil:
if isTracked(info, s.name.s.len): 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) findUsages(info, s, usageSym)
elif gIdeCmd == ideHighlight and info.fileIndex == gTrackPos.fileIndex: 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 elif gIdeCmd == ideOutline and info.fileIndex == gTrackPos.fileIndex and
isDecl: 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) = proc markUsed(info: TLineInfo; s: PSym; usageSym: var PSym) =
incl(s.flags, sfUsed) incl(s.flags, sfUsed)
@ -525,40 +500,31 @@ proc safeSemExpr*(c: PContext, n: PNode): PNode =
except ERecoverableError: except ERecoverableError:
result = ast.emptyNode result = ast.emptyNode
proc suggestExpr*(c: PContext, node: PNode) = proc sugExpr(c: PContext, n: PNode, outputs: var Suggestions) =
if gTrackPos.line < 0: return if n.kind == nkDotExpr:
var cp = inCheckpoint(node.info) var obj = safeSemExpr(c, n.sons[0])
if cp == cpNone: return # 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: # This keeps semExpr() from coming here recursively:
if c.compilesContextId > 0: return if c.compilesContextId > 0: return
inc(c.compilesContextId) inc(c.compilesContextId)
var outputs: Suggestions = @[] var outputs: Suggestions = @[]
if gIdeCmd == ideSug: if gIdeCmd == ideSug:
var n = findClosestDot(node) sugExpr(c, n, outputs)
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)
elif gIdeCmd == ideCon: elif gIdeCmd == ideCon:
var n = findClosestCall(node)
if n == nil: n = node
if n.kind in nkCallKinds: if n.kind in nkCallKinds:
var a = copyNode(n) var a = copyNode(n)
var x = safeSemExpr(c, n.sons[0]) var x = safeSemExpr(c, n.sons[0])
@ -576,16 +542,32 @@ proc suggestExpr*(c: PContext, node: PNode) =
produceOutput(outputs) produceOutput(outputs)
suggestQuit() 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) = proc suggestStmt*(c: PContext, n: PNode) =
suggestExpr(c, n) 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) = proc suggestSentinel*(c: PContext) =
if gIdeCmd != ideSug or c.module.position != gTrackPos.fileIndex: return if gIdeCmd != ideSug or c.module.position != gTrackPos.fileIndex: return
if c.compilesContextId > 0: return if c.compilesContextId > 0: return
inc(c.compilesContextId) inc(c.compilesContextId)
var outputs: Suggestions = @[]
# suggest everything: # suggest everything:
var isLocal = true var isLocal = true
var outputs: Suggestions = @[]
var scopeN = 0 var scopeN = 0
for scope in walkScopes(c.currentScope): for scope in walkScopes(c.currentScope):
if scope == c.topLevelScope: isLocal = false if scope == c.topLevelScope: isLocal = false
@ -593,7 +575,7 @@ proc suggestSentinel*(c: PContext) =
for it in items(scope.symbols): for it in items(scope.symbols):
var pm: PrefixMatch var pm: PrefixMatch
if filterSymNoOpr(it, nil, pm): 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) dec(c.compilesContextId)
produceOutput(outputs)

View file

@ -291,10 +291,13 @@ proc transformBreak(c: PTransf, n: PNode): PTransNode =
else: else:
result = newTransNode(n.kind, n.info, 1) result = newTransNode(n.kind, n.info, 1)
result[0] = lablCopy.PTransNode result[0] = lablCopy.PTransNode
else: elif c.breakSyms.len > 0:
# this check can fail for 'nim check'
let labl = c.breakSyms[c.breakSyms.high] let labl = c.breakSyms[c.breakSyms.high]
result = transformSons(c, n) result = transformSons(c, n)
result[0] = newSymNode(labl).PTransNode result[0] = newSymNode(labl).PTransNode
else:
result = n.PTransNode
proc unpackTuple(c: PTransf, n: PNode, father: PTransNode) = proc unpackTuple(c: PTransf, n: PNode, father: PTransNode) =
# XXX: BUG: what if `n` is an expression with side-effects? # XXX: BUG: what if `n` is an expression with side-effects?

View file

@ -20,6 +20,7 @@ type
preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg
proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string
template `$`*(typ: PType): string = typeToString(typ)
proc base*(t: PType): PType = proc base*(t: PType): PType =
result = t.sons[0] result = t.sons[0]
@ -547,7 +548,9 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
if prefer != preferExported: if prefer != preferExported:
result.add("(" & typeToString(t.sons[0]) & ")") result.add("(" & typeToString(t.sons[0]) & ")")
of tyProc: of tyProc:
result = if tfIterator in t.flags: "iterator (" else: "proc (" result = if tfIterator in t.flags: "iterator " else: "proc "
if tfUnresolved in t.flags: result.add "[*missing parameters*]"
result.add "("
for i in countup(1, sonsLen(t) - 1): for i in countup(1, sonsLen(t) - 1):
if t.n != nil and i < t.n.len and t.n[i].kind == nkSym: if t.n != nil and i < t.n.len and t.n[i].kind == nkSym:
add(result, t.n[i].sym.name.s) add(result, t.n[i].sym.name.s)

View file

@ -84,7 +84,7 @@ Collections and algorithms
Efficient implementation of a set of ints as a sparse bit set. Efficient implementation of a set of ints as a sparse bit set.
* `critbits <critbits.html>`_ * `critbits <critbits.html>`_
This module implements a *crit bit tree* which is an efficient 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>`_ * `sequtils <sequtils.html>`_
This module implements operations for the built-in seq type This module implements operations for the built-in seq type
which were inspired by functional programming languages. which were inspired by functional programming languages.

View file

@ -2,7 +2,7 @@ Procedures
========== ==========
What most programming languages call `methods`:idx: or `functions`:idx: are 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 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 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 identifiers separated by either comma or semicolon. A parameter is given a type

View file

@ -393,7 +393,7 @@ Since counting up occurs so often in programs, Nim also has a `..
for i in 1..10: for i in 1..10:
... ...
Zero-indexed counting have two shortcuts ``..<`` and ``..^`` to simplify counting to one less then the higher index: Zero-indexed counting have two shortcuts ``..<`` and ``..^`` to simplify counting to one less than the higher index:
.. code-block:: nim .. code-block:: nim
for i in 0..<10: for i in 0..<10:
@ -411,8 +411,8 @@ Other useful iterators for collections (like arrays and sequences) are
* ``pairs`` and ``mpairs`` which provides the element and an index number (immutable and mutable respectively) * ``pairs`` and ``mpairs`` which provides the element and an index number (immutable and mutable respectively)
.. code-block:: nim .. code-block:: nim
for indx, itm in ["a","b"].pairs: for index, item in ["a","b"].pairs:
echo itm, " at index ", indx echo item, " at index ", index
# => a at index 0 # => a at index 0
# => b at index 1 # => b at index 1
@ -489,10 +489,10 @@ Example:
else: else:
echo "unknown operating system" echo "unknown operating system"
The ``when`` statement is almost identical to the ``if`` statement with some The ``when`` statement is almost identical to the ``if`` statement, but with these
differences: differences:
* Each condition has to be a constant expression since it is evaluated by the * Each condition must be a constant expression since it is evaluated by the
compiler. compiler.
* The statements within a branch do not open a new scope. * The statements within a branch do not open a new scope.
* The compiler checks the semantics and produces code *only* for the statements * The compiler checks the semantics and produces code *only* for the statements
@ -516,8 +516,8 @@ In Nim there is a distinction between *simple statements* and *complex
statements*. *Simple statements* cannot contain other statements: statements*. *Simple statements* cannot contain other statements:
Assignment, procedure calls or the ``return`` statement belong to the simple Assignment, procedure calls or the ``return`` statement belong to the simple
statements. *Complex statements* like ``if``, ``when``, ``for``, ``while`` can statements. *Complex statements* like ``if``, ``when``, ``for``, ``while`` can
contain other statements. To avoid ambiguities, complex statements always have contain other statements. To avoid ambiguities, complex statements must always
to be indented, but single simple statements do not: be indented, but single simple statements do not:
.. code-block:: nim .. code-block:: nim
# no indentation needed for single assignment statement: # no indentation needed for single assignment statement:
@ -586,9 +586,9 @@ false if they answered "no" (or something similar). A ``return`` statement
leaves the procedure (and therefore the while loop) immediately. The leaves the procedure (and therefore the while loop) immediately. The
``(question: string): bool`` syntax describes that the procedure expects a ``(question: string): bool`` syntax describes that the procedure expects a
parameter named ``question`` of type ``string`` and returns a value of type parameter named ``question`` of type ``string`` and returns a value of type
``bool``. ``Bool`` is a built-in type: the only valid values for ``bool`` are ``bool``. The ``bool`` type is built-in: the only valid values for ``bool`` are
``true`` and ``false``. ``true`` and ``false``.
The conditions in if or while statements should be of the type ``bool``. The conditions in if or while statements must be of type ``bool``.
Some terminology: in the example ``question`` is called a (formal) *parameter*, Some terminology: in the example ``question`` is called a (formal) *parameter*,
``"Should I..."`` is called an *argument* that is passed to this parameter. ``"Should I..."`` is called an *argument* that is passed to this parameter.
@ -658,8 +658,8 @@ a tuple as a return value instead of using var parameters.
Discard statement Discard statement
----------------- -----------------
To call a procedure that returns a value just for its side effects and ignoring To call a procedure that returns a value just for its side effects and ignoring
its return value, a ``discard`` statement **has** to be used. Nim does not its return value, a ``discard`` statement **must** be used. Nim does not
allow to silently throw away a return value: allow silently throwing away a return value:
.. code-block:: nim .. code-block:: nim
discard yes("May I ask a pointless question?") discard yes("May I ask a pointless question?")
@ -708,7 +708,7 @@ The compiler checks that each parameter receives exactly one argument.
Default values Default values
-------------- --------------
To make the ``createWindow`` proc easier to use it should provide `default To make the ``createWindow`` proc easier to use it should provide `default
values`, these are values that are used as arguments if the caller does not values`; these are values that are used as arguments if the caller does not
specify them: specify them:
.. code-block:: nim .. code-block:: nim
@ -750,19 +750,19 @@ algorithm. Ambiguous calls are reported as errors.
Operators Operators
--------- ---------
The Nim library makes heavy use of overloading - one reason for this is that The Nim library makes heavy use of overloading - one reason for this is that
each operator like ``+`` is a just an overloaded proc. The parser lets you each operator like ``+`` is just an overloaded proc. The parser lets you
use operators in `infix notation` (``a + b``) or `prefix notation` (``+ a``). use operators in `infix notation` (``a + b``) or `prefix notation` (``+ a``).
An infix operator always receives two arguments, a prefix operator always one. An infix operator always receives two arguments, a prefix operator always one.
Postfix operators are not possible, because this would be ambiguous: does (Postfix operators are not possible, because this would be ambiguous: does
``a @ @ b`` mean ``(a) @ (@b)`` or ``(a@) @ (b)``? It always means ``a @ @ b`` mean ``(a) @ (@b)`` or ``(a@) @ (b)``? It always means
``(a) @ (@b)``, because there are no postfix operators in Nim. ``(a) @ (@b)``, because there are no postfix operators in Nim.)
Apart from a few built-in keyword operators such as ``and``, ``or``, ``not``, Apart from a few built-in keyword operators such as ``and``, ``or``, ``not``,
operators always consist of these characters: operators always consist of these characters:
``+ - * \ / < > = @ $ ~ & % ! ? ^ . |`` ``+ - * \ / < > = @ $ ~ & % ! ? ^ . |``
User defined operators are allowed. Nothing stops you from defining your own User defined operators are allowed. Nothing stops you from defining your own
``@!?+~`` operator, but readability can suffer. ``@!?+~`` operator, but doing so may reduce readability.
The operator's precedence is determined by its first character. The details The operator's precedence is determined by its first character. The details
can be found in the manual. can be found in the manual.
@ -785,7 +785,7 @@ Forward declarations
-------------------- --------------------
Every variable, procedure, etc. needs to be declared before it can be used. Every variable, procedure, etc. needs to be declared before it can be used.
(The reason for this is that it is non-trivial to do better than that in a (The reason for this is that it is non-trivial to avoid this need in a
language that supports meta programming as extensively as Nim does.) language that supports meta programming as extensively as Nim does.)
However, this cannot be done for mutually recursive procedures: However, this cannot be done for mutually recursive procedures:
@ -822,7 +822,7 @@ whose value is then returned implicitly.
Iterators Iterators
========= =========
Let's return to the boring counting example: Let's return to the simple counting example:
.. code-block:: nim .. code-block:: nim
echo "Counting to ten: " echo "Counting to ten: "
@ -843,7 +843,7 @@ However, this does not work. The problem is that the procedure should not
only ``return``, but return and **continue** after an iteration has only ``return``, but return and **continue** after an iteration has
finished. This *return and continue* is called a `yield` statement. Now finished. This *return and continue* is called a `yield` statement. Now
the only thing left to do is to replace the ``proc`` keyword by ``iterator`` the only thing left to do is to replace the ``proc`` keyword by ``iterator``
and there it is - our first iterator: and here it is - our first iterator:
.. code-block:: nim .. code-block:: nim
iterator countup(a, b: int): int = iterator countup(a, b: int): int =
@ -856,8 +856,8 @@ Iterators look very similar to procedures, but there are several
important differences: important differences:
* Iterators can only be called from for loops. * Iterators can only be called from for loops.
* Iterators cannot contain a ``return`` statement and procs cannot contain a * Iterators cannot contain a ``return`` statement (and procs cannot contain a
``yield`` statement. ``yield`` statement).
* Iterators have no implicit ``result`` variable. * Iterators have no implicit ``result`` variable.
* Iterators do not support recursion. * Iterators do not support recursion.
* Iterators cannot be forward declared, because the compiler must be able * Iterators cannot be forward declared, because the compiler must be able
@ -866,8 +866,8 @@ important differences:
However, you can also use a ``closure`` iterator to get a different set of However, you can also use a ``closure`` iterator to get a different set of
restrictions. See `first class iterators <manual.html#first-class-iterators>`_ restrictions. See `first class iterators <manual.html#first-class-iterators>`_
for details. Iterators can have the same name and parameters as a proc, for details. Iterators can have the same name and parameters as a proc, since
essentially they have their own namespace. Therefore it is common practice to essentially they have their own namespaces. Therefore it is common practice to
wrap iterators in procs of the same name which accumulate the result of the wrap iterators in procs of the same name which accumulate the result of the
iterator and return it as a sequence, like ``split`` from the `strutils module iterator and return it as a sequence, like ``split`` from the `strutils module
<strutils.html>`_. <strutils.html>`_.
@ -882,13 +882,13 @@ that are available for them in detail.
Booleans Booleans
-------- --------
The boolean type is named ``bool`` in Nim and consists of the two Nim's boolean type is called ``bool`` and consists of the two
pre-defined values ``true`` and ``false``. Conditions in while, pre-defined values ``true`` and ``false``. Conditions in while,
if, elif, when statements need to be of type bool. if, elif, and when statements must be of type bool.
The operators ``not, and, or, xor, <, <=, >, >=, !=, ==`` are defined The operators ``not, and, or, xor, <, <=, >, >=, !=, ==`` are defined
for the bool type. The ``and`` and ``or`` operators perform short-cut for the bool type. The ``and`` and ``or`` operators perform short-circuit
evaluation. Example: evaluation. For example:
.. code-block:: nim .. code-block:: nim
@ -899,8 +899,8 @@ evaluation. Example:
Characters Characters
---------- ----------
The `character type` is named ``char`` in Nim. Its size is one byte. The `character type` is called ``char``. Its size is always one byte, so
Thus it cannot represent an UTF-8 character, but a part of it. it cannot represent most UTF-8 characters; but it *can* represent one of the bytes that makes up a multi-byte UTF-8 character.
The reason for this is efficiency: for the overwhelming majority of use-cases, The reason for this is efficiency: for the overwhelming majority of use-cases,
the resulting programs will still handle UTF-8 properly as UTF-8 was specially the resulting programs will still handle UTF-8 properly as UTF-8 was specially
designed for this. designed for this.
@ -914,11 +914,11 @@ Converting from an integer to a ``char`` is done with the ``chr`` proc.
Strings Strings
------- -------
String variables in Nim are **mutable**, so appending to a string String variables are **mutable**, so appending to a string
is quite efficient. Strings in Nim are both zero-terminated and have a is possible, and quite efficient. Strings in Nim are both zero-terminated and have a
length field. One can retrieve a string's length with the builtin ``len`` length field. A string's length can be retrieved with the builtin ``len``
procedure; the length never counts the terminating zero. Accessing the procedure; the length never counts the terminating zero. Accessing the
terminating zero is no error and often leads to simpler code: terminating zero is not an error and often leads to simpler code:
.. code-block:: nim .. code-block:: nim
if s[i] == 'a' and s[i+1] == 'b': if s[i] == 'a' and s[i+1] == 'b':
@ -928,15 +928,15 @@ terminating zero is no error and often leads to simpler code:
The assignment operator for strings copies the string. You can use the ``&`` The assignment operator for strings copies the string. You can use the ``&``
operator to concatenate strings and ``add`` to append to a string. operator to concatenate strings and ``add`` to append to a string.
Strings are compared by their lexicographical order. All comparison operators Strings are compared using their lexicographical order. All the comparison operators
are available. Per convention, all strings are UTF-8 strings, but this is not are supported. By convention, all strings are UTF-8 encoded, but this is not
enforced. For example, when reading strings from binary files, they are merely enforced. For example, when reading strings from binary files, they are merely
a sequence of bytes. The index operation ``s[i]`` means the i-th *char* of a sequence of bytes. The index operation ``s[i]`` means the i-th *char* of
``s``, not the i-th *unichar*. ``s``, not the i-th *unichar*.
String variables are initialized with a special value, called ``nil``. However, String variables are initialized with a special value, called ``nil``. However,
most string operations cannot deal with ``nil`` (leading to an exception being most string operations cannot deal with ``nil`` (leading to an exception being
raised) for performance reasons. One should use empty strings ``""`` raised) for performance reasons. It is best to use empty strings ``""``
rather than ``nil`` as the *empty* value. But ``""`` often creates a string rather than ``nil`` as the *empty* value. But ``""`` often creates a string
object on the heap, so there is a trade-off to be made here. object on the heap, so there is a trade-off to be made here.
@ -947,7 +947,7 @@ Nim has these integer types built-in:
``int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64``. ``int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64``.
The default integer type is ``int``. Integer literals can have a *type suffix* The default integer type is ``int``. Integer literals can have a *type suffix*
to mark them to be of another integer type: to specify a non-default integer type:
.. code-block:: nim .. code-block:: nim
@ -961,18 +961,18 @@ Most often integers are used for counting objects that reside in memory, so
``int`` has the same size as a pointer. ``int`` has the same size as a pointer.
The common operators ``+ - * div mod < <= == != > >=`` are defined for The common operators ``+ - * div mod < <= == != > >=`` are defined for
integers. The ``and or xor not`` operators are defined for integers too and integers. The ``and or xor not`` operators are also defined for integers, and
provide *bitwise* operations. Left bit shifting is done with the ``shl``, right provide *bitwise* operations. Left bit shifting is done with the ``shl``, right
shifting with the ``shr`` operator. Bit shifting operators always treat their shifting with the ``shr`` operator. Bit shifting operators always treat their
arguments as *unsigned*. For `arithmetic bit shifts`:idx: ordinary arguments as *unsigned*. For `arithmetic bit shifts`:idx: ordinary
multiplication or division can be used. multiplication or division can be used.
Unsigned operations all wrap around; they cannot lead to over- or underflow Unsigned operations all wrap around; they cannot lead to over- or under-flow
errors. errors.
`Automatic type conversion`:idx: is performed in expressions where different Lossless `Automatic type conversion`:idx: is performed in expressions where different
kinds of integer types are used. However, if the type conversion kinds of integer types are used. However, if the type conversion
loses information, the `EOutOfRange`:idx: exception is raised (if the error would cause loss of information, the `EOutOfRange`:idx: exception is raised (if the error
cannot be detected at compile time). cannot be detected at compile time).
@ -981,9 +981,9 @@ Floats
Nim has these floating point types built-in: ``float float32 float64``. Nim has these floating point types built-in: ``float float32 float64``.
The default float type is ``float``. In the current implementation, The default float type is ``float``. In the current implementation,
``float`` is always 64 bit wide. ``float`` is always 64-bits.
Float literals can have a *type suffix* to mark them to be of another float Float literals can have a *type suffix* to specify a non-default float
type: type:
.. code-block:: nim .. code-block:: nim
@ -993,18 +993,18 @@ type:
z = 0.0'f64 # z is of type ``float64`` z = 0.0'f64 # z is of type ``float64``
The common operators ``+ - * / < <= == != > >=`` are defined for The common operators ``+ - * / < <= == != > >=`` are defined for
floats and follow the IEEE standard. floats and follow the IEEE-754 standard.
Automatic type conversion in expressions with different kinds of floating Automatic type conversion in expressions with different kinds of floating
point types is performed: the smaller type is converted to the larger. Integer point types is performed: the smaller type is converted to the larger. Integer
types are **not** converted to floating point types automatically and vice types are **not** converted to floating point types automatically, nor vice
versa. The `toInt <system.html#toInt>`_ and `toFloat <system.html#toFloat>`_ versa. Use the `toInt <system.html#toInt>`_ and `toFloat <system.html#toFloat>`_
procs can be used for these conversions. procs for these conversions.
Type Conversion Type Conversion
--------------- ---------------
Conversion between basic types in nim is performed by using the Conversion between basic types is performed by using the
type as a function: type as a function:
.. code-block:: nim .. code-block:: nim
@ -1019,9 +1019,9 @@ Internal type representation
============================ ============================
As mentioned earlier, the built-in `$ <system.html#$>`_ (stringify) operator As mentioned earlier, the built-in `$ <system.html#$>`_ (stringify) operator
turns any basic type into a string, which you can then print to the screen turns any basic type into a string, which you can then print to the console
with the ``echo`` proc. However, advanced types, or types you may define using the ``echo`` proc. However, advanced types, and your own custom types,
yourself won't work with the ``$`` operator until you define one for them. won't work with the ``$`` operator until you define it for them.
Sometimes you just want to debug the current value of a complex type without Sometimes you just want to debug the current value of a complex type without
having to write its ``$`` operator. You can use then the `repr having to write its ``$`` operator. You can use then the `repr
<system.html#repr>`_ proc which works with any type and even complex data <system.html#repr>`_ proc which works with any type and even complex data
@ -1057,16 +1057,16 @@ In Nim new types can be defined within a ``type`` statement:
biggestInt = int64 # biggest integer type that is available biggestInt = int64 # biggest integer type that is available
biggestFloat = float64 # biggest float type that is available biggestFloat = float64 # biggest float type that is available
Enumeration and object types cannot be defined on the fly, but only within a Enumeration and object types may only be defined within a
``type`` statement. ``type`` statement.
Enumerations Enumerations
------------ ------------
A variable of an enumeration type can only be assigned a value of a A variable of an enumeration type can only be assigned one of the enumeration's specified values.
limited set. This set consists of ordered symbols. Each symbol is mapped These values are a set of ordered symbols. Each symbol is mapped
to an integer value internally. The first symbol is represented to an integer value internally. The first symbol is represented
at runtime by 0, the second by 1 and so on. Example: at runtime by 0, the second by 1 and so on. For example:
.. code-block:: nim .. code-block:: nim
@ -1077,17 +1077,17 @@ at runtime by 0, the second by 1 and so on. Example:
var x = south # `x` is of type `Direction`; its value is `south` var x = south # `x` is of type `Direction`; its value is `south`
echo x # writes "south" to `stdout` echo x # writes "south" to `stdout`
All comparison operators can be used with enumeration types. All the comparison operators can be used with enumeration types.
An enumeration's symbol can be qualified to avoid ambiguities: An enumeration's symbol can be qualified to avoid ambiguities:
``Direction.south``. ``Direction.south``.
The ``$`` operator can convert any enumeration value to its name, the ``ord`` The ``$`` operator can convert any enumeration value to its name, and the ``ord``
proc to its underlying integer value. proc can convert it to its underlying integer value.
For better interfacing to other programming languages, the symbols of enum For better interfacing to other programming languages, the symbols of enum
types can be assigned an explicit ordinal value. However, the ordinal values types can be assigned an explicit ordinal value. However, the ordinal values
have to be in ascending order. A symbol whose ordinal value is not must be in ascending order. A symbol whose ordinal value is not
explicitly given is assigned the value of the previous symbol + 1. explicitly given is assigned the value of the previous symbol + 1.
An explicit ordered enum can have *holes*: An explicit ordered enum can have *holes*:
@ -1142,8 +1142,8 @@ subrange types (and vice versa) are allowed.
The ``system`` module defines the important `Natural <system.html#Natural>`_ The ``system`` module defines the important `Natural <system.html#Natural>`_
type as ``range[0..high(int)]`` (`high <system.html#high>`_ returns the type as ``range[0..high(int)]`` (`high <system.html#high>`_ returns the
maximal value). Other programming languages mandate the usage of unsigned maximal value). Other programming languages may suggest the use of unsigned
integers for natural numbers. This is often **wrong**: you don't want unsigned integers for natural numbers. This is often **unwise**: you don't want unsigned
arithmetic (which wraps around) just because the numbers cannot be negative. arithmetic (which wraps around) just because the numbers cannot be negative.
Nim's ``Natural`` type helps to avoid this common programming error. Nim's ``Natural`` type helps to avoid this common programming error.
@ -1156,9 +1156,9 @@ Sets
Arrays Arrays
------ ------
An array is a simple fixed length container. Each element in An array is a simple fixed length container. Each element in
the array has the same type. The array's index type can be any ordinal type. an array has the same type. The array's index type can be any ordinal type.
Arrays can be constructed via ``[]``: Arrays can be constructed using ``[]``:
.. code-block:: nim .. code-block:: nim
@ -1222,7 +1222,7 @@ subdivided in height levels accessed through their integer index:
#tower[0][1] = on #tower[0][1] = on
Note how the built-in ``len`` proc returns only the array's first dimension Note how the built-in ``len`` proc returns only the array's first dimension
length. Another way of defining the ``LightTower`` to show better its length. Another way of defining the ``LightTower`` to better illustrate its
nested nature would be to omit the previous definition of the ``LevelSetting`` nested nature would be to omit the previous definition of the ``LevelSetting``
type and instead write it embedded directly as the type of the first dimension: type and instead write it embedded directly as the type of the first dimension:
@ -1230,7 +1230,7 @@ type and instead write it embedded directly as the type of the first dimension:
type type
LightTower = array[1..10, array[north..west, BlinkLights]] LightTower = array[1..10, array[north..west, BlinkLights]]
It is quite frequent to have arrays start at zero, so there's a shortcut syntax It is quite common to have arrays start at zero, so there's a shortcut syntax
to specify a range from zero to the specified index minus one: to specify a range from zero to the specified index minus one:
.. code-block:: nim .. code-block:: nim
@ -1288,8 +1288,8 @@ value. Here the ``for`` statement is looping over the results from the
<system.html>`_ module. Examples: <system.html>`_ module. Examples:
.. code-block:: nim .. code-block:: nim
for i in @[3, 4, 5]: for value in @[3, 4, 5]:
echo i echo value
# --> 3 # --> 3
# --> 4 # --> 4
# --> 5 # --> 5
@ -1320,7 +1320,7 @@ type does not matter.
fruits = @[] # creates an empty sequence on the heap that will be referenced by 'fruits' fruits = @[] # creates an empty sequence on the heap that will be referenced by 'fruits'
capitals = ["New York", "London", "Berlin"] # array 'capitals' allows only assignment of three elements capitals = ["New York", "London", "Berlin"] # array 'capitals' allows assignment of only three elements
fruits.add("Banana") # sequence 'fruits' is dynamically expandable during runtime fruits.add("Banana") # sequence 'fruits' is dynamically expandable during runtime
fruits.add("Mango") fruits.add("Mango")
@ -1406,7 +1406,7 @@ the same type and of the same name in the same order.
The assignment operator for tuples copies each component. The notation The assignment operator for tuples copies each component. The notation
``t.field`` is used to access a tuple's field. Another notation is ``t.field`` is used to access a tuple's field. Another notation is
``t[i]`` to access the ``i``'th field. Here ``i`` needs to be a constant ``t[i]`` to access the ``i``'th field. Here ``i`` must be a constant
integer. integer.
.. code-block:: nim .. code-block:: nim
@ -1449,10 +1449,10 @@ Tuples can be *unpacked* during variable assignment (and only then!). This can
be handy to assign directly the fields of the tuples to individually named be handy to assign directly the fields of the tuples to individually named
variables. An example of this is the `splitFile <os.html#splitFile>`_ proc variables. An example of this is the `splitFile <os.html#splitFile>`_ proc
from the `os module <os.html>`_ which returns the directory, name and from the `os module <os.html>`_ which returns the directory, name and
extension of a path at the same time. For tuple unpacking to work you have to extension of a path at the same time. For tuple unpacking to work you must
use parenthesis around the values you want to assign the unpacking to, use parentheses around the values you want to assign the unpacking to,
otherwise you will be assigning the same value to all the individual otherwise you will be assigning the same value to all the individual
variables! Example: variables! For example:
.. code-block:: nim .. code-block:: nim
@ -1494,12 +1494,12 @@ point to and modify the same location in memory.
Nim distinguishes between `traced`:idx: and `untraced`:idx: references. Nim distinguishes between `traced`:idx: and `untraced`:idx: references.
Untraced references are also called *pointers*. Traced references point to Untraced references are also called *pointers*. Traced references point to
objects of a garbage collected heap, untraced references point to objects in a garbage collected heap, untraced references point to
manually allocated objects or to objects somewhere else in memory. Thus manually allocated objects or to objects elsewhere in memory. Thus
untraced references are *unsafe*. However for certain low-level operations untraced references are *unsafe*. However for certain low-level operations
(accessing the hardware) untraced references are unavoidable. (e.g., accessing the hardware), untraced references are necessary.
Traced references are declared with the **ref** keyword, untraced references Traced references are declared with the **ref** keyword; untraced references
are declared with the **ptr** keyword. are declared with the **ptr** keyword.
The empty ``[]`` subscript notation can be used to *derefer* a reference, The empty ``[]`` subscript notation can be used to *derefer* a reference,
@ -1520,10 +1520,10 @@ operators perform implicit dereferencing operations for reference types:
n.data = 9 n.data = 9
# no need to write n[].data; in fact n[].data is highly discouraged! # no need to write n[].data; in fact n[].data is highly discouraged!
To allocate a new traced object, the built-in procedure ``new`` has to be used. To allocate a new traced object, the built-in procedure ``new`` must be used.
To deal with untraced memory, the procedures ``alloc``, ``dealloc`` and To deal with untraced memory, the procedures ``alloc``, ``dealloc`` and
``realloc`` can be used. The documentation of the `system <system.html>`_ ``realloc`` can be used. The `system <system.html>`_
module contains further information. module's documentation contains further details.
If a reference points to *nothing*, it has the value ``nil``. If a reference points to *nothing*, it has the value ``nil``.
@ -1555,8 +1555,8 @@ listed in the `manual <manual.html#types-procedural-type>`_.
Distinct type Distinct type
------------- -------------
A Distinct type allows for the creation of new type that "does not imply a subtype relationship between it and its base type". A Distinct type allows for the creation of new type that "does not imply a subtype relationship between it and its base type".
You must EXPLICITLY define all behaviour for the distinct type. You must **explicitly** define all behaviour for the distinct type.
To help with this, both the distinct type and its base type can cast from one type to the other. To help with this, both the distinct type and its base type can cast from one type to the other.
Examples are provided in the `manual <manual.html#types-distinct-type>`_. Examples are provided in the `manual <manual.html#types-distinct-type>`_.
@ -1564,8 +1564,8 @@ Modules
======= =======
Nim supports splitting a program into pieces with a module concept. Nim supports splitting a program into pieces with a module concept.
Each module is in its own file. Modules enable `information hiding`:idx: and Each module is in its own file. Modules enable `information hiding`:idx: and
`separate compilation`:idx:. A module may gain access to symbols of another `separate compilation`:idx:. A module may gain access to the symbols of another
module by the `import`:idx: statement. Only top-level symbols that are marked module by using the `import`:idx: statement. Only top-level symbols that are marked
with an asterisk (``*``) are exported: with an asterisk (``*``) are exported:
.. code-block:: nim .. code-block:: nim
@ -1585,7 +1585,7 @@ with an asterisk (``*``) are exported:
The above module exports ``x`` and ``*``, but not ``y``. The above module exports ``x`` and ``*``, but not ``y``.
The top-level statements of a module are executed at the start of the program. A module's top-level statements are executed at the start of the program.
This can be used to initialize complex data structures for example. This can be used to initialize complex data structures for example.
Each module has a special magic constant ``isMainModule`` that is true if the Each module has a special magic constant ``isMainModule`` that is true if the
@ -1625,8 +1625,8 @@ This is best illustrated by an example:
result = x + 1 result = x + 1
A symbol of a module *can* be *qualified* with the ``module.symbol`` syntax. If A symbol of a module *can* be *qualified* with the ``module.symbol`` syntax. And if
the symbol is ambiguous, it even *has* to be qualified. A symbol is ambiguous a symbol is ambiguous, it *must* be qualified. A symbol is ambiguous
if it is defined in two (or more) different modules and both modules are if it is defined in two (or more) different modules and both modules are
imported by a third one: imported by a third one:
@ -1642,7 +1642,7 @@ imported by a third one:
# Module C # Module C
import A, B import A, B
write(stdout, x) # error: x is ambiguous write(stdout, x) # error: x is ambiguous
write(stdout, A.x) # no error: qualifier used write(stdout, A.x) # okay: qualifier used
var x = 4 var x = 4
write(stdout, x) # not ambiguous: uses the module C's x write(stdout, x) # not ambiguous: uses the module C's x

View file

@ -214,9 +214,9 @@ proc buildNimble(latest: bool) =
proc bundleNimsuggest(buildExe: bool) = proc bundleNimsuggest(buildExe: bool) =
if buildExe: if buildExe:
nimexec("c --noNimblePath -d:release -p:compiler tools/nimsuggest/nimsuggest.nim") nimexec("c --noNimblePath -d:release -p:compiler nimsuggest/nimsuggest.nim")
copyExe("tools/nimsuggest/nimsuggest".exe, "bin/nimsuggest".exe) copyExe("nimsuggest/nimsuggest".exe, "bin/nimsuggest".exe)
removeFile("tools/nimsuggest/nimsuggest".exe) removeFile("nimsuggest/nimsuggest".exe)
proc bundleWinTools() = proc bundleWinTools() =
nimexec("c tools/finish.nim") nimexec("c tools/finish.nim")
@ -253,7 +253,7 @@ proc buildTool(toolname, args: string) =
proc buildTools(latest: bool) = proc buildTools(latest: bool) =
let nimsugExe = "bin/nimsuggest".exe let nimsugExe = "bin/nimsuggest".exe
nimexec "c --noNimblePath -p:compiler -d:release -o:" & nimsugExe & nimexec "c --noNimblePath -p:compiler -d:release -o:" & nimsugExe &
" tools/nimsuggest/nimsuggest.nim" " nimsuggest/nimsuggest.nim"
let nimgrepExe = "bin/nimgrep".exe let nimgrepExe = "bin/nimgrep".exe
nimexec "c -o:" & nimgrepExe & " tools/nimgrep.nim" nimexec "c -o:" & nimgrepExe & " tools/nimgrep.nim"
@ -512,6 +512,14 @@ proc pushCsources() =
finally: finally:
setCurrentDir(cwd) setCurrentDir(cwd)
proc valgrind(cmd: string) =
exec("nim c " & cmd)
var i = cmd.len-1
while i >= 0 and cmd[i] != ' ': dec i
let file = if i >= 0: substr(cmd, i+1) else: cmd
let supp = getAppDir() / "tools" / "nimgrind.supp"
exec("valgrind --suppressions=" & supp & " " & changeFileExt(file, ""))
proc showHelp() = proc showHelp() =
quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)), quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)),
CompileDate, CompileTime], QuitSuccess) CompileDate, CompileTime], QuitSuccess)
@ -548,5 +556,6 @@ of cmdArgument:
of "nimsuggest": bundleNimsuggest(buildExe=true) of "nimsuggest": bundleNimsuggest(buildExe=true)
of "tools": buildTools(existsDir(".git")) of "tools": buildTools(existsDir(".git"))
of "pushcsource", "pushcsources": pushCsources() of "pushcsource", "pushcsources": pushCsources()
of "valgrind": valgrind(op.cmdLineRest)
else: showHelp() else: showHelp()
of cmdEnd: showHelp() of cmdEnd: showHelp()

View file

@ -8,8 +8,9 @@
# #
## This module implements a `crit bit tree`:idx: which is an efficient ## 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. ## by Adam Langley.
## (A crit bit tree is a form of `radix tree`:idx: or `patricia trie`:idx:.)
include "system/inclrtl" include "system/inclrtl"

View file

@ -16,9 +16,6 @@
## <manual.html#anonymous-procs>`_ to procs like ``filter`` to reduce typing. ## <manual.html#anonymous-procs>`_ to procs like ``filter`` to reduce typing.
## Anonymous procs can use `the special do notation <manual.html#do-notation>`_ ## Anonymous procs can use `the special do notation <manual.html#do-notation>`_
## which is more convenient in certain situations. ## which is more convenient in certain situations.
##
## **Note**: This interface will change as soon as the compiler supports
## closures and proper coroutines.
include "system/inclrtl" include "system/inclrtl"

View file

@ -83,7 +83,8 @@ proc unmapMem*(f: var MemFile, p: pointer, size: int) =
proc open*(filename: string, mode: FileMode = fmRead, proc open*(filename: string, mode: FileMode = fmRead,
mappedSize = -1, offset = 0, newFileSize = -1): MemFile = mappedSize = -1, offset = 0, newFileSize = -1,
allowRemap = false): MemFile =
## opens a memory mapped file. If this fails, ``EOS`` is raised. ## opens a memory mapped file. If this fails, ``EOS`` is raised.
## ##
## ``newFileSize`` can only be set if the file does not exist and is opened ## ``newFileSize`` can only be set if the file does not exist and is opened
@ -95,6 +96,9 @@ proc open*(filename: string, mode: FileMode = fmRead,
## ``offset`` must be multiples of the PAGE SIZE of your OS ## ``offset`` must be multiples of the PAGE SIZE of your OS
## (usually 4K or 8K but is unique to your OS) ## (usually 4K or 8K but is unique to your OS)
## ##
## ``allowRemap`` only needs to be true if you want to call ``mapMem`` on
## the resulting MemFile; else file handles are not kept open.
##
## Example: ## Example:
## ##
## .. code-block:: nim ## .. code-block:: nim
@ -189,11 +193,14 @@ proc open*(filename: string, mode: FileMode = fmRead,
else: result.size = fileSize.int else: result.size = fileSize.int
result.wasOpened = true result.wasOpened = true
if not allowRemap and result.fHandle != INVALID_HANDLE_VALUE:
if closeHandle(result.fHandle) == 0:
result.fHandle = INVALID_HANDLE_VALUE
else: else:
template fail(errCode: OSErrorCode, msg: expr) = template fail(errCode: OSErrorCode, msg: expr) =
rollback() rollback()
if result.handle != 0: discard close(result.handle) if result.handle != -1: discard close(result.handle)
raiseOSError(errCode) raiseOSError(errCode)
var flags = if readonly: O_RDONLY else: O_RDWR var flags = if readonly: O_RDONLY else: O_RDWR
@ -236,6 +243,10 @@ proc open*(filename: string, mode: FileMode = fmRead,
if result.mem == cast[pointer](MAP_FAILED): if result.mem == cast[pointer](MAP_FAILED):
fail(osLastError(), "file mapping failed") fail(osLastError(), "file mapping failed")
if not allowRemap and result.handle != -1:
if close(result.handle) == 0:
result.handle = -1
proc close*(f: var MemFile) = proc close*(f: var MemFile) =
## closes the memory mapped file `f`. All changes are written back to the ## closes the memory mapped file `f`. All changes are written back to the
## file system, if `f` was opened with write access. ## file system, if `f` was opened with write access.
@ -244,15 +255,16 @@ proc close*(f: var MemFile) =
var lastErr: OSErrorCode var lastErr: OSErrorCode
when defined(windows): when defined(windows):
if f.fHandle != INVALID_HANDLE_VALUE and f.wasOpened: if f.wasOpened:
error = unmapViewOfFile(f.mem) == 0 error = unmapViewOfFile(f.mem) == 0
lastErr = osLastError() lastErr = osLastError()
error = (closeHandle(f.mapHandle) == 0) or error error = (closeHandle(f.mapHandle) == 0) or error
error = (closeHandle(f.fHandle) == 0) or error if f.fHandle != INVALID_HANDLE_VALUE:
error = (closeHandle(f.fHandle) == 0) or error
else: else:
if f.handle != 0: error = munmap(f.mem, f.size) != 0
error = munmap(f.mem, f.size) != 0 lastErr = osLastError()
lastErr = osLastError() if f.handle != -1:
error = (close(f.handle) != 0) or error error = (close(f.handle) != 0) or error
f.size = 0 f.size = 0
@ -263,7 +275,7 @@ proc close*(f: var MemFile) =
f.mapHandle = 0 f.mapHandle = 0
f.wasOpened = false f.wasOpened = false
else: else:
f.handle = 0 f.handle = -1
if error: raiseOSError(lastErr) if error: raiseOSError(lastErr)

View file

@ -25,8 +25,8 @@ when not declared(getEnv) or defined(nimscript):
WriteEnvEffect* = object of WriteIOEffect ## effect that denotes a write WriteEnvEffect* = object of WriteIOEffect ## effect that denotes a write
## to an environment variable ## to an environment variable
ReadDirEffect* = object of ReadIOEffect ## effect that denotes a write ReadDirEffect* = object of ReadIOEffect ## effect that denotes a read
## operation to the directory ## operation from the directory
## structure ## structure
WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write
## operation to ## operation to

View file

@ -20,7 +20,8 @@
## var msg = createMessage("Hello from Nim's SMTP", ## var msg = createMessage("Hello from Nim's SMTP",
## "Hello!.\n Is this awesome or what?", ## "Hello!.\n Is this awesome or what?",
## @["foo@gmail.com"]) ## @["foo@gmail.com"])
## var smtpConn = connect("smtp.gmail.com", Port 465, true, true) ## let smtpConn = newSmtp(useSsl = true, debug=true)
## smtpConn.connect("smtp.gmail.com", Port 465)
## smtpConn.auth("username", "password") ## smtpConn.auth("username", "password")
## smtpConn.sendmail("username@gmail.com", @["foo@gmail.com"], $msg) ## smtpConn.sendmail("username@gmail.com", @["foo@gmail.com"], $msg)
## ##
@ -34,10 +35,6 @@ import asyncnet, asyncdispatch
export Port export Port
type type
Smtp* = object
sock: Socket
debug: bool
Message* = object Message* = object
msgTo: seq[string] msgTo: seq[string]
msgCc: seq[string] msgCc: seq[string]
@ -47,37 +44,29 @@ type
ReplyError* = object of IOError ReplyError* = object of IOError
AsyncSmtp* = ref object SmtpBase[SocketType] = ref object
sock: AsyncSocket sock: SocketType
address: string
port: Port
useSsl: bool
debug: bool debug: bool
Smtp* = SmtpBase[Socket]
AsyncSmtp* = SmtpBase[AsyncSocket]
{.deprecated: [EInvalidReply: ReplyError, TMessage: Message, TSMTP: Smtp].} {.deprecated: [EInvalidReply: ReplyError, TMessage: Message, TSMTP: Smtp].}
proc debugSend(smtp: Smtp, cmd: string) = proc debugSend(smtp: Smtp | AsyncSmtp, cmd: string) {.multisync.} =
if smtp.debug: if smtp.debug:
echo("C:" & cmd) echo("C:" & cmd)
smtp.sock.send(cmd) await smtp.sock.send(cmd)
proc debugRecv(smtp: var Smtp): TaintedString =
var line = TaintedString""
smtp.sock.readLine(line)
proc debugRecv(smtp: Smtp | AsyncSmtp): Future[TaintedString] {.multisync.} =
result = await smtp.sock.recvLine()
if smtp.debug: if smtp.debug:
echo("S:" & line.string) echo("S:" & result.string)
return line
proc quitExcpt(smtp: Smtp, msg: string) = proc quitExcpt(smtp: Smtp, msg: string) =
smtp.debugSend("QUIT") smtp.debugSend("QUIT")
raise newException(ReplyError, msg) raise newException(ReplyError, msg)
proc checkReply(smtp: var Smtp, reply: string) =
var line = smtp.debugRecv()
if not line.string.startswith(reply):
quitExcpt(smtp, "Expected " & reply & " reply, got: " & line.string)
const compiledWithSsl = defined(ssl) const compiledWithSsl = defined(ssl)
when not defined(ssl): when not defined(ssl):
@ -86,63 +75,6 @@ when not defined(ssl):
else: else:
let defaultSSLContext = newContext(verifyMode = CVerifyNone) let defaultSSLContext = newContext(verifyMode = CVerifyNone)
proc connect*(address: string, port = Port(25),
ssl = false, debug = false,
sslContext = defaultSSLContext): Smtp =
## Establishes a connection with a SMTP server.
## May fail with ReplyError or with a socket error.
result.sock = newSocket()
if ssl:
when compiledWithSsl:
sslContext.wrapSocket(result.sock)
else:
raise newException(ESystem,
"SMTP module compiled without SSL support")
result.sock.connect(address, port)
result.debug = debug
result.checkReply("220")
result.debugSend("HELO " & address & "\c\L")
result.checkReply("250")
proc auth*(smtp: var Smtp, username, password: string) =
## Sends an AUTH command to the server to login as the `username`
## using `password`.
## May fail with ReplyError.
smtp.debugSend("AUTH LOGIN\c\L")
smtp.checkReply("334") # TODO: Check whether it's asking for the "Username:"
# i.e "334 VXNlcm5hbWU6"
smtp.debugSend(encode(username) & "\c\L")
smtp.checkReply("334") # TODO: Same as above, only "Password:" (I think?)
smtp.debugSend(encode(password) & "\c\L")
smtp.checkReply("235") # Check whether the authentification was successful.
proc sendmail*(smtp: var Smtp, fromaddr: string,
toaddrs: seq[string], msg: string) =
## Sends `msg` from `fromaddr` to `toaddr`.
## Messages may be formed using ``createMessage`` by converting the
## Message into a string.
smtp.debugSend("MAIL FROM:<" & fromaddr & ">\c\L")
smtp.checkReply("250")
for address in items(toaddrs):
smtp.debugSend("RCPT TO:<" & address & ">\c\L")
smtp.checkReply("250")
# Send the message
smtp.debugSend("DATA " & "\c\L")
smtp.checkReply("354")
smtp.debugSend(msg & "\c\L")
smtp.debugSend(".\c\L")
smtp.checkReply("250")
proc close*(smtp: Smtp) =
## Disconnects from the SMTP server and closes the socket.
smtp.debugSend("QUIT\c\L")
smtp.sock.close()
proc createMessage*(mSubject, mBody: string, mTo, mCc: seq[string], proc createMessage*(mSubject, mBody: string, mTo, mCc: seq[string],
otherHeaders: openarray[tuple[name, value: string]]): Message = otherHeaders: openarray[tuple[name, value: string]]): Message =
## Creates a new MIME compliant message. ## Creates a new MIME compliant message.
@ -178,81 +110,94 @@ proc `$`*(msg: Message): string =
result.add("\c\L") result.add("\c\L")
result.add(msg.msgBody) result.add(msg.msgBody)
proc newAsyncSmtp*(address: string, port: Port, useSsl = false, proc newSmtp*(useSsl = false, debug=false,
sslContext = defaultSslContext): Smtp =
## Creates a new ``Smtp`` instance.
new result
result.debug = debug
result.sock = newSocket()
if useSsl:
when compiledWithSsl:
sslContext.wrapSocket(result.sock)
else:
raise newException(SystemError,
"SMTP module compiled without SSL support")
proc newAsyncSmtp*(useSsl = false, debug=false,
sslContext = defaultSslContext): AsyncSmtp = sslContext = defaultSslContext): AsyncSmtp =
## Creates a new ``AsyncSmtp`` instance. ## Creates a new ``AsyncSmtp`` instance.
new result new result
result.address = address result.debug = debug
result.port = port
result.useSsl = useSsl
result.sock = newAsyncSocket() result.sock = newAsyncSocket()
if useSsl: if useSsl:
when compiledWithSsl: when compiledWithSsl:
sslContext.wrapSocket(result.sock) sslContext.wrapSocket(result.sock)
else: else:
raise newException(ESystem, raise newException(SystemError,
"SMTP module compiled without SSL support") "SMTP module compiled without SSL support")
proc quitExcpt(smtp: AsyncSmtp, msg: string): Future[void] = proc quitExcpt(smtp: AsyncSmtp, msg: string): Future[void] =
var retFuture = newFuture[void]() var retFuture = newFuture[void]()
var sendFut = smtp.sock.send("QUIT") var sendFut = smtp.debugSend("QUIT")
sendFut.callback = sendFut.callback =
proc () = proc () =
# TODO: Fix this in async procs. # TODO: Fix this in async procs.
raise newException(ReplyError, msg) raise newException(ReplyError, msg)
return retFuture return retFuture
proc checkReply(smtp: AsyncSmtp, reply: string) {.async.} = proc checkReply(smtp: Smtp | AsyncSmtp, reply: string) {.multisync.} =
var line = await smtp.sock.recvLine() var line = await smtp.debugRecv()
if not line.string.startswith(reply): if not line.startswith(reply):
await quitExcpt(smtp, "Expected " & reply & " reply, got: " & line.string) await quitExcpt(smtp, "Expected " & reply & " reply, got: " & line)
proc connect*(smtp: AsyncSmtp) {.async.} = proc connect*(smtp: Smtp | AsyncSmtp,
address: string, port: Port) {.multisync.} =
## Establishes a connection with a SMTP server. ## Establishes a connection with a SMTP server.
## May fail with ReplyError or with a socket error. ## May fail with ReplyError or with a socket error.
await smtp.sock.connect(smtp.address, smtp.port) await smtp.sock.connect(address, port)
await smtp.checkReply("220") await smtp.checkReply("220")
await smtp.sock.send("HELO " & smtp.address & "\c\L") await smtp.debugSend("HELO " & address & "\c\L")
await smtp.checkReply("250") await smtp.checkReply("250")
proc auth*(smtp: AsyncSmtp, username, password: string) {.async.} = proc auth*(smtp: Smtp | AsyncSmtp, username, password: string) {.multisync.} =
## Sends an AUTH command to the server to login as the `username` ## Sends an AUTH command to the server to login as the `username`
## using `password`. ## using `password`.
## May fail with ReplyError. ## May fail with ReplyError.
await smtp.sock.send("AUTH LOGIN\c\L") await smtp.debugSend("AUTH LOGIN\c\L")
await smtp.checkReply("334") # TODO: Check whether it's asking for the "Username:" await smtp.checkReply("334") # TODO: Check whether it's asking for the "Username:"
# i.e "334 VXNlcm5hbWU6" # i.e "334 VXNlcm5hbWU6"
await smtp.sock.send(encode(username) & "\c\L") await smtp.debugSend(encode(username) & "\c\L")
await smtp.checkReply("334") # TODO: Same as above, only "Password:" (I think?) await smtp.checkReply("334") # TODO: Same as above, only "Password:" (I think?)
await smtp.sock.send(encode(password) & "\c\L") await smtp.debugSend(encode(password) & "\c\L")
await smtp.checkReply("235") # Check whether the authentification was successful. await smtp.checkReply("235") # Check whether the authentification was successful.
proc sendMail*(smtp: AsyncSmtp, fromAddr: string, proc sendMail*(smtp: Smtp | AsyncSmtp, fromAddr: string,
toAddrs: seq[string], msg: string) {.async.} = toAddrs: seq[string], msg: string) {.multisync.} =
## Sends ``msg`` from ``fromAddr`` to the addresses specified in ``toAddrs``. ## Sends ``msg`` from ``fromAddr`` to the addresses specified in ``toAddrs``.
## Messages may be formed using ``createMessage`` by converting the ## Messages may be formed using ``createMessage`` by converting the
## Message into a string. ## Message into a string.
await smtp.sock.send("MAIL FROM:<" & fromAddr & ">\c\L") await smtp.debugSend("MAIL FROM:<" & fromAddr & ">\c\L")
await smtp.checkReply("250") await smtp.checkReply("250")
for address in items(toAddrs): for address in items(toAddrs):
await smtp.sock.send("RCPT TO:<" & address & ">\c\L") await smtp.debugSend("RCPT TO:<" & address & ">\c\L")
await smtp.checkReply("250") await smtp.checkReply("250")
# Send the message # Send the message
await smtp.sock.send("DATA " & "\c\L") await smtp.debugSend("DATA " & "\c\L")
await smtp.checkReply("354") await smtp.checkReply("354")
await smtp.sock.send(msg & "\c\L") await smtp.sock.send(msg & "\c\L")
await smtp.sock.send(".\c\L") await smtp.debugSend(".\c\L")
await smtp.checkReply("250") await smtp.checkReply("250")
proc close*(smtp: AsyncSmtp) {.async.} = proc close*(smtp: Smtp | AsyncSmtp) {.multisync.} =
## Disconnects from the SMTP server and closes the socket. ## Disconnects from the SMTP server and closes the socket.
await smtp.sock.send("QUIT\c\L") await smtp.debugSend("QUIT\c\L")
smtp.sock.close() smtp.sock.close()
when not defined(testing) and isMainModule: when not defined(testing) and isMainModule:
@ -278,25 +223,24 @@ when not defined(testing) and isMainModule:
proc async_test() {.async.} = proc async_test() {.async.} =
let client = newAsyncSmtp( let client = newAsyncSmtp(
conf["smtphost"], conf["use_tls"].parseBool,
conf["port"].parseInt.Port, debug=true
conf["use_tls"].parseBool
) )
await client.connect() await client.connect(conf["smtphost"], conf["port"].parseInt.Port)
await client.auth(conf["username"], conf["password"]) await client.auth(conf["username"], conf["password"])
await client.sendMail(conf["sender"], @[conf["recipient"]], $msg) await client.sendMail(conf["sender"], @[conf["recipient"]], $msg)
await client.close() await client.close()
echo "async email sent" echo "async email sent"
proc sync_test() = proc sync_test() =
var smtpConn = connect( var smtpConn = newSmtp(
conf["smtphost"],
conf["port"].parseInt.Port,
conf["use_tls"].parseBool, conf["use_tls"].parseBool,
true, # debug debug=true
) )
smtpConn.connect(conf["smtphost"], conf["port"].parseInt.Port)
smtpConn.auth(conf["username"], conf["password"]) smtpConn.auth(conf["username"], conf["password"])
smtpConn.sendmail(conf["sender"], @[conf["recipient"]], $msg) smtpConn.sendMail(conf["sender"], @[conf["recipient"]], $msg)
smtpConn.close()
echo "sync email sent" echo "sync email sent"
waitFor async_test() waitFor async_test()

View file

@ -417,7 +417,7 @@ type
## Base exception class. ## Base exception class.
## ##
## Each exception has to inherit from `Exception`. See the full `exception ## Each exception has to inherit from `Exception`. See the full `exception
## hierarchy`_. ## hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
parent*: ref Exception ## parent exception (can be used as a stack) parent*: ref Exception ## parent exception (can be used as a stack)
name*: cstring ## The exception's name is its Nim identifier. name*: cstring ## The exception's name is its Nim identifier.
## This field is filled automatically in the ## This field is filled automatically in the
@ -430,51 +430,51 @@ type
SystemError* = object of Exception ## \ SystemError* = object of Exception ## \
## Abstract class for exceptions that the runtime system raises. ## Abstract class for exceptions that the runtime system raises.
## ##
## See the full `exception hierarchy`_. ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
IOError* = object of SystemError ## \ IOError* = object of SystemError ## \
## Raised if an IO error occurred. ## Raised if an IO error occurred.
## ##
## See the full `exception hierarchy`_. ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
EOFError* = object of IOError ## \ EOFError* = object of IOError ## \
## Raised if an IO "end of file" error occurred. ## Raised if an IO "end of file" error occurred.
## ##
## See the full `exception hierarchy`_. ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
OSError* = object of SystemError ## \ OSError* = object of SystemError ## \
## Raised if an operating system service failed. ## Raised if an operating system service failed.
## ##
## See the full `exception hierarchy`_. ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
errorCode*: int32 ## OS-defined error code describing this error. errorCode*: int32 ## OS-defined error code describing this error.
LibraryError* = object of OSError ## \ LibraryError* = object of OSError ## \
## Raised if a dynamic library could not be loaded. ## Raised if a dynamic library could not be loaded.
## ##
## See the full `exception hierarchy`_. ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
ResourceExhaustedError* = object of SystemError ## \ ResourceExhaustedError* = object of SystemError ## \
## Raised if a resource request could not be fulfilled. ## Raised if a resource request could not be fulfilled.
## ##
## See the full `exception hierarchy`_. ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
ArithmeticError* = object of Exception ## \ ArithmeticError* = object of Exception ## \
## Raised if any kind of arithmetic error occurred. ## Raised if any kind of arithmetic error occurred.
## ##
## See the full `exception hierarchy`_. ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
DivByZeroError* = object of ArithmeticError ## \ DivByZeroError* = object of ArithmeticError ## \
## Raised for runtime integer divide-by-zero errors. ## Raised for runtime integer divide-by-zero errors.
## ##
## See the full `exception hierarchy`_. ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
OverflowError* = object of ArithmeticError ## \ OverflowError* = object of ArithmeticError ## \
## Raised for runtime integer overflows. ## Raised for runtime integer overflows.
## ##
## This happens for calculations whose results are too large to fit in the ## This happens for calculations whose results are too large to fit in the
## provided bits. See the full `exception hierarchy`_. ## provided bits. See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
AccessViolationError* = object of Exception ## \ AccessViolationError* = object of Exception ## \
## Raised for invalid memory access errors ## Raised for invalid memory access errors
## ##
## See the full `exception hierarchy`_. ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
AssertionError* = object of Exception ## \ AssertionError* = object of Exception ## \
## Raised when assertion is proved wrong. ## Raised when assertion is proved wrong.
## ##
## Usually the result of using the `assert() template <#assert>`_. See the ## Usually the result of using the `assert() template <#assert>`_. See the
## full `exception hierarchy`_. ## full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
ValueError* = object of Exception ## \ ValueError* = object of Exception ## \
## Raised for string and object conversion errors. ## Raised for string and object conversion errors.
KeyError* = object of ValueError ## \ KeyError* = object of ValueError ## \
@ -482,66 +482,66 @@ type
## ##
## Mostly used by the `tables <tables.html>`_ module, it can also be raised ## Mostly used by the `tables <tables.html>`_ module, it can also be raised
## by other collection modules like `sets <sets.html>`_ or `strtabs ## by other collection modules like `sets <sets.html>`_ or `strtabs
## <strtabs.html>`_. See the full `exception hierarchy`_. ## <strtabs.html>`_. See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
OutOfMemError* = object of SystemError ## \ OutOfMemError* = object of SystemError ## \
## Raised for unsuccessful attempts to allocate memory. ## Raised for unsuccessful attempts to allocate memory.
## ##
## See the full `exception hierarchy`_. ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
IndexError* = object of Exception ## \ IndexError* = object of Exception ## \
## Raised if an array index is out of bounds. ## Raised if an array index is out of bounds.
## ##
## See the full `exception hierarchy`_. ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
FieldError* = object of Exception ## \ FieldError* = object of Exception ## \
## Raised if a record field is not accessible because its dicriminant's ## Raised if a record field is not accessible because its dicriminant's
## value does not fit. ## value does not fit.
## ##
## See the full `exception hierarchy`_. ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
RangeError* = object of Exception ## \ RangeError* = object of Exception ## \
## Raised if a range check error occurred. ## Raised if a range check error occurred.
## ##
## See the full `exception hierarchy`_. ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
StackOverflowError* = object of SystemError ## \ StackOverflowError* = object of SystemError ## \
## Raised if the hardware stack used for subroutine calls overflowed. ## Raised if the hardware stack used for subroutine calls overflowed.
## ##
## See the full `exception hierarchy`_. ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
ReraiseError* = object of Exception ## \ ReraiseError* = object of Exception ## \
## Raised if there is no exception to reraise. ## Raised if there is no exception to reraise.
## ##
## See the full `exception hierarchy`_. ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
ObjectAssignmentError* = object of Exception ## \ ObjectAssignmentError* = object of Exception ## \
## Raised if an object gets assigned to its parent's object. ## Raised if an object gets assigned to its parent's object.
## ##
## See the full `exception hierarchy`_. ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
ObjectConversionError* = object of Exception ## \ ObjectConversionError* = object of Exception ## \
## Raised if an object is converted to an incompatible object type. ## Raised if an object is converted to an incompatible object type.
## You can use ``of`` operator to check if conversion will succeed. ## You can use ``of`` operator to check if conversion will succeed.
## ##
## See the full `exception hierarchy`_. ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
FloatingPointError* = object of Exception ## \ FloatingPointError* = object of Exception ## \
## Base class for floating point exceptions. ## Base class for floating point exceptions.
## ##
## See the full `exception hierarchy`_. ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
FloatInvalidOpError* = object of FloatingPointError ## \ FloatInvalidOpError* = object of FloatingPointError ## \
## Raised by invalid operations according to IEEE. ## Raised by invalid operations according to IEEE.
## ##
## Raised by ``0.0/0.0``, for example. See the full `exception ## Raised by ``0.0/0.0``, for example. See the full `exception
## hierarchy`_. ## hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
FloatDivByZeroError* = object of FloatingPointError ## \ FloatDivByZeroError* = object of FloatingPointError ## \
## Raised by division by zero. ## Raised by division by zero.
## ##
## Divisor is zero and dividend is a finite nonzero number. See the full ## Divisor is zero and dividend is a finite nonzero number. See the full
## `exception hierarchy`_. ## `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
FloatOverflowError* = object of FloatingPointError ## \ FloatOverflowError* = object of FloatingPointError ## \
## Raised for overflows. ## Raised for overflows.
## ##
## The operation produced a result that exceeds the range of the exponent. ## The operation produced a result that exceeds the range of the exponent.
## See the full `exception hierarchy`_. ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
FloatUnderflowError* = object of FloatingPointError ## \ FloatUnderflowError* = object of FloatingPointError ## \
## Raised for underflows. ## Raised for underflows.
## ##
## The operation produced a result that is too small to be represented as a ## The operation produced a result that is too small to be represented as a
## normal number. See the full `exception hierarchy`_. ## normal number. See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
FloatInexactError* = object of FloatingPointError ## \ FloatInexactError* = object of FloatingPointError ## \
## Raised for inexact results. ## Raised for inexact results.
## ##
@ -549,11 +549,11 @@ type
## precision -- for example: ``2.0 / 3.0, log(1.1)`` ## precision -- for example: ``2.0 / 3.0, log(1.1)``
## ##
## **NOTE**: Nim currently does not detect these! See the full ## **NOTE**: Nim currently does not detect these! See the full
## `exception hierarchy`_. ## `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
DeadThreadError* = object of Exception ## \ DeadThreadError* = object of Exception ## \
## Raised if it is attempted to send a message to a dead thread. ## Raised if it is attempted to send a message to a dead thread.
## ##
## See the full `exception hierarchy`_. ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
{.deprecated: [TObject: RootObj, PObject: RootRef, TEffect: RootEffect, {.deprecated: [TObject: RootObj, PObject: RootRef, TEffect: RootEffect,
FTime: TimeEffect, FIO: IOEffect, FReadIO: ReadIOEffect, FTime: TimeEffect, FIO: IOEffect, FReadIO: ReadIOEffect,

View file

@ -38,10 +38,10 @@ Options:
--epc use emacs epc mode --epc use emacs epc mode
--debug enable debug output --debug enable debug output
--log enable verbose logging to nimsuggest.log file --log enable verbose logging to nimsuggest.log file
--v2 use version 2 of the protocol; more features and --v1 use version 1 of the protocol; for backwards compatibility
much faster
--refresh perform automatic refreshes to keep the analysis precise --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 '""" & DummyEof & """' for the tester
The server then listens to the connection and takes line-based commands. 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. are supported.
""" """
type type
Mode = enum mstdin, mtcp, mepc, mcmdline Mode = enum mstdin, mtcp, mepc, mcmdsug, mcmdcon
CachedMsg = object CachedMsg = object
info: TLineInfo info: TLineInfo
msg: string msg: string
@ -62,7 +62,7 @@ var
gAddress = "" gAddress = ""
gMode: Mode gMode: Mode
gEmitEof: bool # whether we write '!EOF!' dummy lines gEmitEof: bool # whether we write '!EOF!' dummy lines
gLogging = false gLogging = defined(logging)
gRefresh: bool gRefresh: bool
requests: Channel[string] requests: Channel[string]
@ -79,6 +79,9 @@ proc errorHook(info: TLineInfo; msg: string; sev: Severity) =
line: toLinenumber(info), column: toColumn(info), doc: msg, line: toLinenumber(info), column: toColumn(info), doc: msg,
forth: $sev)) forth: $sev))
proc myLog(s: string) =
if gLogging: log(s)
const const
seps = {':', ';', ' ', '\t'} seps = {':', ';', ' ', '\t'}
Help = "usage: sug|con|def|use|dus|chk|mod|highlight|outline|known file.nim[;dirtyfile.nim]:line:col\n" & 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; proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int;
graph: ModuleGraph; cache: IdentCache) = graph: ModuleGraph; cache: IdentCache) =
if gLogging: myLog("cmd: " & $cmd & ", file: " & file & ", dirtyFile: " & dirtyfile &
log("cmd: " & $cmd & ", file: " & file & ", dirtyFile: " & dirtyfile & "[" & $line & ":" & $col & "]")
"[" & $line & ":" & $col & "]")
gIdeCmd = cmd gIdeCmd = cmd
if cmd == ideChk: if cmd == ideChk:
msgs.structuredErrorHook = errorHook msgs.structuredErrorHook = errorHook
msgs.writelnHook = proc (s: string) = discard msgs.writelnHook = myLog
else: else:
msgs.structuredErrorHook = nil msgs.structuredErrorHook = nil
msgs.writelnHook = proc (s: string) = discard msgs.writelnHook = myLog
if cmd == ideUse and suggestVersion != 2: if cmd == ideUse and suggestVersion != 0:
graph.resetAllModules() graph.resetAllModules()
var isKnownFile = true var isKnownFile = true
let dirtyIdx = file.fileInfoIdx(isKnownFile) let dirtyIdx = file.fileInfoIdx(isKnownFile)
@ -174,12 +176,13 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int;
else: msgs.setDirtyFile(dirtyIdx, nil) else: msgs.setDirtyFile(dirtyIdx, nil)
gTrackPos = newLineInfo(dirtyIdx, line, col) gTrackPos = newLineInfo(dirtyIdx, line, col)
gTrackPosAttached = false
gErrorCounter = 0 gErrorCounter = 0
if suggestVersion < 2: if suggestVersion == 1:
graph.usageSym = nil graph.usageSym = nil
if not isKnownFile: if not isKnownFile:
graph.compileProject(cache) graph.compileProject(cache)
if suggestVersion == 2 and gIdeCmd in {ideUse, ideDus} and if suggestVersion == 0 and gIdeCmd in {ideUse, ideDus} and
dirtyfile.len == 0: dirtyfile.len == 0:
discard "no need to recompile anything" discard "no need to recompile anything"
else: else:
@ -189,7 +192,7 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int;
if gIdeCmd != ideMod: if gIdeCmd != ideMod:
graph.compileProject(cache, modIdx) graph.compileProject(cache, modIdx)
if gIdeCmd in {ideUse, ideDus}: 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: if u != nil:
listUsages(u) listUsages(u)
else: else:
@ -352,8 +355,7 @@ proc replEpc(x: ThreadParams) {.thread.} =
setVerbosity(0) setVerbosity(0)
else: discard else: discard
let cmd = $gIdeCmd & " " & args.argsToStr let cmd = $gIdeCmd & " " & args.argsToStr
if gLogging: myLog "MSG CMD: " & cmd
log "MSG CMD: " & cmd
requests.send(cmd) requests.send(cmd)
toEpc(client, uid) toEpc(client, uid)
of "methods": of "methods":
@ -424,7 +426,7 @@ proc execCmd(cmd: string; graph: ModuleGraph; cache: IdentCache; cachedMsgs: Cac
else: else:
if gIdeCmd == ideChk: if gIdeCmd == ideChk:
for cm in cachedMsgs: errorHook(cm.info, cm.msg, cm.sev) 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() sentinel()
proc recompileFullProject(graph: ModuleGraph; cache: IdentCache) = proc recompileFullProject(graph: ModuleGraph; cache: IdentCache) =
@ -502,8 +504,10 @@ proc mainCommand(graph: ModuleGraph; cache: IdentCache) =
of mstdin: createThread(inputThread, replStdin, (gPort, gAddress)) of mstdin: createThread(inputThread, replStdin, (gPort, gAddress))
of mtcp: createThread(inputThread, replTcp, (gPort, gAddress)) of mtcp: createThread(inputThread, replTcp, (gPort, gAddress))
of mepc: createThread(inputThread, replEpc, (gPort, gAddress)) of mepc: createThread(inputThread, replEpc, (gPort, gAddress))
of mcmdline: createThread(inputThread, replCmdline, of mcmdsug: createThread(inputThread, replCmdline,
(gPort, "sug \"" & options.gProjectFull & "\":" & gAddress)) (gPort, "sug \"" & options.gProjectFull & "\":" & gAddress))
of mcmdcon: createThread(inputThread, replCmdline,
(gPort, "con \"" & options.gProjectFull & "\":" & gAddress))
mainThread(graph, cache) mainThread(graph, cache)
joinThread(inputThread) joinThread(inputThread)
close(requests) close(requests)
@ -524,17 +528,21 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
gAddress = p.val gAddress = p.val
gMode = mtcp gMode = mtcp
of "stdin": gMode = mstdin of "stdin": gMode = mstdin
of "cmdline": of "cmdsug":
gMode = mcmdline gMode = mcmdsug
suggestVersion = 2
gAddress = p.val gAddress = p.val
incl(gGlobalOptions, optIdeDebug)
of "cmdcon":
gMode = mcmdcon
gAddress = p.val
incl(gGlobalOptions, optIdeDebug)
of "epc": of "epc":
gMode = mepc gMode = mepc
gVerbosity = 0 # Port number gotta be first. gVerbosity = 0 # Port number gotta be first.
of "debug": incl(gGlobalOptions, optIdeDebug) of "debug": incl(gGlobalOptions, optIdeDebug)
of "v2": suggestVersion = 2 of "v2": suggestVersion = 0
of "v1": suggestVersion = 1
of "tester": of "tester":
suggestVersion = 2
gMode = mstdin gMode = mstdin
gEmitEof = true gEmitEof = true
gRefresh = false gRefresh = false
@ -544,9 +552,17 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
gRefresh = parseBool(p.val) gRefresh = parseBool(p.val)
else: else:
gRefresh = true gRefresh = true
of "maxresults":
suggestMaxResults = parseInt(p.val)
else: processSwitch(pass, p) else: processSwitch(pass, p)
of cmdArgument: 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 # if processArgument(pass, p, argsCount): break
proc handleCmdLine(cache: IdentCache; config: ConfigRef) = 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") "Cannot find Nim standard library: Nim compiler not in PATH")
gPrefixDir = binaryPath.splitPath().head.parentDir() gPrefixDir = binaryPath.splitPath().head.parentDir()
#msgs.writelnHook = proc (line: string) = log(line) #msgs.writelnHook = proc (line: string) = log(line)
if gLogging: myLog("START " & gProjectFull)
log("START " & gProjectFull)
loadConfigs(DefaultConfig, cache, config) # load all config files loadConfigs(DefaultConfig, cache, config) # load all config files
# now process command line arguments again, because some options in the # now process command line arguments again, because some options in the

View file

@ -22,4 +22,4 @@ define:nimsuggest
--path:"$nim" --path:"$nim"
--threads:on --threads:on
--noNimblePath --noNimblePath
--path:"../../compiler" --path:"../compiler"

View file

@ -30,8 +30,8 @@ proc parseTest(filename: string; epcMode=false): Test =
var markers = newSeq[string]() var markers = newSeq[string]()
var i = 1 var i = 1
for x in lines(filename): for x in lines(filename):
let marker = x.find(cursorMarker)+1 let marker = x.find(cursorMarker)
if marker > 0: if marker >= 0:
if epcMode: if epcMode:
markers.add "(\"" & filename & "\" " & $i & " " & $marker & " \"" & result.dest & "\")" markers.add "(\"" & filename & "\" " & $i & " " & $marker & " \"" & result.dest & "\")"
else: else:
@ -304,8 +304,7 @@ proc runTest(filename: string): int =
proc main() = proc main() =
var failures = 0 var failures = 0
if os.paramCount() > 0: if os.paramCount() > 0:
let f = os.paramStr(1) let x = os.paramStr(1)
let x = getAppDir() / f
let xx = expandFilename x let xx = expandFilename x
failures += runTest(xx) failures += runTest(xx)
failures += runEpcTest(xx) failures += runEpcTest(xx)

View 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
"""

View 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
"""

View file

@ -11,4 +11,4 @@ type
x, y: int x, y: int
proc main(f: Foo) = proc main(f: Foo) =
f.#[!]# if f.#[!]#:

View 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".#[!]#

View file

@ -1,7 +1,7 @@
discard """ discard """
$nimsuggest --tester compiler/nim.nim $nimsuggest --tester compiler/nim.nim
>def compiler/semexprs.nim:13:50 >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 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
""" """

View 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
View 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
View 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
View 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
View 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
View 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

View 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

View file

@ -5,7 +5,7 @@ discard """
import import
macros, strutils macros, strutils
macro test_macro*(n: stmt): stmt {.immediate.} = macro test_macro*(s: string, n: stmt): stmt {.immediate.} =
result = newNimNode(nnkStmtList) result = newNimNode(nnkStmtList)
var ass : NimNode = newNimNode(nnkAsgn) var ass : NimNode = newNimNode(nnkAsgn)
add(ass, newIdentNode("str")) add(ass, newIdentNode("str"))

View file

@ -6,7 +6,7 @@ discard """
import macros import macros
macro quoteWords(n: expr): expr {.immediate.} = macro quoteWords(n: varargs[expr]): expr {.immediate.} =
let n = callsite() let n = callsite()
result = newNimNode(nnkBracket, n) result = newNimNode(nnkBracket, n)
for i in 1..n.len-1: for i in 1..n.len-1:

View 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"

View file

@ -18,7 +18,7 @@ mm = memfiles.open(fn, mode = fmReadWrite, newFileSize = 20)
mm.close() mm.close()
# read, change # read, change
mm_full = memfiles.open(fn, mode = fmWrite, mappedSize = -1) mm_full = memfiles.open(fn, mode = fmWrite, mappedSize = -1, allowRemap = true)
echo "Full read size: ",mm_full.size echo "Full read size: ",mm_full.size
p = mm_full.mapMem(fmReadWrite, 20, 0) p = mm_full.mapMem(fmReadWrite, 20, 0)
var p2 = cast[cstring](p) var p2 = cast[cstring](p)

14
tools/nimgrind.supp Normal file
View file

@ -0,0 +1,14 @@
{
markstackandregisters_Cond
Memcheck:Cond
...
fun:markStackAndRegisters*
...
}
{
markstackandregisters_Value8
Memcheck:Value8
...
fun:markStackAndRegisters*
...
}

View file

@ -23,6 +23,12 @@ Changes affecting backwards compatibility
pointer. Now the hash is calculated from the contents of the string, assuming pointer. Now the hash is calculated from the contents of the string, assuming
``cstring`` is a null-terminated string. Equal ``string`` and ``cstring`` ``cstring`` is a null-terminated string. Equal ``string`` and ``cstring``
values produce an equal hash value. values produce an equal hash value.
- Macros accepting `varargs` arguments will now receive a node having the
`nkArgList` node kind. Previous code expecting the node kind to be `nkBracket`
may have to be updated.
- ``memfiles.open`` now closes file handleds/fds by default. Passing
``allowRemap=true`` to ``memfiles.open`` recovers the old behavior. The old
behavior is only needed to call ``mapMem`` on the resulting ``MemFile``.
Library Additions Library Additions
----------------- -----------------