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