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
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(L: static[int]): array[L, int]
# can be attached to ranges to indicate that the range
# can be attached to generic procs with free standing
# type parameters: e.g. proc foo[T]()
# depends on unresolved static params.
tfRetType, # marks return types in proc (used to detect type classes
# used as return types for return type inference)

View File

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

View File

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

View File

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

View File

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

View File

@@ -392,6 +392,23 @@ proc findModule*(modulename, currentModule: string): string =
result = findFile(m)
patchModule()
proc findProjectNimFile*(pkg: string): string =
const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"]
var candidates: seq[string] = @[]
for k, f in os.walkDir(pkg, relative=true):
if k == pcFile and f != "config.nims":
let (_, name, ext) = splitFile(f)
if ext in extensions:
let x = changeFileExt(pkg / name, ".nim")
if fileExists(x):
candidates.add x
for c in candidates:
# nim-foo foo or foo nfoo
if (pkg in c) or (c in pkg): return c
if candidates.len >= 1:
return candidates[0]
return ""
proc canonDynlibName(s: string): string =
let start = if s.startsWith("lib"): 3 else: 0
let ende = strutils.find(s, {'(', ')', '.'})
@@ -420,11 +437,6 @@ proc binaryStrSearch*(x: openArray[string], y: string): int =
return mid
result = - 1
template nimdbg*: untyped = c.module.fileIdx == gProjectMainIdx
template cnimdbg*: untyped = p.module.module.fileIdx == gProjectMainIdx
template pnimdbg*: untyped = p.lex.fileIdx == gProjectMainIdx
template lnimdbg*: untyped = L.fileIdx == gProjectMainIdx
proc parseIdeCmd*(s: string): IdeCmd =
case s:
of "sug": ideSug

View File

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

View File

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

View File

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

View File

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

View File

@@ -205,7 +205,8 @@ proc semCase(c: PContext, n: PNode): PNode =
var typ = commonTypeBegin
var hasElse = false
var notOrdinal = false
case skipTypes(n.sons[0].typ, abstractVarRange-{tyTypeDesc}).kind
let caseTyp = skipTypes(n.sons[0].typ, abstractVarRange-{tyTypeDesc})
case caseTyp.kind
of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool:
chckCovered = true
of tyFloat..tyFloat128, tyString, tyError:
@@ -215,6 +216,9 @@ proc semCase(c: PContext, n: PNode): PNode =
return
for i in countup(1, sonsLen(n) - 1):
var x = n.sons[i]
when defined(nimsuggest):
if gIdeCmd == ideSug and exactEquals(gTrackPos, x.info) and caseTyp.kind == tyEnum:
suggestEnum(c, x, caseTyp)
case x.kind
of nkOfBranch:
checkMinSonsLen(x, 2)
@@ -499,6 +503,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
if hasEmpty(typ):
localError(def.info, errCannotInferTypeOfTheLiteral,
($typ.kind).substr(2).toLowerAscii)
elif typ.kind == tyProc and tfUnresolved in typ.flags:
localError(def.info, errProcHasNoConcreteType, def.renderTree)
else:
if symkind == skLet: localError(a.info, errLetNeedsInit)
@@ -711,7 +717,11 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
# we even look at the type definitions on the right
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
if gCmd == cmdIdeTools: suggestStmt(c, a)
when defined(nimsuggest):
if gCmd == cmdIdeTools:
inc c.inTypeContext
suggestStmt(c, a)
dec c.inTypeContext
if a.kind == nkCommentStmt: continue
if a.kind != nkTypeDef: illFormedAst(a)
checkSonsLen(a, 3)

View File

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

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}:
copyCandidate(z, m)
z.callee = arg.sons[i].typ
if tfUnresolved in z.callee.flags: continue
z.calleeSym = arg.sons[i].sym
#if arg.sons[i].sym.name.s == "cmp":
# ggDebug = true
@@ -1650,7 +1651,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
if a >= formalLen-1 and formal != nil and formal.typ.isVarargsUntyped:
incl(marker, formal.position)
if container.isNil:
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, n.info))
container = newNodeIT(nkArgList, n.sons[a].info, arrayConstr(c, n.info))
setSon(m.call, formal.position + 1, container)
else:
incrIndexType(container.typ)
@@ -1738,7 +1739,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
if formal.typ.isVarargsUntyped:
if container.isNil:
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, n.info))
container = newNodeIT(nkArgList, n.sons[a].info, arrayConstr(c, n.info))
setSon(m.call, formal.position + 1, container)
else:
incrIndexType(container.typ)

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,7 @@ Procedures
==========
What most programming languages call `methods`:idx: or `functions`:idx: are
called `procedures`:idx: in Nim (which is the correct terminology). A procedure
called `procedures`:idx: in Nim. A procedure
declaration consists of an identifier, zero or more formal parameters, a return
value type and a block of code. Formal parameters are declared as a list of
identifiers separated by either comma or semicolon. A parameter is given a type

View File

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

View File

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

View File

@@ -8,8 +8,9 @@
#
## This module implements a `crit bit tree`:idx: which is an efficient
## container for a set or a mapping of strings. Based on the excellent paper
## container for a sorted set of strings, or for a sorted mapping of strings. Based on the excellent paper
## by Adam Langley.
## (A crit bit tree is a form of `radix tree`:idx: or `patricia trie`:idx:.)
include "system/inclrtl"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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
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 """
$nimsuggest --tester compiler/nim.nim
>def compiler/semexprs.nim:13:50
def;;skType;;ast.PSym;;PSym;;*ast.nim;;669;;2;;"";;100
def;;skType;;ast.PSym;;PSym;;*ast.nim;;671;;2;;"";;100
>def compiler/semexprs.nim:13:50
def;;skType;;ast.PSym;;PSym;;*ast.nim;;669;;2;;"";;100
def;;skType;;ast.PSym;;PSym;;*ast.nim;;671;;2;;"";;100
"""

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
macros, strutils
macro test_macro*(n: stmt): stmt {.immediate.} =
macro test_macro*(s: string, n: stmt): stmt {.immediate.} =
result = newNimNode(nnkStmtList)
var ass : NimNode = newNimNode(nnkAsgn)
add(ass, newIdentNode("str"))

View File

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

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()
# read, change
mm_full = memfiles.open(fn, mode = fmWrite, mappedSize = -1)
mm_full = memfiles.open(fn, mode = fmWrite, mappedSize = -1, allowRemap = true)
echo "Full read size: ",mm_full.size
p = mm_full.mapMem(fmReadWrite, 20, 0)
var p2 = cast[cstring](p)

14
tools/nimgrind.supp Normal file
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
``cstring`` is a null-terminated string. Equal ``string`` and ``cstring``
values produce an equal hash value.
- Macros accepting `varargs` arguments will now receive a node having the
`nkArgList` node kind. Previous code expecting the node kind to be `nkBracket`
may have to be updated.
- ``memfiles.open`` now closes file handleds/fds by default. Passing
``allowRemap=true`` to ``memfiles.open`` recovers the old behavior. The old
behavior is only needed to call ``mapMem`` on the resulting ``MemFile``.
Library Additions
-----------------