Merge branch 'devel' into jpoirier-realtimeGCTest

This commit is contained in:
Simon Hafner 2015-04-01 06:33:24 +05:00
commit a339cb5ed5
42 changed files with 672 additions and 409 deletions

View file

@ -529,7 +529,7 @@ type
TMagic* = enum # symbols that require compiler magic: TMagic* = enum # symbols that require compiler magic:
mNone, mNone,
mDefined, mDefinedInScope, mCompiles, mDefined, mDefinedInScope, mCompiles,
mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof,
mEcho, mShallowCopy, mSlurp, mStaticExec, mEcho, mShallowCopy, mSlurp, mStaticExec,
mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst, mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
mUnaryLt, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray, mUnaryLt, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray,
@ -557,6 +557,7 @@ type
mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr,
mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet, mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet,
mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mSlice, mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mSlice,
mDotDot, # this one is only necessary to give nice compile time warnings
mFields, mFieldPairs, mOmpParFor, mFields, mFieldPairs, mOmpParFor,
mAppendStrCh, mAppendStrStr, mAppendSeqElem, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq, mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq,

View file

@ -1749,6 +1749,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
initLocExpr(p, x, a) initLocExpr(p, x, a)
initLocExpr(p, e.sons[2], b) initLocExpr(p, e.sons[2], b)
genDeepCopy(p, a, b) genDeepCopy(p, a, b)
of mDotDot: genCall(p, e, d)
else: internalError(e.info, "genMagicExpr: " & $op) else: internalError(e.info, "genMagicExpr: " & $op)
proc genConstExpr(p: BProc, n: PNode): PRope proc genConstExpr(p: BProc, n: PNode): PRope

View file

@ -94,7 +94,7 @@ proc writeIntSet(a: IntSet, s: var string) =
encodeVInt(x, s) encodeVInt(x, s)
inc i inc i
s.add('}') s.add('}')
proc genMergeInfo*(m: BModule): PRope = proc genMergeInfo*(m: BModule): PRope =
if optSymbolFiles notin gGlobalOptions: return nil if optSymbolFiles notin gGlobalOptions: return nil
var s = "/*\tNIM_merge_INFO:" var s = "/*\tNIM_merge_INFO:"
@ -113,7 +113,7 @@ proc genMergeInfo*(m: BModule): PRope =
s.add("*/") s.add("*/")
result = s.toRope result = s.toRope
template `^`(pos: expr): expr = L.buf[pos] template `^`(pos: int): expr = L.buf[pos]
proc skipWhite(L: var TBaseLexer) = proc skipWhite(L: var TBaseLexer) =
var pos = L.bufpos var pos = L.bufpos
@ -132,7 +132,7 @@ proc skipUntilCmd(L: var TBaseLexer) =
of CR: pos = nimlexbase.handleCR(L, pos) of CR: pos = nimlexbase.handleCR(L, pos)
of LF: pos = nimlexbase.handleLF(L, pos) of LF: pos = nimlexbase.handleLF(L, pos)
of '\0': break of '\0': break
of '/': of '/':
if ^(pos+1) == '*' and ^(pos+2) == '\t': if ^(pos+1) == '*' and ^(pos+2) == '\t':
inc pos, 3 inc pos, 3
break break
@ -145,7 +145,7 @@ proc atEndMark(buf: cstring, pos: int): bool =
while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
result = s == NimMergeEndMark.len result = s == NimMergeEndMark.len
proc readVerbatimSection(L: var TBaseLexer): PRope = proc readVerbatimSection(L: var TBaseLexer): PRope =
var pos = L.bufpos var pos = L.bufpos
var buf = L.buf var buf = L.buf
var r = newStringOfCap(30_000) var r = newStringOfCap(30_000)
@ -162,7 +162,7 @@ proc readVerbatimSection(L: var TBaseLexer): PRope =
of '\0': of '\0':
internalError("ccgmerge: expected: " & NimMergeEndMark) internalError("ccgmerge: expected: " & NimMergeEndMark)
break break
else: else:
if atEndMark(buf, pos): if atEndMark(buf, pos):
inc pos, NimMergeEndMark.len inc pos, NimMergeEndMark.len
break break
@ -181,7 +181,7 @@ proc readKey(L: var TBaseLexer, result: var string) =
if buf[pos] != ':': internalError("ccgmerge: ':' expected") if buf[pos] != ':': internalError("ccgmerge: ':' expected")
L.bufpos = pos + 1 # skip ':' L.bufpos = pos + 1 # skip ':'
proc newFakeType(id: int): PType = proc newFakeType(id: int): PType =
new(result) new(result)
result.id = id result.id = id
@ -227,8 +227,8 @@ proc processMergeInfo(L: var TBaseLexer, m: BModule) =
when not defined(nimhygiene): when not defined(nimhygiene):
{.pragma: inject.} {.pragma: inject.}
template withCFile(cfilename: string, body: stmt) {.immediate.} = template withCFile(cfilename: string, body: stmt) {.immediate.} =
var s = llStreamOpen(cfilename, fmRead) var s = llStreamOpen(cfilename, fmRead)
if s == nil: return if s == nil: return
var L {.inject.}: TBaseLexer var L {.inject.}: TBaseLexer
@ -239,7 +239,7 @@ template withCFile(cfilename: string, body: stmt) {.immediate.} =
if ^L.bufpos == '\0': break if ^L.bufpos == '\0': break
body body
closeBaseLexer(L) closeBaseLexer(L)
proc readMergeInfo*(cfilename: string, m: BModule) = proc readMergeInfo*(cfilename: string, m: BModule) =
## reads the merge meta information into `m`. ## reads the merge meta information into `m`.
withCFile(cfilename): withCFile(cfilename):
@ -257,7 +257,7 @@ proc readMergeSections(cfilename: string, m: var TMergeSections) =
## reads the merge sections into `m`. ## reads the merge sections into `m`.
withCFile(cfilename): withCFile(cfilename):
readKey(L, k) readKey(L, k)
if k == "NIM_merge_INFO": if k == "NIM_merge_INFO":
discard discard
elif ^L.bufpos == '*' and ^(L.bufpos+1) == '/': elif ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
inc(L.bufpos, 2) inc(L.bufpos, 2)
@ -283,7 +283,7 @@ proc mergeRequired*(m: BModule): bool =
#echo "not empty: ", i, " ", ropeToStr(m.s[i]) #echo "not empty: ", i, " ", ropeToStr(m.s[i])
return true return true
for i in low(TCProcSection)..high(TCProcSection): for i in low(TCProcSection)..high(TCProcSection):
if m.initProc.s(i) != nil: if m.initProc.s(i) != nil:
#echo "not empty: ", i, " ", ropeToStr(m.initProc.s[i]) #echo "not empty: ", i, " ", ropeToStr(m.initProc.s[i])
return true return true
@ -291,7 +291,7 @@ proc mergeFiles*(cfilename: string, m: BModule) =
## merges the C file with the old version on hard disc. ## merges the C file with the old version on hard disc.
var old: TMergeSections var old: TMergeSections
readMergeSections(cfilename, old) readMergeSections(cfilename, old)
# do the merge; old section before new section: # do the merge; old section before new section:
for i in low(TCFileSection)..high(TCFileSection): for i in low(TCFileSection)..high(TCFileSection):
m.s[i] = con(old.f[i], m.s[i]) m.s[i] = con(old.f[i], m.s[i])
for i in low(TCProcSection)..high(TCProcSection): for i in low(TCProcSection)..high(TCProcSection):

View file

@ -131,11 +131,10 @@ proc semNodeKindConstraints*(p: PNode): PNode =
result.strVal.add(ppEof) result.strVal.add(ppEof)
type type
TSideEffectAnalysis = enum TSideEffectAnalysis* = enum
seUnknown, seSideEffect, seNoSideEffect seUnknown, seSideEffect, seNoSideEffect
proc checkForSideEffects(n: PNode): TSideEffectAnalysis = proc checkForSideEffects*(n: PNode): TSideEffectAnalysis =
# XXX is 'raise' a side effect?
case n.kind case n.kind
of nkCallKinds: of nkCallKinds:
# only calls can produce side effects: # only calls can produce side effects:
@ -162,6 +161,8 @@ proc checkForSideEffects(n: PNode): TSideEffectAnalysis =
# an atom cannot produce a side effect: # an atom cannot produce a side effect:
result = seNoSideEffect result = seNoSideEffect
else: else:
# assume no side effect:
result = seNoSideEffect
for i in 0 .. <n.len: for i in 0 .. <n.len:
let ret = checkForSideEffects(n.sons[i]) let ret = checkForSideEffects(n.sons[i])
if ret == seSideEffect: return ret if ret == seSideEffect: return ret

View file

@ -241,9 +241,15 @@ proc isOperator(tok: TToken): bool =
proc isUnary(p: TParser): bool = proc isUnary(p: TParser): bool =
## Check if the current parser token is a unary operator ## Check if the current parser token is a unary operator
p.strongSpaces and p.tok.tokType in {tkOpr, tkDotDot} and if p.tok.tokType in {tkOpr, tkDotDot} and
p.tok.strongSpaceB == 0 and p.tok.strongSpaceB == 0 and
p.tok.strongSpaceA > 0 p.tok.strongSpaceA > 0:
# XXX change this after 0.10.4 is out
if p.strongSpaces:
result = true
else:
parMessage(p, warnDeprecated,
"will be parsed as unary operator; inconsistent spacing")
proc checkBinary(p: TParser) {.inline.} = proc checkBinary(p: TParser) {.inline.} =
## Check if the current parser token is a binary operator. ## Check if the current parser token is a binary operator.

View file

@ -201,7 +201,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
elif nfDotSetter in n.flags: elif nfDotSetter in n.flags:
internalAssert f.kind == nkIdent and n.sonsLen == 3 internalAssert f.kind == nkIdent and n.sonsLen == 3
let calleeName = newStrNode(nkStrLit, f.ident.s[0.. -2]).withInfo(n.info) let calleeName = newStrNode(nkStrLit,
f.ident.s[0..f.ident.s.len-2]).withInfo(n.info)
let callOp = newIdentNode(getIdent".=", n.info) let callOp = newIdentNode(getIdent".=", n.info)
n.sons[0..1] = [callOp, n[1], calleeName] n.sons[0..1] = [callOp, n[1], calleeName]
orig.sons[0..1] = [callOp, orig[1], calleeName] orig.sons[0..1] = [callOp, orig[1], calleeName]

View file

@ -9,13 +9,13 @@
## This module contains the data structures for the semantic checking phase. ## This module contains the data structures for the semantic checking phase.
import import
strutils, lists, intsets, options, lexer, ast, astalgo, trees, treetab, strutils, lists, intsets, options, lexer, ast, astalgo, trees, treetab,
wordrecg, wordrecg,
ropes, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math, ropes, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math,
magicsys, nversion, nimsets, parser, times, passes, rodread, vmdef magicsys, nversion, nimsets, parser, times, passes, rodread, vmdef
type type
TOptionEntry* = object of lists.TListEntry # entries to put on a TOptionEntry* = object of lists.TListEntry # entries to put on a
# stack for pragma parsing # stack for pragma parsing
options*: TOptions options*: TOptions
@ -26,7 +26,7 @@ type
POptionEntry* = ref TOptionEntry POptionEntry* = ref TOptionEntry
PProcCon* = ref TProcCon PProcCon* = ref TProcCon
TProcCon*{.final.} = object # procedure context; also used for top-level TProcCon* = object # procedure context; also used for top-level
# statements # statements
owner*: PSym # the symbol this context belongs to owner*: PSym # the symbol this context belongs to
resultSym*: PSym # the result symbol (if we are in a proc) resultSym*: PSym # the result symbol (if we are in a proc)
@ -36,12 +36,13 @@ type
# in standalone ``except`` and ``finally`` # in standalone ``except`` and ``finally``
next*: PProcCon # used for stacking procedure contexts next*: PProcCon # used for stacking procedure contexts
wasForwarded*: bool # whether the current proc has a separate header wasForwarded*: bool # whether the current proc has a separate header
bracketExpr*: PNode # current bracket expression (for ^ support)
TInstantiationPair* = object TInstantiationPair* = object
genericSym*: PSym genericSym*: PSym
inst*: PInstantiation inst*: PInstantiation
TExprFlag* = enum TExprFlag* = enum
efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType, efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType,
efAllowDestructor, efWantValue, efOperand, efNoSemCheck efAllowDestructor, efWantValue, efOperand, efNoSemCheck
TExprFlags* = set[TExprFlag] TExprFlags* = set[TExprFlag]
@ -57,7 +58,7 @@ type
# this is used so that generic instantiations # this is used so that generic instantiations
# can access private object fields # can access private object fields
instCounter*: int # to prevent endless instantiations instCounter*: int # to prevent endless instantiations
ambiguousSymbols*: IntSet # ids of all ambiguous symbols (cannot ambiguousSymbols*: IntSet # ids of all ambiguous symbols (cannot
# store this info in the syms themselves!) # store this info in the syms themselves!)
inTypeClass*: int # > 0 if we are in a user-defined type class inTypeClass*: int # > 0 if we are in a user-defined type class
@ -95,7 +96,7 @@ type
instDeepCopy*: proc (c: PContext; dc: PSym; t: PType; instDeepCopy*: proc (c: PContext; dc: PSym; t: PType;
info: TLineInfo): PSym {.nimcall.} info: TLineInfo): PSym {.nimcall.}
proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair = proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
result.genericSym = s result.genericSym = s
result.inst = inst result.inst = inst
@ -127,7 +128,7 @@ proc popOwner*()
var gOwners*: seq[PSym] = @[] var gOwners*: seq[PSym] = @[]
proc getCurrOwner(): PSym = proc getCurrOwner(): PSym =
# owner stack (used for initializing the # owner stack (used for initializing the
# owner field of syms) # owner field of syms)
# the documentation comment always gets # the documentation comment always gets
@ -135,19 +136,19 @@ proc getCurrOwner(): PSym =
# BUGFIX: global array is needed! # BUGFIX: global array is needed!
result = gOwners[high(gOwners)] result = gOwners[high(gOwners)]
proc pushOwner(owner: PSym) = proc pushOwner(owner: PSym) =
add(gOwners, owner) add(gOwners, owner)
proc popOwner() = proc popOwner() =
var length = len(gOwners) var length = len(gOwners)
if length > 0: setLen(gOwners, length - 1) if length > 0: setLen(gOwners, length - 1)
else: internalError("popOwner") else: internalError("popOwner")
proc lastOptionEntry(c: PContext): POptionEntry = proc lastOptionEntry(c: PContext): POptionEntry =
result = POptionEntry(c.optionStack.tail) result = POptionEntry(c.optionStack.tail)
proc pushProcCon*(c: PContext, owner: PSym) {.inline.} = proc pushProcCon*(c: PContext, owner: PSym) {.inline.} =
if owner == nil: if owner == nil:
internalError("owner is nil") internalError("owner is nil")
return return
var x: PProcCon var x: PProcCon
@ -158,7 +159,7 @@ proc pushProcCon*(c: PContext, owner: PSym) {.inline.} =
proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next
proc newOptionEntry(): POptionEntry = proc newOptionEntry(): POptionEntry =
new(result) new(result)
result.options = gOptions result.options = gOptions
result.defaultCC = ccDefault result.defaultCC = ccDefault
@ -182,8 +183,8 @@ proc newContext(module: PSym): PContext =
proc inclSym(sq: var TSymSeq, s: PSym) = proc inclSym(sq: var TSymSeq, s: PSym) =
var L = len(sq) var L = len(sq)
for i in countup(0, L - 1): for i in countup(0, L - 1):
if sq[i].id == s.id: return if sq[i].id == s.id: return
setLen(sq, L + 1) setLen(sq, L + 1)
sq[L] = s sq[L] = s
@ -193,20 +194,20 @@ proc addConverter*(c: PContext, conv: PSym) =
proc addPattern*(c: PContext, p: PSym) = proc addPattern*(c: PContext, p: PSym) =
inclSym(c.patterns, p) inclSym(c.patterns, p)
proc newLib(kind: TLibKind): PLib = proc newLib(kind: TLibKind): PLib =
new(result) new(result)
result.kind = kind #initObjectSet(result.syms) result.kind = kind #initObjectSet(result.syms)
proc addToLib(lib: PLib, sym: PSym) = proc addToLib(lib: PLib, sym: PSym) =
#if sym.annex != nil and not isGenericRoutine(sym): #if sym.annex != nil and not isGenericRoutine(sym):
# LocalError(sym.info, errInvalidPragma) # LocalError(sym.info, errInvalidPragma)
sym.annex = lib sym.annex = lib
proc makePtrType(c: PContext, baseType: PType): PType = proc makePtrType(c: PContext, baseType: PType): PType =
result = newTypeS(tyPtr, c) result = newTypeS(tyPtr, c)
addSonSkipIntLit(result, baseType.assertNotNil) addSonSkipIntLit(result, baseType.assertNotNil)
proc makeVarType(c: PContext, baseType: PType): PType = proc makeVarType(c: PContext, baseType: PType): PType =
result = newTypeS(tyVar, c) result = newTypeS(tyVar, c)
addSonSkipIntLit(result, baseType.assertNotNil) addSonSkipIntLit(result, baseType.assertNotNil)
@ -286,7 +287,7 @@ proc errorNode*(c: PContext, n: PNode): PNode =
result = newNodeI(nkEmpty, n.info) result = newNodeI(nkEmpty, n.info)
result.typ = errorType(c) result.typ = errorType(c)
proc fillTypeS(dest: PType, kind: TTypeKind, c: PContext) = proc fillTypeS(dest: PType, kind: TTypeKind, c: PContext) =
dest.kind = kind dest.kind = kind
dest.owner = getCurrOwner() dest.owner = getCurrOwner()
dest.size = - 1 dest.size = - 1
@ -311,13 +312,13 @@ proc illFormedAst*(n: PNode) =
proc illFormedAstLocal*(n: PNode) = proc illFormedAstLocal*(n: PNode) =
localError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments})) localError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
proc checkSonsLen*(n: PNode, length: int) = proc checkSonsLen*(n: PNode, length: int) =
if sonsLen(n) != length: illFormedAst(n) if sonsLen(n) != length: illFormedAst(n)
proc checkMinSonsLen*(n: PNode, length: int) = proc checkMinSonsLen*(n: PNode, length: int) =
if sonsLen(n) < length: illFormedAst(n) if sonsLen(n) < length: illFormedAst(n)
proc isTopLevel*(c: PContext): bool {.inline.} = proc isTopLevel*(c: PContext): bool {.inline.} =
result = c.currentScope.depthLevel <= 2 result = c.currentScope.depthLevel <= 2
proc experimentalMode*(c: PContext): bool {.inline.} = proc experimentalMode*(c: PContext): bool {.inline.} =

View file

@ -12,7 +12,7 @@
# included from sem.nim # included from sem.nim
# special marker values that indicates that we are # special marker values that indicates that we are
# 1) AnalyzingDestructor: currently analyzing the type for destructor # 1) AnalyzingDestructor: currently analyzing the type for destructor
# generation (needed for recursive types) # generation (needed for recursive types)
# 2) DestructorIsTrivial: completed the analysis before and determined # 2) DestructorIsTrivial: completed the analysis before and determined
# that the type has a trivial destructor # that the type has a trivial destructor
@ -41,7 +41,7 @@ proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
if t.kind != tyGenericBody: if t.kind != tyGenericBody:
localError(n.info, errDestructorNotGenericEnough) localError(n.info, errDestructorNotGenericEnough)
return return
t.destructor = s t.destructor = s
# automatically insert calls to base classes' destructors # automatically insert calls to base classes' destructors
if n.sons[bodyPos].kind != nkEmpty: if n.sons[bodyPos].kind != nkEmpty:
@ -71,17 +71,18 @@ proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode =
result.addSon(newNode(nkDotExpr, n.info, @[holder, n.sons[0]])) result.addSon(newNode(nkDotExpr, n.info, @[holder, n.sons[0]]))
for i in countup(1, n.len - 1): for i in countup(1, n.len - 1):
# of A, B: # of A, B:
var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2]) let ni = n[i]
var caseBranch = newNode(ni.kind, ni.info, ni.sons[0..ni.len-2])
let stmt = destroyFieldOrFields(c, n[i].lastSon, holder)
let stmt = destroyFieldOrFields(c, ni.lastSon, holder)
if stmt == nil: if stmt == nil:
caseBranch.addSon(newNode(nkStmtList, n[i].info, @[])) caseBranch.addSon(newNode(nkStmtList, ni.info, @[]))
else: else:
caseBranch.addSon(stmt) caseBranch.addSon(stmt)
nonTrivialFields += stmt.len nonTrivialFields += stmt.len
result.addSon(caseBranch) result.addSon(caseBranch)
# maybe no fields were destroyed? # maybe no fields were destroyed?
if nonTrivialFields == 0: if nonTrivialFields == 0:
result = nil result = nil
@ -107,7 +108,7 @@ proc destroyFieldOrFields(c: PContext, field: PNode, holder: PNode): PNode =
proc generateDestructor(c: PContext, t: PType): PNode = proc generateDestructor(c: PContext, t: PType): PNode =
## generate a destructor for a user-defined object or tuple type ## generate a destructor for a user-defined object or tuple type
## returns nil if the destructor turns out to be trivial ## returns nil if the destructor turns out to be trivial
# XXX: This may be true for some C-imported types such as # XXX: This may be true for some C-imported types such as
# Tposix_spawnattr # Tposix_spawnattr
if t.n == nil or t.n.sons == nil: return if t.n == nil or t.n.sons == nil: return
@ -120,13 +121,13 @@ proc generateDestructor(c: PContext, t: PType): PNode =
proc instantiateDestructor(c: PContext, typ: PType): PType = proc instantiateDestructor(c: PContext, typ: PType): PType =
# returns nil if a variable of type `typ` doesn't require a # returns nil if a variable of type `typ` doesn't require a
# destructor. Otherwise, returns the type, which holds the # destructor. Otherwise, returns the type, which holds the
# destructor that must be used for the varialbe. # destructor that must be used for the varialbe.
# The destructor is either user-defined or automatically # The destructor is either user-defined or automatically
# generated by the compiler in a member-wise fashion. # generated by the compiler in a member-wise fashion.
var t = skipTypes(typ, {tyConst, tyMutable}).skipGenericAlias var t = skipTypes(typ, {tyConst, tyMutable}).skipGenericAlias
let typeHoldingUserDefinition = if t.kind == tyGenericInst: t.base else: t let typeHoldingUserDefinition = if t.kind == tyGenericInst: t.base else: t
if typeHoldingUserDefinition.destructor != nil: if typeHoldingUserDefinition.destructor != nil:
# XXX: This is not entirely correct for recursive types, but we need # XXX: This is not entirely correct for recursive types, but we need
# it temporarily to hide the "destroy is already defined" problem # it temporarily to hide the "destroy is already defined" problem
@ -135,7 +136,7 @@ proc instantiateDestructor(c: PContext, typ: PType): PType =
return typeHoldingUserDefinition return typeHoldingUserDefinition
else: else:
return nil return nil
t = t.skipTypes({tyGenericInst}) t = t.skipTypes({tyGenericInst})
case t.kind case t.kind
of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs: of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs:
@ -200,16 +201,16 @@ proc insertDestructors(c: PContext,
varId = varSection[j][0] varId = varSection[j][0]
varTyp = varId.sym.typ varTyp = varId.sym.typ
info = varId.info info = varId.info
if varTyp == nil or sfGlobal in varId.sym.flags: continue if varTyp == nil or sfGlobal in varId.sym.flags: continue
let destructableT = instantiateDestructor(c, varTyp) let destructableT = instantiateDestructor(c, varTyp)
if destructableT != nil: if destructableT != nil:
var tryStmt = newNodeI(nkTryStmt, info) var tryStmt = newNodeI(nkTryStmt, info)
if j < totalVars - 1: if j < totalVars - 1:
var remainingVars = newNodeI(varSection.kind, info) var remainingVars = newNodeI(varSection.kind, info)
remainingVars.sons = varSection.sons[(j+1)..(-1)] remainingVars.sons = varSection.sons[(j+1)..varSection.len-1]
let (outer, inner) = insertDestructors(c, remainingVars) let (outer, inner) = insertDestructors(c, remainingVars)
if outer != nil: if outer != nil:
tryStmt.addSon(outer) tryStmt.addSon(outer)
@ -221,7 +222,7 @@ proc insertDestructors(c: PContext,
else: else:
result.inner = newNodeI(nkStmtList, info) result.inner = newNodeI(nkStmtList, info)
tryStmt.addSon(result.inner) tryStmt.addSon(result.inner)
tryStmt.addSon( tryStmt.addSon(
newNode(nkFinally, info, @[ newNode(nkFinally, info, @[
semStmt(c, newNode(nkCall, info, @[ semStmt(c, newNode(nkCall, info, @[

View file

@ -1133,19 +1133,20 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
## returns nil if not a built-in subscript operator; also called for the ## returns nil if not a built-in subscript operator; also called for the
## checking of assignments ## checking of assignments
if sonsLen(n) == 1: if sonsLen(n) == 1:
var x = semDeref(c, n) let x = semDeref(c, n)
if x == nil: return nil if x == nil: return nil
result = newNodeIT(nkDerefExpr, x.info, x.typ) result = newNodeIT(nkDerefExpr, x.info, x.typ)
result.add(x[0]) result.add(x[0])
return return
checkMinSonsLen(n, 2) checkMinSonsLen(n, 2)
n.sons[0] = semExprWithType(c, n.sons[0]) n.sons[0] = semExprWithType(c, n.sons[0])
var arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyPtr, tyRef}) let arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyPtr, tyRef})
case arr.kind case arr.kind
of tyArray, tyOpenArray, tyVarargs, tyArrayConstr, tySequence, tyString, of tyArray, tyOpenArray, tyVarargs, tyArrayConstr, tySequence, tyString,
tyCString: tyCString:
if n.len != 2: return nil if n.len != 2: return nil
n.sons[0] = makeDeref(n.sons[0]) n.sons[0] = makeDeref(n.sons[0])
c.p.bracketExpr = n.sons[0]
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
n.sons[i] = semExprWithType(c, n.sons[i], n.sons[i] = semExprWithType(c, n.sons[i],
flags*{efInTypeof, efDetermineType}) flags*{efInTypeof, efDetermineType})
@ -1166,6 +1167,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
of tyTuple: of tyTuple:
checkSonsLen(n, 2) checkSonsLen(n, 2)
n.sons[0] = makeDeref(n.sons[0]) n.sons[0] = makeDeref(n.sons[0])
c.p.bracketExpr = n.sons[0]
# [] operator for tuples requires constant expression: # [] operator for tuples requires constant expression:
n.sons[1] = semConstExpr(c, n.sons[1]) n.sons[1] = semConstExpr(c, n.sons[1])
if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal}).kind in if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal}).kind in
@ -1176,13 +1178,16 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
else: else:
localError(n.info, errIndexTypesDoNotMatch) localError(n.info, errIndexTypesDoNotMatch)
result = n result = n
else: discard else:
c.p.bracketExpr = n.sons[0]
proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
let oldBracketExpr = c.p.bracketExpr
result = semSubscript(c, n, flags) result = semSubscript(c, n, flags)
if result == nil: if result == nil:
# overloaded [] operator: # overloaded [] operator:
result = semExpr(c, buildOverloadedSubscripts(n, getIdent"[]")) result = semExpr(c, buildOverloadedSubscripts(n, getIdent"[]"))
c.p.bracketExpr = oldBracketExpr
proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode = proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
var id = considerQuotedIdent(a[1]) var id = considerQuotedIdent(a[1])
@ -1249,11 +1254,15 @@ proc semAsgn(c: PContext, n: PNode): PNode =
of nkBracketExpr: of nkBracketExpr:
# a[i] = x # a[i] = x
# --> `[]=`(a, i, x) # --> `[]=`(a, i, x)
let oldBracketExpr = c.p.bracketExpr
a = semSubscript(c, a, {efLValue}) a = semSubscript(c, a, {efLValue})
if a == nil: if a == nil:
result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=") result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=")
add(result, n[1]) add(result, n[1])
return semExprNoType(c, result) result = semExprNoType(c, result)
c.p.bracketExpr = oldBracketExpr
return result
c.p.bracketExpr = oldBracketExpr
of nkCurlyExpr: of nkCurlyExpr:
# a{i} = x --> `{}=`(a, i, x) # a{i} = x --> `{}=`(a, i, x)
result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=") result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=")
@ -1895,7 +1904,8 @@ proc checkInitialized(n: PNode, ids: IntSet, info: TLineInfo) =
of nkOfBranch, nkElse: checkInitialized(lastSon(n.sons[i]), ids, info) of nkOfBranch, nkElse: checkInitialized(lastSon(n.sons[i]), ids, info)
else: internalError(info, "checkInitialized") else: internalError(info, "checkInitialized")
of nkSym: of nkSym:
if tfNeedsInit in n.sym.typ.flags and n.sym.name.id notin ids: if {tfNotNil, tfNeedsInit} * n.sym.typ.flags != {} and
n.sym.name.id notin ids:
message(info, errGenerated, "field not initialized: " & n.sym.name.s) message(info, errGenerated, "field not initialized: " & n.sym.name.s)
else: internalError(info, "checkInitialized") else: internalError(info, "checkInitialized")

View file

@ -10,8 +10,8 @@
# this module folds constants; used by semantic checking phase # this module folds constants; used by semantic checking phase
# and evaluation phase # and evaluation phase
import import
strutils, lists, options, ast, astalgo, trees, treetab, nimsets, times, strutils, lists, options, ast, astalgo, trees, treetab, nimsets, times,
nversion, platform, math, msgs, os, condsyms, idents, renderer, types, nversion, platform, math, msgs, os, condsyms, idents, renderer, types,
commands, magicsys, saturate commands, magicsys, saturate
@ -41,7 +41,7 @@ proc newIntNodeT(intVal: BiggestInt, n: PNode): PNode =
result.typ = n.typ result.typ = n.typ
result.info = n.info result.info = n.info
proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode = proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode =
result = newFloatNode(nkFloatLit, floatVal) result = newFloatNode(nkFloatLit, floatVal)
if skipTypes(n.typ, abstractVarRange).kind == tyFloat: if skipTypes(n.typ, abstractVarRange).kind == tyFloat:
result.typ = getFloatLitType(result) result.typ = getFloatLitType(result)
@ -49,27 +49,27 @@ proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode =
result.typ = n.typ result.typ = n.typ
result.info = n.info result.info = n.info
proc newStrNodeT(strVal: string, n: PNode): PNode = proc newStrNodeT(strVal: string, n: PNode): PNode =
result = newStrNode(nkStrLit, strVal) result = newStrNode(nkStrLit, strVal)
result.typ = n.typ result.typ = n.typ
result.info = n.info result.info = n.info
proc ordinalValToString*(a: PNode): string = proc ordinalValToString*(a: PNode): string =
# because $ has the param ordinal[T], `a` is not necessarily an enum, but an # because $ has the param ordinal[T], `a` is not necessarily an enum, but an
# ordinal # ordinal
var x = getInt(a) var x = getInt(a)
var t = skipTypes(a.typ, abstractRange) var t = skipTypes(a.typ, abstractRange)
case t.kind case t.kind
of tyChar: of tyChar:
result = $chr(int(x) and 0xff) result = $chr(int(x) and 0xff)
of tyEnum: of tyEnum:
var n = t.n var n = t.n
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if n.sons[i].kind != nkSym: internalError(a.info, "ordinalValToString") if n.sons[i].kind != nkSym: internalError(a.info, "ordinalValToString")
var field = n.sons[i].sym var field = n.sons[i].sym
if field.position == x: if field.position == x:
if field.ast == nil: if field.ast == nil:
return field.name.s return field.name.s
else: else:
return field.ast.strVal return field.ast.strVal
@ -112,7 +112,7 @@ proc pickMaxInt(n: PNode): BiggestInt =
else: else:
internalError(n.info, "pickMaxInt") internalError(n.info, "pickMaxInt")
proc makeRange(typ: PType, first, last: BiggestInt): PType = proc makeRange(typ: PType, first, last: BiggestInt): PType =
let minA = min(first, last) let minA = min(first, last)
let maxA = max(first, last) let maxA = max(first, last)
let lowerNode = newIntNode(nkIntLit, minA) let lowerNode = newIntNode(nkIntLit, minA)
@ -138,7 +138,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
# Nimrod requires interval arithmetic for ``range`` types. Lots of tedious # Nimrod requires interval arithmetic for ``range`` types. Lots of tedious
# work but the feature is very nice for reducing explicit conversions. # work but the feature is very nice for reducing explicit conversions.
result = n.typ result = n.typ
template commutativeOp(opr: expr) {.immediate.} = template commutativeOp(opr: expr) {.immediate.} =
let a = n.sons[1] let a = n.sons[1]
let b = n.sons[2] let b = n.sons[2]
@ -146,7 +146,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
result = makeRange(pickIntRange(a.typ, b.typ), result = makeRange(pickIntRange(a.typ, b.typ),
opr(pickMinInt(a), pickMinInt(b)), opr(pickMinInt(a), pickMinInt(b)),
opr(pickMaxInt(a), pickMaxInt(b))) opr(pickMaxInt(a), pickMaxInt(b)))
template binaryOp(opr: expr) {.immediate.} = template binaryOp(opr: expr) {.immediate.} =
let a = n.sons[1] let a = n.sons[1]
let b = n.sons[2] let b = n.sons[2]
@ -154,7 +154,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
result = makeRange(a.typ, result = makeRange(a.typ,
opr(pickMinInt(a), pickMinInt(b)), opr(pickMinInt(a), pickMinInt(b)),
opr(pickMaxInt(a), pickMaxInt(b))) opr(pickMaxInt(a), pickMaxInt(b)))
case m case m
of mUnaryMinusI, mUnaryMinusI64: of mUnaryMinusI, mUnaryMinusI64:
let a = n.sons[1].typ let a = n.sons[1].typ
@ -231,7 +231,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
of mMaxI, mMaxI64: of mMaxI, mMaxI64:
commutativeOp(max) commutativeOp(max)
else: discard else: discard
discard """ discard """
mShlI, mShlI64, mShlI, mShlI64,
mShrI, mShrI64, mAddF64, mSubF64, mMulF64, mDivF64, mMaxF64, mMinF64 mShrI, mShrI64, mAddF64, mSubF64, mMulF64, mDivF64, mMaxF64, mMinF64
@ -242,7 +242,7 @@ proc evalIs(n, a: PNode): PNode =
internalAssert a.kind == nkSym and a.sym.kind == skType internalAssert a.kind == nkSym and a.sym.kind == skType
internalAssert n.sonsLen == 3 and internalAssert n.sonsLen == 3 and
n[2].kind in {nkStrLit..nkTripleStrLit, nkType} n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
let t1 = a.sym.typ let t1 = a.sym.typ
if n[2].kind in {nkStrLit..nkTripleStrLit}: if n[2].kind in {nkStrLit..nkTripleStrLit}:
@ -250,12 +250,12 @@ proc evalIs(n, a: PNode): PNode =
of "closure": of "closure":
let t = skipTypes(t1, abstractRange) let t = skipTypes(t1, abstractRange)
result = newIntNode(nkIntLit, ord(t.kind == tyProc and result = newIntNode(nkIntLit, ord(t.kind == tyProc and
t.callConv == ccClosure and t.callConv == ccClosure and
tfIterator notin t.flags)) tfIterator notin t.flags))
of "iterator": of "iterator":
let t = skipTypes(t1, abstractRange) let t = skipTypes(t1, abstractRange)
result = newIntNode(nkIntLit, ord(t.kind == tyProc and result = newIntNode(nkIntLit, ord(t.kind == tyProc and
t.callConv == ccClosure and t.callConv == ccClosure and
tfIterator in t.flags)) tfIterator in t.flags))
else: discard else: discard
else: else:
@ -265,7 +265,7 @@ proc evalIs(n, a: PNode): PNode =
result = newIntNode(nkIntLit, ord(match)) result = newIntNode(nkIntLit, ord(match))
result.typ = n.typ result.typ = n.typ
proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
# b and c may be nil # b and c may be nil
result = nil result = nil
case m case m
@ -280,14 +280,14 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mLengthArray: result = newIntNodeT(lengthOrd(a.typ), n) of mLengthArray: result = newIntNodeT(lengthOrd(a.typ), n)
of mLengthSeq, mLengthOpenArray: result = newIntNodeT(sonsLen(a), n) # BUGFIX of mLengthSeq, mLengthOpenArray: result = newIntNodeT(sonsLen(a), n) # BUGFIX
of mUnaryPlusI, mUnaryPlusI64, mUnaryPlusF64: result = a # throw `+` away of mUnaryPlusI, mUnaryPlusI64, mUnaryPlusF64: result = a # throw `+` away
of mToFloat, mToBiggestFloat: of mToFloat, mToBiggestFloat:
result = newFloatNodeT(toFloat(int(getInt(a))), n) result = newFloatNodeT(toFloat(int(getInt(a))), n)
of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n) of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n)
of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n) of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n)
of mAbsI, mAbsI64: of mAbsI, mAbsI64:
if getInt(a) >= 0: result = a if getInt(a) >= 0: result = a
else: result = newIntNodeT(- getInt(a), n) else: result = newIntNodeT(- getInt(a), n)
of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64: of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64:
# byte(-128) = 1...1..1000_0000'64 --> 0...0..1000_0000'64 # byte(-128) = 1...1..1000_0000'64 --> 0...0..1000_0000'64
result = newIntNodeT(getInt(a) and (`shl`(1, getSize(a.typ) * 8) - 1), n) result = newIntNodeT(getInt(a) and (`shl`(1, getSize(a.typ) * 8) - 1), n)
of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n) of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n)
@ -299,21 +299,21 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mAddI, mAddI64: result = newIntNodeT(getInt(a) + getInt(b), n) of mAddI, mAddI64: result = newIntNodeT(getInt(a) + getInt(b), n)
of mSubI, mSubI64: result = newIntNodeT(getInt(a) - getInt(b), n) of mSubI, mSubI64: result = newIntNodeT(getInt(a) - getInt(b), n)
of mMulI, mMulI64: result = newIntNodeT(getInt(a) * getInt(b), n) of mMulI, mMulI64: result = newIntNodeT(getInt(a) * getInt(b), n)
of mMinI, mMinI64: of mMinI, mMinI64:
if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n) if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n)
else: result = newIntNodeT(getInt(a), n) else: result = newIntNodeT(getInt(a), n)
of mMaxI, mMaxI64: of mMaxI, mMaxI64:
if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n) if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n)
else: result = newIntNodeT(getInt(b), n) else: result = newIntNodeT(getInt(b), n)
of mShlI, mShlI64: of mShlI, mShlI64:
case skipTypes(n.typ, abstractRange).kind case skipTypes(n.typ, abstractRange).kind
of tyInt8: result = newIntNodeT(int8(getInt(a)) shl int8(getInt(b)), n) of tyInt8: result = newIntNodeT(int8(getInt(a)) shl int8(getInt(b)), n)
of tyInt16: result = newIntNodeT(int16(getInt(a)) shl int16(getInt(b)), n) of tyInt16: result = newIntNodeT(int16(getInt(a)) shl int16(getInt(b)), n)
of tyInt32: result = newIntNodeT(int32(getInt(a)) shl int32(getInt(b)), n) of tyInt32: result = newIntNodeT(int32(getInt(a)) shl int32(getInt(b)), n)
of tyInt64, tyInt, tyUInt..tyUInt64: of tyInt64, tyInt, tyUInt..tyUInt64:
result = newIntNodeT(`shl`(getInt(a), getInt(b)), n) result = newIntNodeT(`shl`(getInt(a), getInt(b)), n)
else: internalError(n.info, "constant folding for shl") else: internalError(n.info, "constant folding for shl")
of mShrI, mShrI64: of mShrI, mShrI64:
case skipTypes(n.typ, abstractRange).kind case skipTypes(n.typ, abstractRange).kind
of tyInt8: result = newIntNodeT(int8(getInt(a)) shr int8(getInt(b)), n) of tyInt8: result = newIntNodeT(int8(getInt(a)) shr int8(getInt(b)), n)
of tyInt16: result = newIntNodeT(int16(getInt(a)) shr int16(getInt(b)), n) of tyInt16: result = newIntNodeT(int16(getInt(a)) shr int16(getInt(b)), n)
@ -332,34 +332,34 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n) of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n)
of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n) of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n)
of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n) of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n)
of mDivF64: of mDivF64:
if getFloat(b) == 0.0: if getFloat(b) == 0.0:
if getFloat(a) == 0.0: result = newFloatNodeT(NaN, n) if getFloat(a) == 0.0: result = newFloatNodeT(NaN, n)
else: result = newFloatNodeT(Inf, n) else: result = newFloatNodeT(Inf, n)
else: else:
result = newFloatNodeT(getFloat(a) / getFloat(b), n) result = newFloatNodeT(getFloat(a) / getFloat(b), n)
of mMaxF64: of mMaxF64:
if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(a), n) if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(a), n)
else: result = newFloatNodeT(getFloat(b), n) else: result = newFloatNodeT(getFloat(b), n)
of mMinF64: of mMinF64:
if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(b), n) if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(b), n)
else: result = newFloatNodeT(getFloat(a), n) else: result = newFloatNodeT(getFloat(a), n)
of mIsNil: result = newIntNodeT(ord(a.kind == nkNilLit), n) of mIsNil: result = newIntNodeT(ord(a.kind == nkNilLit), n)
of mLtI, mLtI64, mLtB, mLtEnum, mLtCh: of mLtI, mLtI64, mLtB, mLtEnum, mLtCh:
result = newIntNodeT(ord(getOrdValue(a) < getOrdValue(b)), n) result = newIntNodeT(ord(getOrdValue(a) < getOrdValue(b)), n)
of mLeI, mLeI64, mLeB, mLeEnum, mLeCh: of mLeI, mLeI64, mLeB, mLeEnum, mLeCh:
result = newIntNodeT(ord(getOrdValue(a) <= getOrdValue(b)), n) result = newIntNodeT(ord(getOrdValue(a) <= getOrdValue(b)), n)
of mEqI, mEqI64, mEqB, mEqEnum, mEqCh: of mEqI, mEqI64, mEqB, mEqEnum, mEqCh:
result = newIntNodeT(ord(getOrdValue(a) == getOrdValue(b)), n) result = newIntNodeT(ord(getOrdValue(a) == getOrdValue(b)), n)
of mLtF64: result = newIntNodeT(ord(getFloat(a) < getFloat(b)), n) of mLtF64: result = newIntNodeT(ord(getFloat(a) < getFloat(b)), n)
of mLeF64: result = newIntNodeT(ord(getFloat(a) <= getFloat(b)), n) of mLeF64: result = newIntNodeT(ord(getFloat(a) <= getFloat(b)), n)
of mEqF64: result = newIntNodeT(ord(getFloat(a) == getFloat(b)), n) of mEqF64: result = newIntNodeT(ord(getFloat(a) == getFloat(b)), n)
of mLtStr: result = newIntNodeT(ord(getStr(a) < getStr(b)), n) of mLtStr: result = newIntNodeT(ord(getStr(a) < getStr(b)), n)
of mLeStr: result = newIntNodeT(ord(getStr(a) <= getStr(b)), n) of mLeStr: result = newIntNodeT(ord(getStr(a) <= getStr(b)), n)
of mEqStr: result = newIntNodeT(ord(getStr(a) == getStr(b)), n) of mEqStr: result = newIntNodeT(ord(getStr(a) == getStr(b)), n)
of mLtU, mLtU64: of mLtU, mLtU64:
result = newIntNodeT(ord(`<%`(getOrdValue(a), getOrdValue(b))), n) result = newIntNodeT(ord(`<%`(getOrdValue(a), getOrdValue(b))), n)
of mLeU, mLeU64: of mLeU, mLeU64:
result = newIntNodeT(ord(`<=%`(getOrdValue(a), getOrdValue(b))), n) result = newIntNodeT(ord(`<=%`(getOrdValue(a), getOrdValue(b))), n)
of mBitandI, mBitandI64, mAnd: result = newIntNodeT(a.getInt and b.getInt, n) of mBitandI, mBitandI64, mAnd: result = newIntNodeT(a.getInt and b.getInt, n)
of mBitorI, mBitorI64, mOr: result = newIntNodeT(getInt(a) or getInt(b), n) of mBitorI, mBitorI64, mOr: result = newIntNodeT(getInt(a) or getInt(b), n)
@ -377,18 +377,18 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
result = newIntNodeT(`/%`(getInt(a), y), n) result = newIntNodeT(`/%`(getInt(a), y), n)
of mLeSet: result = newIntNodeT(ord(containsSets(a, b)), n) of mLeSet: result = newIntNodeT(ord(containsSets(a, b)), n)
of mEqSet: result = newIntNodeT(ord(equalSets(a, b)), n) of mEqSet: result = newIntNodeT(ord(equalSets(a, b)), n)
of mLtSet: of mLtSet:
result = newIntNodeT(ord(containsSets(a, b) and not equalSets(a, b)), n) result = newIntNodeT(ord(containsSets(a, b) and not equalSets(a, b)), n)
of mMulSet: of mMulSet:
result = nimsets.intersectSets(a, b) result = nimsets.intersectSets(a, b)
result.info = n.info result.info = n.info
of mPlusSet: of mPlusSet:
result = nimsets.unionSets(a, b) result = nimsets.unionSets(a, b)
result.info = n.info result.info = n.info
of mMinusSet: of mMinusSet:
result = nimsets.diffSets(a, b) result = nimsets.diffSets(a, b)
result.info = n.info result.info = n.info
of mSymDiffSet: of mSymDiffSet:
result = nimsets.symdiffSets(a, b) result = nimsets.symdiffSets(a, b)
result.info = n.info result.info = n.info
of mConStrStr: result = newStrNodeT(getStrOrChar(a) & getStrOrChar(b), n) of mConStrStr: result = newStrNodeT(getStrOrChar(a) & getStrOrChar(b), n)
@ -397,104 +397,104 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
# BUGFIX: we cannot eval mRepr here for reasons that I forgot. # BUGFIX: we cannot eval mRepr here for reasons that I forgot.
discard discard
of mIntToStr, mInt64ToStr: result = newStrNodeT($(getOrdValue(a)), n) of mIntToStr, mInt64ToStr: result = newStrNodeT($(getOrdValue(a)), n)
of mBoolToStr: of mBoolToStr:
if getOrdValue(a) == 0: result = newStrNodeT("false", n) if getOrdValue(a) == 0: result = newStrNodeT("false", n)
else: result = newStrNodeT("true", n) else: result = newStrNodeT("true", n)
of mCopyStr: result = newStrNodeT(substr(getStr(a), int(getOrdValue(b))), n) of mCopyStr: result = newStrNodeT(substr(getStr(a), int(getOrdValue(b))), n)
of mCopyStrLast: of mCopyStrLast:
result = newStrNodeT(substr(getStr(a), int(getOrdValue(b)), result = newStrNodeT(substr(getStr(a), int(getOrdValue(b)),
int(getOrdValue(c))), n) int(getOrdValue(c))), n)
of mFloatToStr: result = newStrNodeT($getFloat(a), n) of mFloatToStr: result = newStrNodeT($getFloat(a), n)
of mCStrToStr, mCharToStr: result = newStrNodeT(getStrOrChar(a), n) of mCStrToStr, mCharToStr: result = newStrNodeT(getStrOrChar(a), n)
of mStrToStr: result = a of mStrToStr: result = a
of mEnumToStr: result = newStrNodeT(ordinalValToString(a), n) of mEnumToStr: result = newStrNodeT(ordinalValToString(a), n)
of mArrToSeq: of mArrToSeq:
result = copyTree(a) result = copyTree(a)
result.typ = n.typ result.typ = n.typ
of mCompileOption: of mCompileOption:
result = newIntNodeT(ord(commands.testCompileOption(a.getStr, n.info)), n) result = newIntNodeT(ord(commands.testCompileOption(a.getStr, n.info)), n)
of mCompileOptionArg: of mCompileOptionArg:
result = newIntNodeT(ord( result = newIntNodeT(ord(
testCompileOptionArg(getStr(a), getStr(b), n.info)), n) testCompileOptionArg(getStr(a), getStr(b), n.info)), n)
of mNewString, mNewStringOfCap, of mNewString, mNewStringOfCap,
mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh, mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh,
mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq,
mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, mDotDot,
mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn, mParallel: mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn, mParallel:
discard discard
else: internalError(a.info, "evalOp(" & $m & ')') else: internalError(a.info, "evalOp(" & $m & ')')
proc getConstIfExpr(c: PSym, n: PNode): PNode = proc getConstIfExpr(c: PSym, n: PNode): PNode =
result = nil result = nil
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var it = n.sons[i] var it = n.sons[i]
if it.len == 2: if it.len == 2:
var e = getConstExpr(c, it.sons[0]) var e = getConstExpr(c, it.sons[0])
if e == nil: return nil if e == nil: return nil
if getOrdValue(e) != 0: if getOrdValue(e) != 0:
if result == nil: if result == nil:
result = getConstExpr(c, it.sons[1]) result = getConstExpr(c, it.sons[1])
if result == nil: return if result == nil: return
elif it.len == 1: elif it.len == 1:
if result == nil: result = getConstExpr(c, it.sons[0]) if result == nil: result = getConstExpr(c, it.sons[0])
else: internalError(it.info, "getConstIfExpr()") else: internalError(it.info, "getConstIfExpr()")
proc partialAndExpr(c: PSym, n: PNode): PNode = proc partialAndExpr(c: PSym, n: PNode): PNode =
# partial evaluation # partial evaluation
result = n result = n
var a = getConstExpr(c, n.sons[1]) var a = getConstExpr(c, n.sons[1])
var b = getConstExpr(c, n.sons[2]) var b = getConstExpr(c, n.sons[2])
if a != nil: if a != nil:
if getInt(a) == 0: result = a if getInt(a) == 0: result = a
elif b != nil: result = b elif b != nil: result = b
else: result = n.sons[2] else: result = n.sons[2]
elif b != nil: elif b != nil:
if getInt(b) == 0: result = b if getInt(b) == 0: result = b
else: result = n.sons[1] else: result = n.sons[1]
proc partialOrExpr(c: PSym, n: PNode): PNode = proc partialOrExpr(c: PSym, n: PNode): PNode =
# partial evaluation # partial evaluation
result = n result = n
var a = getConstExpr(c, n.sons[1]) var a = getConstExpr(c, n.sons[1])
var b = getConstExpr(c, n.sons[2]) var b = getConstExpr(c, n.sons[2])
if a != nil: if a != nil:
if getInt(a) != 0: result = a if getInt(a) != 0: result = a
elif b != nil: result = b elif b != nil: result = b
else: result = n.sons[2] else: result = n.sons[2]
elif b != nil: elif b != nil:
if getInt(b) != 0: result = b if getInt(b) != 0: result = b
else: result = n.sons[1] else: result = n.sons[1]
proc leValueConv(a, b: PNode): bool = proc leValueConv(a, b: PNode): bool =
result = false result = false
case a.kind case a.kind
of nkCharLit..nkUInt64Lit: of nkCharLit..nkUInt64Lit:
case b.kind case b.kind
of nkCharLit..nkUInt64Lit: result = a.intVal <= b.intVal of nkCharLit..nkUInt64Lit: result = a.intVal <= b.intVal
of nkFloatLit..nkFloat128Lit: result = a.intVal <= round(b.floatVal) of nkFloatLit..nkFloat128Lit: result = a.intVal <= round(b.floatVal)
else: internalError(a.info, "leValueConv") else: internalError(a.info, "leValueConv")
of nkFloatLit..nkFloat128Lit: of nkFloatLit..nkFloat128Lit:
case b.kind case b.kind
of nkFloatLit..nkFloat128Lit: result = a.floatVal <= b.floatVal of nkFloatLit..nkFloat128Lit: result = a.floatVal <= b.floatVal
of nkCharLit..nkUInt64Lit: result = a.floatVal <= toFloat(int(b.intVal)) of nkCharLit..nkUInt64Lit: result = a.floatVal <= toFloat(int(b.intVal))
else: internalError(a.info, "leValueConv") else: internalError(a.info, "leValueConv")
else: internalError(a.info, "leValueConv") else: internalError(a.info, "leValueConv")
proc magicCall(m: PSym, n: PNode): PNode = proc magicCall(m: PSym, n: PNode): PNode =
if sonsLen(n) <= 1: return if sonsLen(n) <= 1: return
var s = n.sons[0].sym var s = n.sons[0].sym
var a = getConstExpr(m, n.sons[1]) var a = getConstExpr(m, n.sons[1])
var b, c: PNode var b, c: PNode
if a == nil: return if a == nil: return
if sonsLen(n) > 2: if sonsLen(n) > 2:
b = getConstExpr(m, n.sons[2]) b = getConstExpr(m, n.sons[2])
if b == nil: return if b == nil: return
if sonsLen(n) > 3: if sonsLen(n) > 3:
c = getConstExpr(m, n.sons[3]) c = getConstExpr(m, n.sons[3])
if c == nil: return if c == nil: return
result = evalOp(s.magic, n, a, b, c) result = evalOp(s.magic, n, a, b, c)
proc getAppType(n: PNode): PNode = proc getAppType(n: PNode): PNode =
if gGlobalOptions.contains(optGenDynLib): if gGlobalOptions.contains(optGenDynLib):
result = newStrNodeT("lib", n) result = newStrNodeT("lib", n)
@ -510,48 +510,48 @@ proc rangeCheck(n: PNode, value: BiggestInt) =
localError(n.info, errGenerated, "cannot convert " & $value & localError(n.info, errGenerated, "cannot convert " & $value &
" to " & typeToString(n.typ)) " to " & typeToString(n.typ))
proc foldConv*(n, a: PNode; check = false): PNode = proc foldConv*(n, a: PNode; check = false): PNode =
# XXX range checks? # XXX range checks?
case skipTypes(n.typ, abstractRange).kind case skipTypes(n.typ, abstractRange).kind
of tyInt..tyInt64: of tyInt..tyInt64:
case skipTypes(a.typ, abstractRange).kind case skipTypes(a.typ, abstractRange).kind
of tyFloat..tyFloat64: of tyFloat..tyFloat64:
result = newIntNodeT(int(getFloat(a)), n) result = newIntNodeT(int(getFloat(a)), n)
of tyChar: result = newIntNodeT(getOrdValue(a), n) of tyChar: result = newIntNodeT(getOrdValue(a), n)
else: else:
result = a result = a
result.typ = n.typ result.typ = n.typ
if check: rangeCheck(n, result.intVal) if check: rangeCheck(n, result.intVal)
of tyFloat..tyFloat64: of tyFloat..tyFloat64:
case skipTypes(a.typ, abstractRange).kind case skipTypes(a.typ, abstractRange).kind
of tyInt..tyInt64, tyEnum, tyBool, tyChar: of tyInt..tyInt64, tyEnum, tyBool, tyChar:
result = newFloatNodeT(toFloat(int(getOrdValue(a))), n) result = newFloatNodeT(toFloat(int(getOrdValue(a))), n)
else: else:
result = a result = a
result.typ = n.typ result.typ = n.typ
of tyOpenArray, tyVarargs, tyProc: of tyOpenArray, tyVarargs, tyProc:
discard discard
else: else:
result = a result = a
result.typ = n.typ result.typ = n.typ
proc getArrayConstr(m: PSym, n: PNode): PNode = proc getArrayConstr(m: PSym, n: PNode): PNode =
if n.kind == nkBracket: if n.kind == nkBracket:
result = n result = n
else: else:
result = getConstExpr(m, n) result = getConstExpr(m, n)
if result == nil: result = n if result == nil: result = n
proc foldArrayAccess(m: PSym, n: PNode): PNode = proc foldArrayAccess(m: PSym, n: PNode): PNode =
var x = getConstExpr(m, n.sons[0]) var x = getConstExpr(m, n.sons[0])
if x == nil or x.typ.skipTypes({tyGenericInst}).kind == tyTypeDesc: return if x == nil or x.typ.skipTypes({tyGenericInst}).kind == tyTypeDesc: return
var y = getConstExpr(m, n.sons[1]) var y = getConstExpr(m, n.sons[1])
if y == nil: return if y == nil: return
var idx = getOrdValue(y) var idx = getOrdValue(y)
case x.kind case x.kind
of nkPar: of nkPar:
if idx >= 0 and idx < sonsLen(x): if idx >= 0 and idx < sonsLen(x):
result = x.sons[int(idx)] result = x.sons[int(idx)]
if result.kind == nkExprColonExpr: result = result.sons[1] if result.kind == nkExprColonExpr: result = result.sons[1]
@ -563,14 +563,14 @@ proc foldArrayAccess(m: PSym, n: PNode): PNode =
else: localError(n.info, errIndexOutOfBounds) else: localError(n.info, errIndexOutOfBounds)
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
result = newNodeIT(nkCharLit, x.info, n.typ) result = newNodeIT(nkCharLit, x.info, n.typ)
if idx >= 0 and idx < len(x.strVal): if idx >= 0 and idx < len(x.strVal):
result.intVal = ord(x.strVal[int(idx)]) result.intVal = ord(x.strVal[int(idx)])
elif idx == len(x.strVal): elif idx == len(x.strVal):
discard discard
else: else:
localError(n.info, errIndexOutOfBounds) localError(n.info, errIndexOutOfBounds)
else: discard else: discard
proc foldFieldAccess(m: PSym, n: PNode): PNode = proc foldFieldAccess(m: PSym, n: PNode): PNode =
# a real field access; proc calls have already been transformed # a real field access; proc calls have already been transformed
var x = getConstExpr(m, n.sons[0]) var x = getConstExpr(m, n.sons[0])
@ -584,15 +584,15 @@ proc foldFieldAccess(m: PSym, n: PNode): PNode =
result = x.sons[field.position] result = x.sons[field.position]
if result.kind == nkExprColonExpr: result = result.sons[1] if result.kind == nkExprColonExpr: result = result.sons[1]
return return
if it.sons[0].sym.name.id == field.name.id: if it.sons[0].sym.name.id == field.name.id:
result = x.sons[i].sons[1] result = x.sons[i].sons[1]
return return
localError(n.info, errFieldXNotFound, field.name.s) localError(n.info, errFieldXNotFound, field.name.s)
proc foldConStrStr(m: PSym, n: PNode): PNode = proc foldConStrStr(m: PSym, n: PNode): PNode =
result = newNodeIT(nkStrLit, n.info, n.typ) result = newNodeIT(nkStrLit, n.info, n.typ)
result.strVal = "" result.strVal = ""
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
let a = getConstExpr(m, n.sons[i]) let a = getConstExpr(m, n.sons[i])
if a == nil: return nil if a == nil: return nil
result.strVal.add(getStrOrChar(a)) result.strVal.add(getStrOrChar(a))
@ -602,10 +602,10 @@ proc newSymNodeTypeDesc*(s: PSym; info: TLineInfo): PNode =
result.typ = newType(tyTypeDesc, s.owner) result.typ = newType(tyTypeDesc, s.owner)
result.typ.addSonSkipIntLit(s.typ) result.typ.addSonSkipIntLit(s.typ)
proc getConstExpr(m: PSym, n: PNode): PNode = proc getConstExpr(m: PSym, n: PNode): PNode =
result = nil result = nil
case n.kind case n.kind
of nkSym: of nkSym:
var s = n.sym var s = n.sym
case s.kind case s.kind
of skEnumField: of skEnumField:
@ -636,14 +636,14 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
else: else:
result = newSymNodeTypeDesc(s, n.info) result = newSymNodeTypeDesc(s, n.info)
else: discard else: discard
of nkCharLit..nkNilLit: of nkCharLit..nkNilLit:
result = copyNode(n) result = copyNode(n)
of nkIfExpr: of nkIfExpr:
result = getConstIfExpr(m, n) result = getConstIfExpr(m, n)
of nkCall, nkCommand, nkCallStrLit, nkPrefix, nkInfix: of nkCall, nkCommand, nkCallStrLit, nkPrefix, nkInfix:
if n.sons[0].kind != nkSym: return if n.sons[0].kind != nkSym: return
var s = n.sons[0].sym var s = n.sons[0].sym
if s.kind != skProc: return if s.kind != skProc: return
try: try:
case s.magic case s.magic
of mNone: of mNone:
@ -651,8 +651,8 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
return return
of mSizeOf: of mSizeOf:
var a = n.sons[1] var a = n.sons[1]
if computeSize(a.typ) < 0: if computeSize(a.typ) < 0:
localError(a.info, errCannotEvalXBecauseIncompletelyDefined, localError(a.info, errCannotEvalXBecauseIncompletelyDefined,
"sizeof") "sizeof")
result = nil result = nil
elif skipTypes(a.typ, typedescInst).kind in elif skipTypes(a.typ, typedescInst).kind in
@ -662,21 +662,21 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
else: else:
result = nil result = nil
# XXX: size computation for complex types is still wrong # XXX: size computation for complex types is still wrong
of mLow: of mLow:
result = newIntNodeT(firstOrd(n.sons[1].typ), n) result = newIntNodeT(firstOrd(n.sons[1].typ), n)
of mHigh: of mHigh:
if skipTypes(n.sons[1].typ, abstractVar).kind notin if skipTypes(n.sons[1].typ, abstractVar).kind notin
{tySequence, tyString, tyCString, tyOpenArray, tyVarargs}: {tySequence, tyString, tyCString, tyOpenArray, tyVarargs}:
result = newIntNodeT(lastOrd(skipTypes(n[1].typ, abstractVar)), n) result = newIntNodeT(lastOrd(skipTypes(n[1].typ, abstractVar)), n)
else: else:
var a = getArrayConstr(m, n.sons[1]) var a = getArrayConstr(m, n.sons[1])
if a.kind == nkBracket: if a.kind == nkBracket:
# we can optimize it away: # we can optimize it away:
result = newIntNodeT(sonsLen(a)-1, n) result = newIntNodeT(sonsLen(a)-1, n)
of mLengthOpenArray: of mLengthOpenArray:
var a = getArrayConstr(m, n.sons[1]) var a = getArrayConstr(m, n.sons[1])
if a.kind == nkBracket: if a.kind == nkBracket:
# we can optimize it away! This fixes the bug ``len(134)``. # we can optimize it away! This fixes the bug ``len(134)``.
result = newIntNodeT(sonsLen(a), n) result = newIntNodeT(sonsLen(a), n)
else: else:
result = magicCall(m, n) result = magicCall(m, n)
@ -694,33 +694,33 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
result = evalIs(n, a) result = evalIs(n, a)
else: else:
result = magicCall(m, n) result = magicCall(m, n)
except OverflowError: except OverflowError:
localError(n.info, errOverOrUnderflow) localError(n.info, errOverOrUnderflow)
except DivByZeroError: except DivByZeroError:
localError(n.info, errConstantDivisionByZero) localError(n.info, errConstantDivisionByZero)
of nkAddr: of nkAddr:
var a = getConstExpr(m, n.sons[0]) var a = getConstExpr(m, n.sons[0])
if a != nil: if a != nil:
result = n result = n
n.sons[0] = a n.sons[0] = a
of nkBracket: of nkBracket:
result = copyTree(n) result = copyTree(n)
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var a = getConstExpr(m, n.sons[i]) var a = getConstExpr(m, n.sons[i])
if a == nil: return nil if a == nil: return nil
result.sons[i] = a result.sons[i] = a
incl(result.flags, nfAllConst) incl(result.flags, nfAllConst)
of nkRange: of nkRange:
var a = getConstExpr(m, n.sons[0]) var a = getConstExpr(m, n.sons[0])
if a == nil: return if a == nil: return
var b = getConstExpr(m, n.sons[1]) var b = getConstExpr(m, n.sons[1])
if b == nil: return if b == nil: return
result = copyNode(n) result = copyNode(n)
addSon(result, a) addSon(result, a)
addSon(result, b) addSon(result, b)
of nkCurly: of nkCurly:
result = copyTree(n) result = copyTree(n)
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var a = getConstExpr(m, n.sons[i]) var a = getConstExpr(m, n.sons[i])
if a == nil: return nil if a == nil: return nil
result.sons[i] = a result.sons[i] = a
@ -735,33 +735,33 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
of nkPar: of nkPar:
# tuple constructor # tuple constructor
result = copyTree(n) result = copyTree(n)
if (sonsLen(n) > 0) and (n.sons[0].kind == nkExprColonExpr): if (sonsLen(n) > 0) and (n.sons[0].kind == nkExprColonExpr):
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var a = getConstExpr(m, n.sons[i].sons[1]) var a = getConstExpr(m, n.sons[i].sons[1])
if a == nil: return nil if a == nil: return nil
result.sons[i].sons[1] = a result.sons[i].sons[1] = a
else: else:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var a = getConstExpr(m, n.sons[i]) var a = getConstExpr(m, n.sons[i])
if a == nil: return nil if a == nil: return nil
result.sons[i] = a result.sons[i] = a
incl(result.flags, nfAllConst) incl(result.flags, nfAllConst)
of nkChckRangeF, nkChckRange64, nkChckRange: of nkChckRangeF, nkChckRange64, nkChckRange:
var a = getConstExpr(m, n.sons[0]) var a = getConstExpr(m, n.sons[0])
if a == nil: return if a == nil: return
if leValueConv(n.sons[1], a) and leValueConv(a, n.sons[2]): if leValueConv(n.sons[1], a) and leValueConv(a, n.sons[2]):
result = a # a <= x and x <= b result = a # a <= x and x <= b
result.typ = n.typ result.typ = n.typ
else: else:
localError(n.info, errGenerated, `%`( localError(n.info, errGenerated, `%`(
msgKindToString(errIllegalConvFromXtoY), msgKindToString(errIllegalConvFromXtoY),
[typeToString(n.sons[0].typ), typeToString(n.typ)])) [typeToString(n.sons[0].typ), typeToString(n.typ)]))
of nkStringToCString, nkCStringToString: of nkStringToCString, nkCStringToString:
var a = getConstExpr(m, n.sons[0]) var a = getConstExpr(m, n.sons[0])
if a == nil: return if a == nil: return
result = a result = a
result.typ = n.typ result.typ = n.typ
of nkHiddenStdConv, nkHiddenSubConv, nkConv: of nkHiddenStdConv, nkHiddenSubConv, nkConv:
var a = getConstExpr(m, n.sons[1]) var a = getConstExpr(m, n.sons[1])
if a == nil: return if a == nil: return
result = foldConv(n, a, check=n.kind == nkHiddenStdConv) result = foldConv(n, a, check=n.kind == nkHiddenStdConv)

View file

@ -130,6 +130,18 @@ proc semLocals(c: PContext, n: PNode): PNode =
result.add(a) result.add(a)
proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode
proc isStrangeArray(t: PType): bool =
let t = t.skipTypes(abstractInst)
result = t.kind == tyArray and t.firstOrd != 0
proc isNegative(n: PNode): bool =
let n = n.skipConv
if n.kind in {nkCharLit..nkUInt64Lit}:
result = n.intVal < 0
elif n.kind in nkCallKinds and n.sons[0].kind == nkSym:
result = n.sons[0].sym.magic in {mUnaryMinusI, mUnaryMinusI64}
proc magicsAfterOverloadResolution(c: PContext, n: PNode, proc magicsAfterOverloadResolution(c: PContext, n: PNode,
flags: TExprFlags): PNode = flags: TExprFlags): PNode =
case n[0].sym.magic case n[0].sym.magic
@ -153,4 +165,35 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
of mProcCall: of mProcCall:
result = n result = n
result.typ = n[1].typ result.typ = n[1].typ
of mDotDot:
result = n
# disallow negative indexing for now:
if not c.p.bracketExpr.isNil:
if isNegative(n.sons[1]) or (n.len > 2 and isNegative(n.sons[2])):
localError(n.info, "use '^' instead of '-'; negative indexing is obsolete")
of mRoof:
# error correction:
result = n.sons[1]
if c.p.bracketExpr.isNil:
localError(n.info, "no surrounding array access context for '^'")
elif c.p.bracketExpr.checkForSideEffects != seNoSideEffect:
localError(n.info, "invalid context for '^' as '$#' has side effects" %
renderTree(c.p.bracketExpr))
elif c.p.bracketExpr.typ.isStrangeArray:
localError(n.info, "invalid context for '^' as len!=high+1 for '$#'" %
renderTree(c.p.bracketExpr))
else:
# ^x is rewritten to: len(a)-x
let lenExpr = newNodeI(nkCall, n.info)
lenExpr.add newIdentNode(getIdent"len", n.info)
lenExpr.add c.p.bracketExpr
let lenExprB = semExprWithType(c, lenExpr)
if lenExprB.typ.isNil or not isOrdinalType(lenExprB.typ):
localError(n.info, "'$#' has to be of an ordinal type for '^'" %
renderTree(lenExpr))
else:
result = newNodeIT(nkCall, n.info, getSysType(tyInt))
result.add newSymNode(createMagic("-", mSubI), n.info)
result.add lenExprB
result.add n.sons[1]
else: result = n else: result = n

View file

@ -1281,7 +1281,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
var tryStmt = newNodeI(nkTryStmt, n.sons[i].info) var tryStmt = newNodeI(nkTryStmt, n.sons[i].info)
var body = newNodeI(nkStmtList, n.sons[i].info) var body = newNodeI(nkStmtList, n.sons[i].info)
if i < n.sonsLen - 1: if i < n.sonsLen - 1:
body.sons = n.sons[(i+1)..(-1)] body.sons = n.sons[(i+1)..n.len-1]
tryStmt.addSon(body) tryStmt.addSon(body)
tryStmt.addSon(deferPart) tryStmt.addSon(deferPart)
n.sons[i] = semTry(c, tryStmt) n.sons[i] = semTry(c, tryStmt)

View file

@ -1175,6 +1175,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = semAnyRef(c, n, tyPtr, prev) result = semAnyRef(c, n, tyPtr, prev)
elif op.id == ord(wRef): elif op.id == ord(wRef):
result = semAnyRef(c, n, tyRef, prev) result = semAnyRef(c, n, tyRef, prev)
elif op.id == ord(wType):
checkSonsLen(n, 2)
let typExpr = semExprWithType(c, n.sons[1], {efInTypeof})
result = typExpr.typ.skipTypes({tyIter})
else: else:
result = semTypeExpr(c, n) result = semTypeExpr(c, n)
of nkWhenStmt: of nkWhenStmt:

View file

@ -590,7 +590,7 @@ proc firstOrd(t: PType): BiggestInt =
of tyUInt..tyUInt64: result = 0 of tyUInt..tyUInt64: result = 0
of tyEnum: of tyEnum:
# if basetype <> nil then return firstOrd of basetype # if basetype <> nil then return firstOrd of basetype
if (sonsLen(t) > 0) and (t.sons[0] != nil): if sonsLen(t) > 0 and t.sons[0] != nil:
result = firstOrd(t.sons[0]) result = firstOrd(t.sons[0])
else: else:
assert(t.n.sons[0].kind == nkSym) assert(t.n.sons[0].kind == nkSym)

View file

@ -42,6 +42,7 @@ proc opSlurp*(file: string, info: TLineInfo, module: PSym): string =
proc atomicTypeX(name: string; t: PType; info: TLineInfo): PNode = proc atomicTypeX(name: string; t: PType; info: TLineInfo): PNode =
let sym = newSym(skType, getIdent(name), t.owner, info) let sym = newSym(skType, getIdent(name), t.owner, info)
sym.typ = t
result = newSymNode(sym) result = newSymNode(sym)
result.typ = t result.typ = t

View file

@ -1008,7 +1008,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
if dest < 0: dest = c.getTemp(n.typ) if dest < 0: dest = c.getTemp(n.typ)
c.gABC(n, opcCallSite, dest) c.gABC(n, opcCallSite, dest)
of mNGenSym: genBinaryABC(c, n, dest, opcGenSym) of mNGenSym: genBinaryABC(c, n, dest, opcGenSym)
of mMinI, mMaxI, mMinI64, mMaxI64, mAbsF64, mMinF64, mMaxF64, mAbsI, mAbsI64: of mMinI, mMaxI, mMinI64, mMaxI64, mAbsF64, mMinF64, mMaxF64, mAbsI,
mAbsI64, mDotDot:
c.genCall(n, dest) c.genCall(n, dest)
of mExpandToAst: of mExpandToAst:
if n.len != 2: if n.len != 2:

View file

@ -121,7 +121,7 @@ different; for this a special setter syntax is needed:
.. code-block:: nim .. code-block:: nim
type type
Socket* = object of RootObj Socket* = ref object of RootObj
FHost: int # cannot be accessed from the outside of the module FHost: int # cannot be accessed from the outside of the module
# the `F` prefix is a convention to avoid clashes since # the `F` prefix is a convention to avoid clashes since
# the accessors are named `host` # the accessors are named `host`
@ -134,8 +134,8 @@ different; for this a special setter syntax is needed:
## getter of hostAddr ## getter of hostAddr
s.FHost s.FHost
var var s: Socket
s: Socket new s
s.host = 34 # same as `host=`(s, 34) s.host = 34 # same as `host=`(s, 34)
@ -351,32 +351,32 @@ dispatch.
.. code-block:: nim .. code-block:: nim
type type
Expression = object of RootObj ## abstract base class for an expression Expression = ref object of RootObj ## abstract base class for an expression
Literal = object of Expression Literal = ref object of Expression
x: int x: int
PlusExpr = object of Expression PlusExpr = ref object of Expression
a, b: ref Expression a, b: Expression
method eval(e: ref Expression): int = method eval(e: Expression): int =
# override this base method # override this base method
quit "to override!" quit "to override!"
method eval(e: ref Literal): int = return e.x method eval(e: Literal): int = return e.x
method eval(e: ref PlusExpr): int = method eval(e: PlusExpr): int =
# watch out: relies on dynamic binding # watch out: relies on dynamic binding
result = eval(e.a) + eval(e.b) result = eval(e.a) + eval(e.b)
proc newLit(x: int): ref Literal = proc newLit(x: int): Literal =
new(result) new(result)
result.x = x result.x = x
proc newPlus(a, b: ref Expression): ref PlusExpr = proc newPlus(a, b: Expression): PlusExpr =
new(result) new(result)
result.a = a result.a = a
result.b = b result.b = b
echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4))) echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
In the example the constructors ``newLit`` and ``newPlus`` are procs In the example the constructors ``newLit`` and ``newPlus`` are procs
because they should use static binding, but ``eval`` is a method because it because they should use static binding, but ``eval`` is a method because it
@ -387,8 +387,8 @@ dispatching:
.. code-block:: nim .. code-block:: nim
type type
Thing = object of RootObj Thing = ref object of RootObj
Unit = object of Thing Unit = ref object of Thing
x: int x: int
method collide(a, b: Thing) {.inline.} = method collide(a, b: Thing) {.inline.} =
@ -400,8 +400,9 @@ dispatching:
method collide(a: Unit, b: Thing) {.inline.} = method collide(a: Unit, b: Thing) {.inline.} =
echo "2" echo "2"
var var a, b: Unit
a, b: Unit new a
new b
collide(a, b) # output: 2 collide(a, b) # output: 2

View file

@ -568,7 +568,7 @@ the ``of`` operator can be used to determine the object's type.
name*: string # the * means that `name` is accessible from other modules name*: string # the * means that `name` is accessible from other modules
age: int # no * means that the field is hidden age: int # no * means that the field is hidden
Student = object of Person # a student is a person Student = ref object of Person # a student is a person
id: int # with an id field id: int # with an id field
var var

View file

@ -56,12 +56,12 @@ Objects have access to their type at runtime. There is an
.. code-block:: nim .. code-block:: nim
type type
Person = object of RootObj Person = ref object of RootObj
name*: string # the * means that `name` is accessible from other modules name*: string # the * means that `name` is accessible from other modules
age: int # no * means that the field is hidden from other modules age: int # no * means that the field is hidden from other modules
Student = object of Person # Student inherits from Person Student = ref object of Person # Student inherits from Person
id: int # with an id field id: int # with an id field
var var
student: Student student: Student
@ -69,6 +69,7 @@ Objects have access to their type at runtime. There is an
assert(student of Student) # is true assert(student of Student) # is true
# object construction: # object construction:
student = Student(name: "Anton", age: 5, id: 2) student = Student(name: "Anton", age: 5, id: 2)
echo student[]
Object fields that should be visible from outside the defining module have to Object fields that should be visible from outside the defining module have to
be marked by ``*``. In contrast to tuples, different object types are be marked by ``*``. In contrast to tuples, different object types are
@ -82,6 +83,9 @@ no ancestor are implicitly ``final``. You can use the ``inheritable`` pragma
to introduce new object roots apart from ``system.RootObj``. (This is used to introduce new object roots apart from ``system.RootObj``. (This is used
in the GTK wrapper for instance.) in the GTK wrapper for instance.)
Ref objects should be used whenever inheritance is used. It isn't strictly
necessary, but with non-ref objects assignments such as ``let person: Person =
Student(id: 123)`` will truncate subclass fields.
**Note**: Composition (*has-a* relation) is often preferable to inheritance **Note**: Composition (*has-a* relation) is often preferable to inheritance
(*is-a* relation) for simple code reuse. Since objects are value types in (*is-a* relation) for simple code reuse. Since objects are value types in
@ -228,7 +232,7 @@ is needed:
.. code-block:: nim .. code-block:: nim
type type
Socket* = object of RootObj Socket* = ref object of RootObj
FHost: int # cannot be accessed from the outside of the module FHost: int # cannot be accessed from the outside of the module
# the `F` prefix is a convention to avoid clashes since # the `F` prefix is a convention to avoid clashes since
# the accessors are named `host` # the accessors are named `host`
@ -241,8 +245,8 @@ is needed:
## getter of hostAddr ## getter of hostAddr
s.FHost s.FHost
var var s: Socket
s: Socket new s
s.host = 34 # same as `host=`(s, 34) s.host = 34 # same as `host=`(s, 34)
(The example also shows ``inline`` procedures.) (The example also shows ``inline`` procedures.)
@ -313,8 +317,8 @@ dispatching:
.. code-block:: nim .. code-block:: nim
type type
Thing = object of RootObj Thing = ref object of RootObj
Unit = object of Thing Unit = ref object of Thing
x: int x: int
method collide(a, b: Thing) {.inline.} = method collide(a, b: Thing) {.inline.} =
@ -326,8 +330,9 @@ dispatching:
method collide(a: Unit, b: Thing) {.inline.} = method collide(a: Unit, b: Thing) {.inline.} =
echo "2" echo "2"
var var a, b: Unit
a, b: Unit new a
new b
collide(a, b) # output: 2 collide(a, b) # output: 2

View file

@ -253,16 +253,16 @@ proc product*[T](x: openArray[seq[T]]): seq[seq[T]] =
while true: while true:
while indexes[index] == -1: while indexes[index] == -1:
indexes[index] = initial[index] indexes[index] = initial[index]
index +=1 index += 1
if index == x.len: return if index == x.len: return
indexes[index] -=1 indexes[index] -= 1
for ni, i in indexes: for ni, i in indexes:
next[ni] = x[ni][i] next[ni] = x[ni][i]
var res: seq[T] var res: seq[T]
shallowCopy(res, next) shallowCopy(res, next)
result.add(res) result.add(res)
index = 0 index = 0
indexes[index] -=1 indexes[index] -= 1
proc nextPermutation*[T](x: var openarray[T]): bool {.discardable.} = proc nextPermutation*[T](x: var openarray[T]): bool {.discardable.} =
## Calculates the next lexicographic permutation, directly modifying ``x``. ## Calculates the next lexicographic permutation, directly modifying ``x``.

View file

@ -61,8 +61,8 @@ proc pasv(ftp: AsyncFtpClient) {.async.} =
assertReply(pasvMsg, "227") assertReply(pasvMsg, "227")
var betweenParens = captureBetween(pasvMsg.string, '(', ')') var betweenParens = captureBetween(pasvMsg.string, '(', ')')
var nums = betweenParens.split(',') var nums = betweenParens.split(',')
var ip = nums[0.. -3] var ip = nums[0.. ^3]
var port = nums[-2.. -1] var port = nums[^2.. ^1]
var properPort = port[0].parseInt()*256+port[1].parseInt() var properPort = port[0].parseInt()*256+port[1].parseInt()
await ftp.dsock.connect(ip.join("."), Port(properPort.toU16)) await ftp.dsock.connect(ip.join("."), Port(properPort.toU16))
ftp.dsockConnected = true ftp.dsockConnected = true

View file

@ -18,8 +18,8 @@ import sockets, os
## This module implements an asynchronous event loop together with asynchronous ## This module implements an asynchronous event loop together with asynchronous
## sockets which use this event loop. ## sockets which use this event loop.
## It is akin to Python's asyncore module. Many modules that use sockets ## It is akin to Python's asyncore module. Many modules that use sockets
## have an implementation for this module, those modules should all have a ## have an implementation for this module, those modules should all have a
## ``register`` function which you should use to add the desired objects to a ## ``register`` function which you should use to add the desired objects to a
## dispatcher which you created so ## dispatcher which you created so
## that you can receive the events associated with that module's object. ## that you can receive the events associated with that module's object.
## ##
@ -27,19 +27,19 @@ import sockets, os
## function in a while loop. ## function in a while loop.
## ##
## **Note:** Most modules have tasks which need to be ran regularly, this is ## **Note:** Most modules have tasks which need to be ran regularly, this is
## why you should not call ``poll`` with a infinite timeout, or even a ## why you should not call ``poll`` with a infinite timeout, or even a
## very long one. In most cases the default timeout is fine. ## very long one. In most cases the default timeout is fine.
## ##
## **Note:** This module currently only supports select(), this is limited by ## **Note:** This module currently only supports select(), this is limited by
## FD_SETSIZE, which is usually 1024. So you may only be able to use 1024 ## FD_SETSIZE, which is usually 1024. So you may only be able to use 1024
## sockets at a time. ## sockets at a time.
## ##
## Most (if not all) modules that use asyncio provide a userArg which is passed ## Most (if not all) modules that use asyncio provide a userArg which is passed
## on with the events. The type that you set userArg to must be inheriting from ## on with the events. The type that you set userArg to must be inheriting from
## ``RootObj``! ## ``RootObj``!
## ##
## **Note:** If you want to provide async ability to your module please do not ## **Note:** If you want to provide async ability to your module please do not
## use the ``Delegate`` object, instead use ``AsyncSocket``. It is possible ## use the ``Delegate`` object, instead use ``AsyncSocket``. It is possible
## that in the future this type's fields will not be exported therefore breaking ## that in the future this type's fields will not be exported therefore breaking
## your code. ## your code.
## ##
@ -59,11 +59,11 @@ import sockets, os
## socket which will give you the client which is connecting. You should then ## socket which will give you the client which is connecting. You should then
## set any events that you want to use on that client and add it to your dispatcher ## set any events that you want to use on that client and add it to your dispatcher
## using the ``register`` procedure. ## using the ``register`` procedure.
## ##
## An example ``handleAccept`` follows: ## An example ``handleAccept`` follows:
## ##
## .. code-block:: nim ## .. code-block:: nim
## ##
## var disp = newDispatcher() ## var disp = newDispatcher()
## ... ## ...
## proc handleAccept(s: AsyncSocket) = ## proc handleAccept(s: AsyncSocket) =
@ -74,7 +74,7 @@ import sockets, os
## client.handleRead = ... ## client.handleRead = ...
## disp.register(client) ## disp.register(client)
## ... ## ...
## ##
## For client sockets you should only be interested in the ``handleRead`` and ## For client sockets you should only be interested in the ``handleRead`` and
## ``handleConnect`` events. The former gets called whenever the socket has ## ``handleConnect`` events. The former gets called whenever the socket has
## received messages and can be read from and the latter gets called whenever ## received messages and can be read from and the latter gets called whenever
@ -83,14 +83,14 @@ import sockets, os
## ##
## Getting a blocking client from an AsyncSocket ## Getting a blocking client from an AsyncSocket
## ============================================= ## =============================================
## ##
## If you need a asynchronous server socket but you wish to process the clients ## If you need a asynchronous server socket but you wish to process the clients
## synchronously then you can use the ``getSocket`` converter to get ## synchronously then you can use the ``getSocket`` converter to get
## a ``Socket`` from the ``AsyncSocket`` object, this can then be combined ## a ``Socket`` from the ``AsyncSocket`` object, this can then be combined
## with ``accept`` like so: ## with ``accept`` like so:
## ##
## .. code-block:: nim ## .. code-block:: nim
## ##
## proc handleAccept(s: AsyncSocket) = ## proc handleAccept(s: AsyncSocket) =
## var client: Socket ## var client: Socket
## getSocket(s).accept(client) ## getSocket(s).accept(client)
@ -113,11 +113,11 @@ type
handleWrite*: proc (h: RootRef) {.nimcall, gcsafe.} handleWrite*: proc (h: RootRef) {.nimcall, gcsafe.}
handleError*: proc (h: RootRef) {.nimcall, gcsafe.} handleError*: proc (h: RootRef) {.nimcall, gcsafe.}
hasDataBuffered*: proc (h: RootRef): bool {.nimcall, gcsafe.} hasDataBuffered*: proc (h: RootRef): bool {.nimcall, gcsafe.}
open*: bool open*: bool
task*: proc (h: RootRef) {.nimcall, gcsafe.} task*: proc (h: RootRef) {.nimcall, gcsafe.}
mode*: FileMode mode*: FileMode
Delegate* = ref DelegateObj Delegate* = ref DelegateObj
Dispatcher* = ref DispatcherObj Dispatcher* = ref DispatcherObj
@ -144,7 +144,7 @@ type
deleg: Delegate deleg: Delegate
SocketStatus* = enum SocketStatus* = enum
SockIdle, SockConnecting, SockConnected, SockListening, SockClosed, SockIdle, SockConnecting, SockConnected, SockListening, SockClosed,
SockUDPBound SockUDPBound
{.deprecated: [TDelegate: DelegateObj, PDelegate: Delegate, {.deprecated: [TDelegate: DelegateObj, PDelegate: Delegate,
@ -176,8 +176,8 @@ proc newAsyncSocket(): AsyncSocket =
result.lineBuffer = "".TaintedString result.lineBuffer = "".TaintedString
result.sendBuffer = "" result.sendBuffer = ""
proc asyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM, proc asyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP, protocol: Protocol = IPPROTO_TCP,
buffered = true): AsyncSocket = buffered = true): AsyncSocket =
## Initialises an AsyncSocket object. If a socket cannot be initialised ## Initialises an AsyncSocket object. If a socket cannot be initialised
## EOS is raised. ## EOS is raised.
@ -236,7 +236,7 @@ proc asyncSockHandleWrite(h: RootRef) =
if AsyncSocket(h).socket.isSSL and not if AsyncSocket(h).socket.isSSL and not
AsyncSocket(h).socket.gotHandshake: AsyncSocket(h).socket.gotHandshake:
return return
if AsyncSocket(h).info == SockConnecting: if AsyncSocket(h).info == SockConnecting:
AsyncSocket(h).handleConnect(AsyncSocket(h)) AsyncSocket(h).handleConnect(AsyncSocket(h))
AsyncSocket(h).info = SockConnected AsyncSocket(h).info = SockConnected
@ -256,10 +256,10 @@ proc asyncSockHandleWrite(h: RootRef) =
# do nothing instead. # do nothing instead.
discard discard
elif bytesSent != sock.sendBuffer.len: elif bytesSent != sock.sendBuffer.len:
sock.sendBuffer = sock.sendBuffer[bytesSent .. -1] sock.sendBuffer = sock.sendBuffer[bytesSent .. ^1]
elif bytesSent == sock.sendBuffer.len: elif bytesSent == sock.sendBuffer.len:
sock.sendBuffer = "" sock.sendBuffer = ""
if AsyncSocket(h).handleWrite != nil: if AsyncSocket(h).handleWrite != nil:
AsyncSocket(h).handleWrite(AsyncSocket(h)) AsyncSocket(h).handleWrite(AsyncSocket(h))
except OSError: except OSError:
@ -284,7 +284,7 @@ when defined(ssl):
else: else:
# handshake will set socket's ``sslNoHandshake`` field. # handshake will set socket's ``sslNoHandshake`` field.
discard AsyncSocket(h).socket.handshake() discard AsyncSocket(h).socket.handshake()
proc asyncSockTask(h: RootRef) = proc asyncSockTask(h: RootRef) =
when defined(ssl): when defined(ssl):
@ -377,9 +377,9 @@ proc acceptAddr*(server: AsyncSocket, client: var AsyncSocket,
if c == invalidSocket: raiseSocketError(server.socket) if c == invalidSocket: raiseSocketError(server.socket)
c.setBlocking(false) # TODO: Needs to be tested. c.setBlocking(false) # TODO: Needs to be tested.
# deleg.open is set in ``toDelegate``. # deleg.open is set in ``toDelegate``.
client.socket = c client.socket = c
client.lineBuffer = "".TaintedString client.lineBuffer = "".TaintedString
client.sendBuffer = "" client.sendBuffer = ""
@ -393,7 +393,7 @@ proc accept*(server: AsyncSocket, client: var AsyncSocket) =
proc acceptAddr*(server: AsyncSocket): tuple[sock: AsyncSocket, proc acceptAddr*(server: AsyncSocket): tuple[sock: AsyncSocket,
address: string] {.deprecated.} = address: string] {.deprecated.} =
## Equivalent to ``sockets.acceptAddr``. ## Equivalent to ``sockets.acceptAddr``.
## ##
## **Deprecated since version 0.9.0:** Please use the function above. ## **Deprecated since version 0.9.0:** Please use the function above.
var client = newAsyncSocket() var client = newAsyncSocket()
var address: string = "" var address: string = ""
@ -441,17 +441,17 @@ proc isConnected*(s: AsyncSocket): bool =
## Determines whether ``s`` is connected. ## Determines whether ``s`` is connected.
return s.info == SockConnected return s.info == SockConnected
proc isListening*(s: AsyncSocket): bool = proc isListening*(s: AsyncSocket): bool =
## Determines whether ``s`` is listening for incoming connections. ## Determines whether ``s`` is listening for incoming connections.
return s.info == SockListening return s.info == SockListening
proc isConnecting*(s: AsyncSocket): bool = proc isConnecting*(s: AsyncSocket): bool =
## Determines whether ``s`` is connecting. ## Determines whether ``s`` is connecting.
return s.info == SockConnecting return s.info == SockConnecting
proc isClosed*(s: AsyncSocket): bool = proc isClosed*(s: AsyncSocket): bool =
## Determines whether ``s`` has been closed. ## Determines whether ``s`` has been closed.
return s.info == SockClosed return s.info == SockClosed
proc isSendDataBuffered*(s: AsyncSocket): bool = proc isSendDataBuffered*(s: AsyncSocket): bool =
## Determines whether ``s`` has data waiting to be sent, i.e. whether this ## Determines whether ``s`` has data waiting to be sent, i.e. whether this
## socket's sendBuffer contains data. ## socket's sendBuffer contains data.
return s.sendBuffer.len != 0 return s.sendBuffer.len != 0
proc setHandleWrite*(s: AsyncSocket, proc setHandleWrite*(s: AsyncSocket,
@ -550,7 +550,7 @@ proc send*(sock: AsyncSocket, data: string) =
sock.sendBuffer.add(data) sock.sendBuffer.add(data)
sock.deleg.mode = fmReadWrite sock.deleg.mode = fmReadWrite
elif bytesSent != data.len: elif bytesSent != data.len:
sock.sendBuffer.add(data[bytesSent .. -1]) sock.sendBuffer.add(data[bytesSent .. ^1])
sock.deleg.mode = fmReadWrite sock.deleg.mode = fmReadWrite
proc timeValFromMilliseconds(timeout = 500): Timeval = proc timeValFromMilliseconds(timeout = 500): Timeval =
@ -561,10 +561,10 @@ proc timeValFromMilliseconds(timeout = 500): Timeval =
proc createFdSet(fd: var TFdSet, s: seq[Delegate], m: var int) = proc createFdSet(fd: var TFdSet, s: seq[Delegate], m: var int) =
FD_ZERO(fd) FD_ZERO(fd)
for i in items(s): for i in items(s):
m = max(m, int(i.fd)) m = max(m, int(i.fd))
FD_SET(i.fd, fd) FD_SET(i.fd, fd)
proc pruneSocketSet(s: var seq[Delegate], fd: var TFdSet) = proc pruneSocketSet(s: var seq[Delegate], fd: var TFdSet) =
var i = 0 var i = 0
var L = s.len var L = s.len
@ -576,16 +576,16 @@ proc pruneSocketSet(s: var seq[Delegate], fd: var TFdSet) =
inc(i) inc(i)
setLen(s, L) setLen(s, L)
proc select(readfds, writefds, exceptfds: var seq[Delegate], proc select(readfds, writefds, exceptfds: var seq[Delegate],
timeout = 500): int = timeout = 500): int =
var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout) var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
var rd, wr, ex: TFdSet var rd, wr, ex: TFdSet
var m = 0 var m = 0
createFdSet(rd, readfds, m) createFdSet(rd, readfds, m)
createFdSet(wr, writefds, m) createFdSet(wr, writefds, m)
createFdSet(ex, exceptfds, m) createFdSet(ex, exceptfds, m)
if timeout != -1: if timeout != -1:
result = int(select(cint(m+1), addr(rd), addr(wr), addr(ex), addr(tv))) result = int(select(cint(m+1), addr(rd), addr(wr), addr(ex), addr(tv)))
else: else:
@ -599,7 +599,7 @@ proc poll*(d: Dispatcher, timeout: int = 500): bool =
## This function checks for events on all the delegates in the `PDispatcher`. ## This function checks for events on all the delegates in the `PDispatcher`.
## It then proceeds to call the correct event handler. ## It then proceeds to call the correct event handler.
## ##
## This function returns ``True`` if there are file descriptors that are still ## This function returns ``True`` if there are file descriptors that are still
## open, otherwise ``False``. File descriptors that have been ## open, otherwise ``False``. File descriptors that have been
## closed are immediately removed from the dispatcher automatically. ## closed are immediately removed from the dispatcher automatically.
## ##
@ -611,7 +611,7 @@ proc poll*(d: Dispatcher, timeout: int = 500): bool =
var readDg, writeDg, errorDg: seq[Delegate] = @[] var readDg, writeDg, errorDg: seq[Delegate] = @[]
var len = d.delegates.len var len = d.delegates.len
var dc = 0 var dc = 0
while dc < len: while dc < len:
let deleg = d.delegates[dc] let deleg = d.delegates[dc]
if (deleg.mode != fmWrite or deleg.mode != fmAppend) and deleg.open: if (deleg.mode != fmWrite or deleg.mode != fmAppend) and deleg.open:
@ -625,20 +625,20 @@ proc poll*(d: Dispatcher, timeout: int = 500): bool =
# File/socket has been closed. Remove it from dispatcher. # File/socket has been closed. Remove it from dispatcher.
d.delegates[dc] = d.delegates[len-1] d.delegates[dc] = d.delegates[len-1]
dec len dec len
d.delegates.setLen(len) d.delegates.setLen(len)
var hasDataBufferedCount = 0 var hasDataBufferedCount = 0
for d in d.delegates: for d in d.delegates:
if d.hasDataBuffered(d.deleVal): if d.hasDataBuffered(d.deleVal):
hasDataBufferedCount.inc() hasDataBufferedCount.inc()
d.handleRead(d.deleVal) d.handleRead(d.deleVal)
if hasDataBufferedCount > 0: return true if hasDataBufferedCount > 0: return true
if readDg.len() == 0 and writeDg.len() == 0: if readDg.len() == 0 and writeDg.len() == 0:
## TODO: Perhaps this shouldn't return if errorDg has something? ## TODO: Perhaps this shouldn't return if errorDg has something?
return false return false
if select(readDg, writeDg, errorDg, timeout) != 0: if select(readDg, writeDg, errorDg, timeout) != 0:
for i in 0..len(d.delegates)-1: for i in 0..len(d.delegates)-1:
if i > len(d.delegates)-1: break # One delegate might've been removed. if i > len(d.delegates)-1: break # One delegate might've been removed.
@ -651,7 +651,7 @@ proc poll*(d: Dispatcher, timeout: int = 500): bool =
deleg.handleWrite(deleg.deleVal) deleg.handleWrite(deleg.deleVal)
if deleg notin errorDg: if deleg notin errorDg:
deleg.handleError(deleg.deleVal) deleg.handleError(deleg.deleVal)
# Execute tasks # Execute tasks
for i in items(d.delegates): for i in items(d.delegates):
i.task(i.deleVal) i.task(i.deleVal)
@ -664,7 +664,7 @@ when isMainModule:
proc testConnect(s: AsyncSocket, no: int) = proc testConnect(s: AsyncSocket, no: int) =
echo("Connected! " & $no) echo("Connected! " & $no)
proc testRead(s: AsyncSocket, no: int) = proc testRead(s: AsyncSocket, no: int) =
echo("Reading! " & $no) echo("Reading! " & $no)
var data = "" var data = ""
@ -682,31 +682,31 @@ when isMainModule:
var address = "" var address = ""
s.acceptAddr(client, address) s.acceptAddr(client, address)
echo("Accepted ", address) echo("Accepted ", address)
client.handleRead = client.handleRead =
proc (s: AsyncSocket) = proc (s: AsyncSocket) =
testRead(s, 2) testRead(s, 2)
disp.register(client) disp.register(client)
proc main = proc main =
var d = newDispatcher() var d = newDispatcher()
var s = asyncSocket() var s = asyncSocket()
s.connect("amber.tenthbit.net", Port(6667)) s.connect("amber.tenthbit.net", Port(6667))
s.handleConnect = s.handleConnect =
proc (s: AsyncSocket) = proc (s: AsyncSocket) =
testConnect(s, 1) testConnect(s, 1)
s.handleRead = s.handleRead =
proc (s: AsyncSocket) = proc (s: AsyncSocket) =
testRead(s, 1) testRead(s, 1)
d.register(s) d.register(s)
var server = asyncSocket() var server = asyncSocket()
server.handleAccept = server.handleAccept =
proc (s: AsyncSocket) = proc (s: AsyncSocket) =
testAccept(s, d, 78) testAccept(s, d, 78)
server.bindAddr(Port(5555)) server.bindAddr(Port(5555))
server.listen() server.listen()
d.register(server) d.register(server)
while d.poll(-1): discard while d.poll(-1): discard
main() main()

View file

@ -204,7 +204,7 @@ proc nimFlowVarSignal(fv: FlowVarBase) {.compilerProc.} =
inc fv.ai.cv.counter inc fv.ai.cv.counter
release(fv.ai.cv.L) release(fv.ai.cv.L)
signal(fv.ai.cv.c) signal(fv.ai.cv.c)
if fv.usesSemaphore: if fv.usesSemaphore:
signal(fv.cv) signal(fv.cv)
proc awaitAndThen*[T](fv: FlowVar[T]; action: proc (x: T) {.closure.}) = proc awaitAndThen*[T](fv: FlowVar[T]; action: proc (x: T) {.closure.}) =

View file

@ -15,8 +15,8 @@ from rawsockets import nil
from asyncdispatch import PFuture from asyncdispatch import PFuture
## This module **partially** implements an FTP client as specified ## This module **partially** implements an FTP client as specified
## by `RFC 959 <http://tools.ietf.org/html/rfc959>`_. ## by `RFC 959 <http://tools.ietf.org/html/rfc959>`_.
## ##
## This module provides both a synchronous and asynchronous implementation. ## This module provides both a synchronous and asynchronous implementation.
## The asynchronous implementation requires you to use the ``asyncFTPClient`` ## The asynchronous implementation requires you to use the ``asyncFTPClient``
## function. You are then required to register the ``AsyncFTPClient`` with a ## function. You are then required to register the ``AsyncFTPClient`` with a
@ -27,7 +27,7 @@ from asyncdispatch import PFuture
## file transfers, calls to functions which use the command socket will block. ## file transfers, calls to functions which use the command socket will block.
## ##
## Here is some example usage of this module: ## Here is some example usage of this module:
## ##
## .. code-block:: Nim ## .. code-block:: Nim
## var ftp = ftpClient("example.org", user = "user", pass = "pass") ## var ftp = ftpClient("example.org", user = "user", pass = "pass")
## ftp.connect() ## ftp.connect()
@ -51,7 +51,7 @@ type
port*: rawsockets.Port port*: rawsockets.Port
else: else:
port*: Port port*: Port
jobInProgress*: bool jobInProgress*: bool
job*: FTPJob[SockType] job*: FTPJob[SockType]
@ -91,7 +91,7 @@ type
of EvLines: of EvLines:
lines*: string ## Lines that have been transferred. lines*: string ## Lines that have been transferred.
of EvRetr, EvStore: ## Retr/Store operation finished. of EvRetr, EvStore: ## Retr/Store operation finished.
nil nil
of EvTransferProgress: of EvTransferProgress:
bytesTotal*: BiggestInt ## Bytes total. bytesTotal*: BiggestInt ## Bytes total.
bytesFinished*: BiggestInt ## Bytes transferred. bytesFinished*: BiggestInt ## Bytes transferred.
@ -213,7 +213,7 @@ proc handleConnect(s: AsyncSocket, ftp: AsyncFTPClient) =
proc handleRead(s: AsyncSocket, ftp: AsyncFTPClient) = proc handleRead(s: AsyncSocket, ftp: AsyncFTPClient) =
assert ftp.jobInProgress assert ftp.jobInProgress
assert ftp.job.typ != JStore assert ftp.job.typ != JStore
# This can never return true, because it shouldn't check for code # This can never return true, because it shouldn't check for code
# 226 from csock. # 226 from csock.
assert(not ftp.job.prc(ftp, true)) assert(not ftp.job.prc(ftp, true))
@ -236,13 +236,13 @@ proc pasv[T](ftp: FtpBase[T]) =
ftp.disp.register(ftp.dsock) ftp.disp.register(ftp.dsock)
else: else:
{.fatal: "Incorrect socket instantiation".} {.fatal: "Incorrect socket instantiation".}
var pasvMsg = ftp.send("PASV").string.strip.TaintedString var pasvMsg = ftp.send("PASV").string.strip.TaintedString
assertReply(pasvMsg, "227") assertReply(pasvMsg, "227")
var betweenParens = captureBetween(pasvMsg.string, '(', ')') var betweenParens = captureBetween(pasvMsg.string, '(', ')')
var nums = betweenParens.split(',') var nums = betweenParens.split(',')
var ip = nums[0.. -3] var ip = nums[0.. ^3]
var port = nums[-2.. -1] var port = nums[^2.. ^1]
var properPort = port[0].parseInt()*256+port[1].parseInt() var properPort = port[0].parseInt()*256+port[1].parseInt()
ftp.dsock.connect(ip.join("."), Port(properPort.toU16)) ftp.dsock.connect(ip.join("."), Port(properPort.toU16))
when T is AsyncSocket: when T is AsyncSocket:
@ -307,7 +307,7 @@ proc getLines[T](ftp: FtpBase[T], async: bool = false): bool =
ftp.job.lines.add(r.string & "\n") ftp.job.lines.add(r.string & "\n")
else: else:
{.fatal: "Incorrect socket instantiation".} {.fatal: "Incorrect socket instantiation".}
if not async: if not async:
var readSocks: seq[Socket] = @[ftp.csock] var readSocks: seq[Socket] = @[ftp.csock]
# This is only needed here. Asyncio gets this socket... # This is only needed here. Asyncio gets this socket...
@ -396,7 +396,7 @@ proc chmod*[T](ftp: FtpBase[T], path: string,
proc list*[T](ftp: FtpBase[T], dir: string = "", async = false): string = proc list*[T](ftp: FtpBase[T], dir: string = "", async = false): string =
## Lists all files in ``dir``. If ``dir`` is ``""``, uses the current ## Lists all files in ``dir``. If ``dir`` is ``""``, uses the current
## working directory. If ``async`` is true, this function will return ## working directory. If ``async`` is true, this function will return
## immediately and it will be your job to call asyncio's ## immediately and it will be your job to call asyncio's
## ``poll`` to progress this operation. ## ``poll`` to progress this operation.
ftp.createJob(getLines[T], JRetrText) ftp.createJob(getLines[T], JRetrText)
ftp.pasv() ftp.pasv()
@ -417,7 +417,7 @@ proc retrText*[T](ftp: FtpBase[T], file: string, async = false): string =
ftp.createJob(getLines[T], JRetrText) ftp.createJob(getLines[T], JRetrText)
ftp.pasv() ftp.pasv()
assertReply ftp.send("RETR " & file.normalizePathSep), ["125", "150"] assertReply ftp.send("RETR " & file.normalizePathSep), ["125", "150"]
if not async: if not async:
while not ftp.job.prc(ftp, false): discard while not ftp.job.prc(ftp, false): discard
result = ftp.job.lines result = ftp.job.lines
@ -436,7 +436,7 @@ proc getFile[T](ftp: FtpBase[T], async = false): bool =
else: else:
bytesRead = ftp.dsock.recvAsync(r, BufferSize) bytesRead = ftp.dsock.recvAsync(r, BufferSize)
returned = bytesRead != -1 returned = bytesRead != -1
else: else:
bytesRead = ftp.dsock.recv(r, BufferSize) bytesRead = ftp.dsock.recv(r, BufferSize)
returned = true returned = true
let r2 = r.string let r2 = r.string
@ -458,7 +458,7 @@ proc getFile[T](ftp: FtpBase[T], async = false): bool =
proc retrFile*[T](ftp: FtpBase[T], file, dest: string, async = false) = proc retrFile*[T](ftp: FtpBase[T], file, dest: string, async = false) =
## Downloads ``file`` and saves it to ``dest``. Usage of this function ## Downloads ``file`` and saves it to ``dest``. Usage of this function
## asynchronously is recommended to view the progress of the download. ## asynchronously is recommended to view the progress of the download.
## The ``EvRetr`` event is passed to the specified ``handleEvent`` function ## The ``EvRetr`` event is passed to the specified ``handleEvent`` function
## when the download is finished, and the ``filename`` field will be equal ## when the download is finished, and the ``filename`` field will be equal
## to ``file``. ## to ``file``.
ftp.createJob(getFile[T], JRetr) ftp.createJob(getFile[T], JRetr)
@ -471,7 +471,7 @@ proc retrFile*[T](ftp: FtpBase[T], file, dest: string, async = false) =
var fileSize: BiggestInt var fileSize: BiggestInt
if reply.string.captureBetween('(', ')').parseBiggestInt(fileSize) == 0: if reply.string.captureBetween('(', ')').parseBiggestInt(fileSize) == 0:
raise newException(ReplyError, "Reply has no file size.") raise newException(ReplyError, "Reply has no file size.")
ftp.job.total = fileSize ftp.job.total = fileSize
ftp.job.lastProgressReport = epochTime() ftp.job.lastProgressReport = epochTime()
ftp.job.filename = file.normalizePathSep ftp.job.filename = file.normalizePathSep
@ -488,7 +488,7 @@ proc doUpload[T](ftp: FtpBase[T], async = false): bool =
if bytesSent == ftp.job.toStore.len: if bytesSent == ftp.job.toStore.len:
ftp.job.toStore = "" ftp.job.toStore = ""
elif bytesSent != ftp.job.toStore.len and bytesSent != 0: elif bytesSent != ftp.job.toStore.len and bytesSent != 0:
ftp.job.toStore = ftp.job.toStore[bytesSent .. -1] ftp.job.toStore = ftp.job.toStore[bytesSent .. ^1]
ftp.job.progress.inc(bytesSent) ftp.job.progress.inc(bytesSent)
ftp.job.oneSecond.inc(bytesSent) ftp.job.oneSecond.inc(bytesSent)
else: else:
@ -499,12 +499,12 @@ proc doUpload[T](ftp: FtpBase[T], async = false): bool =
# File finished uploading. # File finished uploading.
ftp.dsock.close() ftp.dsock.close()
ftp.dsockConnected = false ftp.dsockConnected = false
if not async: if not async:
assertReply ftp.expectReply(), "226" assertReply ftp.expectReply(), "226"
return true return true
return false return false
if not async: if not async:
ftp.dsock.send(s) ftp.dsock.send(s)
else: else:
@ -512,9 +512,9 @@ proc doUpload[T](ftp: FtpBase[T], async = false): bool =
if bytesSent == 0: if bytesSent == 0:
ftp.job.toStore.add(s) ftp.job.toStore.add(s)
elif bytesSent != s.len: elif bytesSent != s.len:
ftp.job.toStore.add(s[bytesSent .. -1]) ftp.job.toStore.add(s[bytesSent .. ^1])
len = bytesSent len = bytesSent
ftp.job.progress.inc(len) ftp.job.progress.inc(len)
ftp.job.oneSecond.inc(len) ftp.job.oneSecond.inc(len)
@ -522,8 +522,8 @@ proc store*[T](ftp: FtpBase[T], file, dest: string, async = false) =
## Uploads ``file`` to ``dest`` on the remote FTP server. Usage of this ## Uploads ``file`` to ``dest`` on the remote FTP server. Usage of this
## function asynchronously is recommended to view the progress of ## function asynchronously is recommended to view the progress of
## the download. ## the download.
## The ``EvStore`` event is passed to the specified ``handleEvent`` function ## The ``EvStore`` event is passed to the specified ``handleEvent`` function
## when the upload is finished, and the ``filename`` field will be ## when the upload is finished, and the ``filename`` field will be
## equal to ``file``. ## equal to ``file``.
ftp.createJob(doUpload[T], JStore) ftp.createJob(doUpload[T], JStore)
ftp.job.file = open(file) ftp.job.file = open(file)
@ -531,7 +531,7 @@ proc store*[T](ftp: FtpBase[T], file, dest: string, async = false) =
ftp.job.lastProgressReport = epochTime() ftp.job.lastProgressReport = epochTime()
ftp.job.filename = file ftp.job.filename = file
ftp.pasv() ftp.pasv()
assertReply ftp.send("STOR " & dest.normalizePathSep), ["125", "150"] assertReply ftp.send("STOR " & dest.normalizePathSep), ["125", "150"]
if not async: if not async:
@ -564,12 +564,12 @@ proc csockHandleRead(s: AsyncSocket, ftp: AsyncFTPClient) =
if ftp.job.progress != ftp.job.total: if ftp.job.progress != ftp.job.total:
raise newException(FTPError, "Didn't upload full file.") raise newException(FTPError, "Didn't upload full file.")
ftp.deleteJob() ftp.deleteJob()
ftp.handleEvent(ftp, r) ftp.handleEvent(ftp, r)
proc asyncFTPClient*(address: string, port = Port(21), proc asyncFTPClient*(address: string, port = Port(21),
user, pass = "", user, pass = "",
handleEvent: proc (ftp: AsyncFTPClient, ev: FTPEvent) {.closure,gcsafe.} = handleEvent: proc (ftp: AsyncFTPClient, ev: FTPEvent) {.closure,gcsafe.} =
(proc (ftp: AsyncFTPClient, ev: FTPEvent) = discard)): AsyncFTPClient = (proc (ftp: AsyncFTPClient, ev: FTPEvent) = discard)): AsyncFTPClient =
## Create a ``AsyncFTPClient`` object. ## Create a ``AsyncFTPClient`` object.
## ##
@ -617,7 +617,7 @@ when isMainModule:
echo d.len echo d.len
else: assert(false) else: assert(false)
var ftp = asyncFTPClient("example.com", user = "foo", pass = "bar", handleEvent = hev) var ftp = asyncFTPClient("example.com", user = "foo", pass = "bar", handleEvent = hev)
d.register(ftp) d.register(ftp)
d.len.echo() d.len.echo()
ftp.connect() ftp.connect()

View file

@ -227,7 +227,7 @@ proc parseResponse(s: Socket, getBody: bool, timeout: int): Response =
inc(linei, le) inc(linei, le)
# Status code # Status code
linei.inc skipWhitespace(line, linei) linei.inc skipWhitespace(line, linei)
result.status = line[linei .. -1] result.status = line[linei .. ^1]
parsedStatus = true parsedStatus = true
else: else:
# Parse headers # Parse headers
@ -238,7 +238,7 @@ proc parseResponse(s: Socket, getBody: bool, timeout: int): Response =
if line[linei] != ':': httpError("invalid headers") if line[linei] != ':': httpError("invalid headers")
inc(linei) # Skip : inc(linei) # Skip :
result.headers[name] = line[linei.. -1].strip() result.headers[name] = line[linei.. ^1].strip()
if not fullyRead: if not fullyRead:
httpError("Connection was closed before full request has been made") httpError("Connection was closed before full request has been made")
if getBody: if getBody:
@ -442,7 +442,7 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
## | Extra headers can be specified and must be separated by ``\c\L`` ## | Extra headers can be specified and must be separated by ``\c\L``
## | An optional timeout can be specified in miliseconds, if reading from the ## | An optional timeout can be specified in miliseconds, if reading from the
## server takes longer than specified an ETimeout exception will be raised. ## server takes longer than specified an ETimeout exception will be raised.
result = request(url, $httpMethod, extraHeaders, body, sslContext, timeout, result = request(url, $httpMethod, extraHeaders, body, sslContext, timeout,
userAgent, proxy) userAgent, proxy)
proc redirection(status: string): bool = proc redirection(status: string): bool =
@ -725,7 +725,7 @@ proc parseResponse(client: AsyncHttpClient,
inc(linei, le) inc(linei, le)
# Status code # Status code
linei.inc skipWhitespace(line, linei) linei.inc skipWhitespace(line, linei)
result.status = line[linei .. -1] result.status = line[linei .. ^1]
parsedStatus = true parsedStatus = true
else: else:
# Parse headers # Parse headers
@ -736,7 +736,7 @@ proc parseResponse(client: AsyncHttpClient,
if line[linei] != ':': httpError("invalid headers") if line[linei] != ':': httpError("invalid headers")
inc(linei) # Skip : inc(linei) # Skip :
result.headers[name] = line[linei.. -1].strip() result.headers[name] = line[linei.. ^1].strip()
if not fullyRead: if not fullyRead:
httpError("Connection was closed before full request has been made") httpError("Connection was closed before full request has been made")
if getBody: if getBody:

View file

@ -10,7 +10,7 @@
## This module implements a simple logger. It has been designed to be as simple ## This module implements a simple logger. It has been designed to be as simple
## as possible to avoid bloat, if this library does not fulfill your needs, ## as possible to avoid bloat, if this library does not fulfill your needs,
## write your own. ## write your own.
## ##
## Format strings support the following variables which must be prefixed with ## Format strings support the following variables which must be prefixed with
## the dollar operator (``$``): ## the dollar operator (``$``):
## ##
@ -21,13 +21,13 @@
## $time Current time ## $time Current time
## $app ``os.getAppFilename()`` ## $app ``os.getAppFilename()``
## ============ ======================= ## ============ =======================
## ##
## ##
## The following example demonstrates logging to three different handlers ## The following example demonstrates logging to three different handlers
## simultaneously: ## simultaneously:
## ##
## .. code-block:: nim ## .. code-block:: nim
## ##
## var L = newConsoleLogger() ## var L = newConsoleLogger()
## var fL = newFileLogger("test.log", fmtStr = verboseFmtStr) ## var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
## var rL = newRollingFileLogger("rolling.log", fmtStr = verboseFmtStr) ## var rL = newRollingFileLogger("rolling.log", fmtStr = verboseFmtStr)
@ -64,20 +64,20 @@ const
type type
Logger* = ref object of RootObj ## abstract logger; the base type of all loggers Logger* = ref object of RootObj ## abstract logger; the base type of all loggers
levelThreshold*: Level ## only messages of level >= levelThreshold levelThreshold*: Level ## only messages of level >= levelThreshold
## should be processed ## should be processed
fmtStr: string ## = defaultFmtStr by default, see substituteLog for $date etc. fmtStr: string ## = defaultFmtStr by default, see substituteLog for $date etc.
ConsoleLogger* = ref object of Logger ## logger that writes the messages to the ConsoleLogger* = ref object of Logger ## logger that writes the messages to the
## console ## console
FileLogger* = ref object of Logger ## logger that writes the messages to a file FileLogger* = ref object of Logger ## logger that writes the messages to a file
f: File f: File
RollingFileLogger* = ref object of FileLogger ## logger that writes the RollingFileLogger* = ref object of FileLogger ## logger that writes the
## messages to a file and ## messages to a file and
## performs log rotation ## performs log rotation
maxLines: int # maximum number of lines maxLines: int # maximum number of lines
curLine : int curLine : int
baseName: string # initial filename baseName: string # initial filename
baseMode: FileMode # initial file mode baseMode: FileMode # initial file mode
@ -86,22 +86,22 @@ type
{.deprecated: [TLevel: Level, PLogger: Logger, PConsoleLogger: ConsoleLogger, {.deprecated: [TLevel: Level, PLogger: Logger, PConsoleLogger: ConsoleLogger,
PFileLogger: FileLogger, PRollingFileLogger: RollingFileLogger].} PFileLogger: FileLogger, PRollingFileLogger: RollingFileLogger].}
proc substituteLog(frmt: string): string = proc substituteLog(frmt: string): string =
## converts $date to the current date ## converts $date to the current date
## converts $time to the current time ## converts $time to the current time
## converts $app to getAppFilename() ## converts $app to getAppFilename()
## converts ## converts
result = newStringOfCap(frmt.len + 20) result = newStringOfCap(frmt.len + 20)
var i = 0 var i = 0
while i < frmt.len: while i < frmt.len:
if frmt[i] != '$': if frmt[i] != '$':
result.add(frmt[i]) result.add(frmt[i])
inc(i) inc(i)
else: else:
inc(i) inc(i)
var v = "" var v = ""
var app = getAppFilename() var app = getAppFilename()
while frmt[i] in IdentChars: while frmt[i] in IdentChars:
v.add(toLower(frmt[i])) v.add(toLower(frmt[i]))
inc(i) inc(i)
case v case v
@ -114,12 +114,12 @@ proc substituteLog(frmt: string): string =
method log*(logger: Logger, level: Level, method log*(logger: Logger, level: Level,
frmt: string, args: varargs[string, `$`]) {. frmt: string, args: varargs[string, `$`]) {.
raises: [Exception], raises: [Exception],
tags: [TimeEffect, WriteIOEffect, ReadIOEffect].} = tags: [TimeEffect, WriteIOEffect, ReadIOEffect].} =
## Override this method in custom loggers. Default implementation does ## Override this method in custom loggers. Default implementation does
## nothing. ## nothing.
discard discard
method log*(logger: ConsoleLogger, level: Level, method log*(logger: ConsoleLogger, level: Level,
frmt: string, args: varargs[string, `$`]) = frmt: string, args: varargs[string, `$`]) =
## Logs to the console using ``logger`` only. ## Logs to the console using ``logger`` only.
@ -127,14 +127,14 @@ method log*(logger: ConsoleLogger, level: Level,
writeln(stdout, LevelNames[level], " ", substituteLog(logger.fmtStr), writeln(stdout, LevelNames[level], " ", substituteLog(logger.fmtStr),
frmt % args) frmt % args)
method log*(logger: FileLogger, level: Level, method log*(logger: FileLogger, level: Level,
frmt: string, args: varargs[string, `$`]) = frmt: string, args: varargs[string, `$`]) =
## Logs to a file using ``logger`` only. ## Logs to a file using ``logger`` only.
if level >= logger.levelThreshold: if level >= logger.levelThreshold:
writeln(logger.f, LevelNames[level], " ", writeln(logger.f, LevelNames[level], " ",
substituteLog(logger.fmtStr), frmt % args) substituteLog(logger.fmtStr), frmt % args)
proc defaultFilename*(): string = proc defaultFilename*(): string =
## Returns the default filename for a logger. ## Returns the default filename for a logger.
var (path, name, ext) = splitFile(getAppFilename()) var (path, name, ext) = splitFile(getAppFilename())
result = changeFileExt(path / name, "log") result = changeFileExt(path / name, "log")
@ -145,10 +145,10 @@ proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr): Console
result.fmtStr = fmtStr result.fmtStr = fmtStr
result.levelThreshold = levelThreshold result.levelThreshold = levelThreshold
proc newFileLogger*(filename = defaultFilename(), proc newFileLogger*(filename = defaultFilename(),
mode: FileMode = fmAppend, mode: FileMode = fmAppend,
levelThreshold = lvlAll, levelThreshold = lvlAll,
fmtStr = defaultFmtStr): FileLogger = fmtStr = defaultFmtStr): FileLogger =
## Creates a new file logger. This logger logs to a file. ## Creates a new file logger. This logger logs to a file.
new(result) new(result)
result.levelThreshold = levelThreshold result.levelThreshold = levelThreshold
@ -170,14 +170,14 @@ proc countFiles(filename: string): int =
if kind == pcFile: if kind == pcFile:
let llfn = name & ext & ExtSep let llfn = name & ext & ExtSep
if path.extractFilename.startsWith(llfn): if path.extractFilename.startsWith(llfn):
let numS = path.extractFilename[llfn.len .. -1] let numS = path.extractFilename[llfn.len .. ^1]
try: try:
let num = parseInt(numS) let num = parseInt(numS)
if num > result: if num > result:
result = num result = num
except ValueError: discard except ValueError: discard
proc newRollingFileLogger*(filename = defaultFilename(), proc newRollingFileLogger*(filename = defaultFilename(),
mode: FileMode = fmReadWrite, mode: FileMode = fmReadWrite,
levelThreshold = lvlAll, levelThreshold = lvlAll,
fmtStr = defaultFmtStr, fmtStr = defaultFmtStr,
@ -192,9 +192,9 @@ proc newRollingFileLogger*(filename = defaultFilename(),
result.curLine = 0 result.curLine = 0
result.baseName = filename result.baseName = filename
result.baseMode = mode result.baseMode = mode
result.logFiles = countFiles(filename) result.logFiles = countFiles(filename)
if mode == fmAppend: if mode == fmAppend:
# We need to get a line count because we will be appending to the file. # We need to get a line count because we will be appending to the file.
result.curLine = countLogLines(result) result.curLine = countLogLines(result)
@ -206,7 +206,7 @@ proc rotate(logger: RollingFileLogger) =
moveFile(dir / (name & ext & srcSuff), moveFile(dir / (name & ext & srcSuff),
dir / (name & ext & ExtSep & $(i+1))) dir / (name & ext & ExtSep & $(i+1)))
method log*(logger: RollingFileLogger, level: Level, method log*(logger: RollingFileLogger, level: Level,
frmt: string, args: varargs[string, `$`]) = frmt: string, args: varargs[string, `$`]) =
## Logs to a file using rolling ``logger`` only. ## Logs to a file using rolling ``logger`` only.
if level >= logger.levelThreshold: if level >= logger.levelThreshold:
@ -216,7 +216,7 @@ method log*(logger: RollingFileLogger, level: Level,
logger.logFiles.inc logger.logFiles.inc
logger.curLine = 0 logger.curLine = 0
logger.f = open(logger.baseName, logger.baseMode) logger.f = open(logger.baseName, logger.baseMode)
writeln(logger.f, LevelNames[level], " ",substituteLog(logger.fmtStr), frmt % args) writeln(logger.f, LevelNames[level], " ",substituteLog(logger.fmtStr), frmt % args)
logger.curLine.inc logger.curLine.inc
@ -226,7 +226,7 @@ var level {.threadvar.}: Level ## global log filter
var handlers {.threadvar.}: seq[Logger] ## handlers with their own log levels var handlers {.threadvar.}: seq[Logger] ## handlers with their own log levels
proc logLoop(level: Level, frmt: string, args: varargs[string, `$`]) = proc logLoop(level: Level, frmt: string, args: varargs[string, `$`]) =
for logger in items(handlers): for logger in items(handlers):
if level >= logger.levelThreshold: if level >= logger.levelThreshold:
log(logger, level, frmt, args) log(logger, level, frmt, args)
@ -235,7 +235,7 @@ template log*(level: Level, frmt: string, args: varargs[string, `$`]) =
bind logLoop bind logLoop
bind `%` bind `%`
bind logging.level bind logging.level
if level >= logging.level: if level >= logging.level:
logLoop(level, frmt, args) logLoop(level, frmt, args)
@ -243,19 +243,19 @@ template debug*(frmt: string, args: varargs[string, `$`]) =
## Logs a debug message to all registered handlers. ## Logs a debug message to all registered handlers.
log(lvlDebug, frmt, args) log(lvlDebug, frmt, args)
template info*(frmt: string, args: varargs[string, `$`]) = template info*(frmt: string, args: varargs[string, `$`]) =
## Logs an info message to all registered handlers. ## Logs an info message to all registered handlers.
log(lvlInfo, frmt, args) log(lvlInfo, frmt, args)
template warn*(frmt: string, args: varargs[string, `$`]) = template warn*(frmt: string, args: varargs[string, `$`]) =
## Logs a warning message to all registered handlers. ## Logs a warning message to all registered handlers.
log(lvlWarn, frmt, args) log(lvlWarn, frmt, args)
template error*(frmt: string, args: varargs[string, `$`]) = template error*(frmt: string, args: varargs[string, `$`]) =
## Logs an error message to all registered handlers. ## Logs an error message to all registered handlers.
log(lvlError, frmt, args) log(lvlError, frmt, args)
template fatal*(frmt: string, args: varargs[string, `$`]) = template fatal*(frmt: string, args: varargs[string, `$`]) =
## Logs a fatal error message to all registered handlers. ## Logs a fatal error message to all registered handlers.
log(lvlFatal, frmt, args) log(lvlFatal, frmt, args)
@ -287,5 +287,5 @@ when isMainModule:
addHandler(rL) addHandler(rL)
for i in 0 .. 25: for i in 0 .. 25:
info("hello" & $i, []) info("hello" & $i, [])

View file

@ -222,13 +222,13 @@ type
set*{.magic: "Set".}[T] ## Generic type to construct bit sets. set*{.magic: "Set".}[T] ## Generic type to construct bit sets.
type type
Slice* {.final, pure.}[T] = object ## builtin slice type Slice*[T] = object ## builtin slice type
a*, b*: T ## the bounds a*, b*: T ## the bounds
when defined(nimalias): when defined(nimalias):
{.deprecated: [TSlice: Slice].} {.deprecated: [TSlice: Slice].}
proc `..`*[T](a, b: T): Slice[T] {.noSideEffect, inline.} = proc `..`*[T](a, b: T): Slice[T] {.noSideEffect, inline, magic: "DotDot".} =
## `slice`:idx: operator that constructs an interval ``[a, b]``, both `a` ## `slice`:idx: operator that constructs an interval ``[a, b]``, both `a`
## and `b` are inclusive. Slices can also be used in the set constructor ## and `b` are inclusive. Slices can also be used in the set constructor
## and in ordinal case statements, but then they are special-cased by the ## and in ordinal case statements, but then they are special-cased by the
@ -236,7 +236,7 @@ proc `..`*[T](a, b: T): Slice[T] {.noSideEffect, inline.} =
result.a = a result.a = a
result.b = b result.b = b
proc `..`*[T](b: T): Slice[T] {.noSideEffect, inline.} = proc `..`*[T](b: T): Slice[T] {.noSideEffect, inline, magic: "DotDot".} =
## `slice`:idx: operator that constructs an interval ``[default(T), b]`` ## `slice`:idx: operator that constructs an interval ``[default(T), b]``
result.b = b result.b = b
@ -2864,28 +2864,27 @@ template spliceImpl(s, a, L, b: expr): stmt {.immediate.} =
when hostOS != "standalone": when hostOS != "standalone":
proc `[]`*(s: string, x: Slice[int]): string {.inline.} = proc `[]`*(s: string, x: Slice[int]): string {.inline.} =
## slice operation for strings. Negative indexes are supported. ## slice operation for strings.
result = s.substr(x.a-|s, x.b-|s) result = s.substr(x.a, x.b)
proc `[]=`*(s: var string, x: Slice[int], b: string) = proc `[]=`*(s: var string, x: Slice[int], b: string) =
## slice assignment for strings. Negative indexes are supported. If ## slice assignment for strings. If
## ``b.len`` is not exactly the number of elements that are referred to ## ``b.len`` is not exactly the number of elements that are referred to
## by `x`, a `splice`:idx: is performed: ## by `x`, a `splice`:idx: is performed:
## ##
## .. code-block:: nim ## .. code-block:: nim
## var s = "abcdef" ## var s = "abcdef"
## s[1 .. -2] = "xyz" ## s[1 .. ^2] = "xyz"
## assert s == "axyzf" ## assert s == "axyzf"
var a = x.a-|s var a = x.a
var L = x.b-|s - a + 1 var L = x.b - a + 1
if L == b.len: if L == b.len:
for i in 0 .. <L: s[i+a] = b[i] for i in 0 .. <L: s[i+a] = b[i]
else: else:
spliceImpl(s, a, L, b) spliceImpl(s, a, L, b)
proc `[]`*[Idx, T](a: array[Idx, T], x: Slice[int]): seq[T] = proc `[]`*[Idx, T](a: array[Idx, T], x: Slice[int]): seq[T] =
## slice operation for arrays. Negative indexes are **not** supported ## slice operation for arrays.
## because the array might have negative bounds.
when low(a) < 0: when low(a) < 0:
{.error: "Slicing for arrays with negative indices is unsupported.".} {.error: "Slicing for arrays with negative indices is unsupported.".}
var L = x.b - x.a + 1 var L = x.b - x.a + 1
@ -2893,8 +2892,7 @@ proc `[]`*[Idx, T](a: array[Idx, T], x: Slice[int]): seq[T] =
for i in 0.. <L: result[i] = a[i + x.a] for i in 0.. <L: result[i] = a[i + x.a]
proc `[]=`*[Idx, T](a: var array[Idx, T], x: Slice[int], b: openArray[T]) = proc `[]=`*[Idx, T](a: var array[Idx, T], x: Slice[int], b: openArray[T]) =
## slice assignment for arrays. Negative indexes are **not** supported ## slice assignment for arrays.
## because the array might have negative bounds.
when low(a) < 0: when low(a) < 0:
{.error: "Slicing for arrays with negative indices is unsupported.".} {.error: "Slicing for arrays with negative indices is unsupported.".}
var L = x.b - x.a + 1 var L = x.b - x.a + 1
@ -2904,16 +2902,14 @@ proc `[]=`*[Idx, T](a: var array[Idx, T], x: Slice[int], b: openArray[T]) =
sysFatal(RangeError, "different lengths for slice assignment") sysFatal(RangeError, "different lengths for slice assignment")
proc `[]`*[Idx, T](a: array[Idx, T], x: Slice[Idx]): seq[T] = proc `[]`*[Idx, T](a: array[Idx, T], x: Slice[Idx]): seq[T] =
## slice operation for arrays. Negative indexes are **not** supported ## slice operation for arrays.
## because the array might have negative bounds.
var L = ord(x.b) - ord(x.a) + 1 var L = ord(x.b) - ord(x.a) + 1
newSeq(result, L) newSeq(result, L)
for i in 0.. <L: for i in 0.. <L:
result[i] = a[Idx(ord(x.a) + i)] result[i] = a[Idx(ord(x.a) + i)]
proc `[]=`*[Idx, T](a: var array[Idx, T], x: Slice[Idx], b: openArray[T]) = proc `[]=`*[Idx, T](a: var array[Idx, T], x: Slice[Idx], b: openArray[T]) =
## slice assignment for arrays. Negative indexes are **not** supported ## slice assignment for arrays.
## because the array might have negative bounds.
var L = ord(x.b) - ord(x.a) + 1 var L = ord(x.b) - ord(x.a) + 1
if L == b.len: if L == b.len:
for i in 0 .. <L: for i in 0 .. <L:
@ -2922,18 +2918,18 @@ proc `[]=`*[Idx, T](a: var array[Idx, T], x: Slice[Idx], b: openArray[T]) =
sysFatal(RangeError, "different lengths for slice assignment") sysFatal(RangeError, "different lengths for slice assignment")
proc `[]`*[T](s: seq[T], x: Slice[int]): seq[T] = proc `[]`*[T](s: seq[T], x: Slice[int]): seq[T] =
## slice operation for sequences. Negative indexes are supported. ## slice operation for sequences.
var a = x.a-|s var a = x.a
var L = x.b-|s - a + 1 var L = x.b - a + 1
newSeq(result, L) newSeq(result, L)
for i in 0.. <L: result[i] = s[i + a] for i in 0.. <L: result[i] = s[i + a]
proc `[]=`*[T](s: var seq[T], x: Slice[int], b: openArray[T]) = proc `[]=`*[T](s: var seq[T], x: Slice[int], b: openArray[T]) =
## slice assignment for sequences. Negative indexes are supported. If ## slice assignment for sequences. If
## ``b.len`` is not exactly the number of elements that are referred to ## ``b.len`` is not exactly the number of elements that are referred to
## by `x`, a `splice`:idx: is performed. ## by `x`, a `splice`:idx: is performed.
var a = x.a-|s var a = x.a
var L = x.b-|s - a + 1 var L = x.b - a + 1
if L == b.len: if L == b.len:
for i in 0 .. <L: s[i+a] = b[i] for i in 0 .. <L: s[i+a] = b[i]
else: else:
@ -3247,4 +3243,12 @@ proc procCall*(x: expr) {.magic: "ProcCall".} =
## procCall someMethod(a, b) ## procCall someMethod(a, b)
discard discard
proc `^`*(x: int): int {.noSideEffect, magic: "Roof".} =
## builtin `roof`:idx: operator that can be used for convenient array access.
## ``a[^x]`` is rewritten to ``a[a.len-x]``. However currently the ``a``
## expression must not have side effects for this to compile. Note that since
## this is a builtin, it automatically works for all kinds of
## overloaded ``[]`` or ``[]=`` accessors.
discard
{.pop.} #{.push warning[GcMem]: off.} {.pop.} #{.push warning[GcMem]: off.}

View file

@ -165,7 +165,7 @@ proc initGC() =
init(gch.tempStack) init(gch.tempStack)
init(gch.additionalRoots) init(gch.additionalRoots)
when withBitvectors: when withBitvectors:
Init(gch.allocated) init(gch.allocated)
init(gch.marked) init(gch.marked)
var var

36
tests/array/troof1.nim Normal file
View file

@ -0,0 +1,36 @@
discard """
output: '''@[2, 3, 4]321
9.0 4.0
(a: 1.0, b: 2.0, c: 8.0)2.0'''
"""
proc foo[T](x, y: T): T = x
var a = @[1, 2, 3, 4]
var b: array[3, array[2, float]] = [[1.0,2], [3.0,4], [8.0,9]]
echo a[1.. ^1], a[^2], a[^3], a[^4]
echo b[^1][^1], " ", (b[^2]).foo(b[^1])[^1]
type
MyArray = object
a, b, c: float
var
ma = MyArray(a: 1.0, b: 2.0, c: 3.0)
proc len(x: MyArray): int = 3
proc `[]=`(x: var MyArray; idx: range[0..2]; val: float) =
case idx
of 0: x.a = val
of 1: x.b = val
of 2: x.c = val
proc `[]`(x: var MyArray; idx: range[0..2]): float =
case idx
of 0: result = x.a
of 1: result = x.b
of 2: result = x.c
ma[^1] = 8.0
echo ma, ma[^2]

10
tests/array/troof2.nim Normal file
View file

@ -0,0 +1,10 @@
discard """
errormsg: "invalid context for '^' as 'foo()' has side effects"
line: "9"
"""
proc foo(): seq[int] =
echo "ha"
let f = foo()[^1]

8
tests/array/troof3.nim Normal file
View file

@ -0,0 +1,8 @@
discard """
errormsg: "invalid context for '^' as len!=high+1 for 'a'"
line: "8"
"""
var a: array[1..3, string]
echo a[^1]

37
tests/array/troof4.nim Normal file
View file

@ -0,0 +1,37 @@
discard """
errormsg: "no surrounding array access context for '^'"
line: "37"
"""
proc foo[T](x, y: T): T = x
var a = @[1, 2, 3, 4]
var b: array[3, array[2, float]] = [[1.0,2], [3.0,4], [8.0,9]]
echo a[1.. ^1], a[^2], a[^3], a[^4]
echo b[^1][^1], " ", (b[^2]).foo(b[^1])[^1]
type
MyArray = object
a, b, c: float
var
ma = MyArray(a: 1.0, b: 2.0, c: 3.0)
proc len(x: MyArray): int = 3
proc `[]=`(x: var MyArray; idx: range[0..2]; val: float) =
case idx
of 0: x.a = val
of 1: x.b = val
of 2: x.c = val
proc `[]`(x: var MyArray; idx: range[0..2]): float =
case idx
of 0: result = x.a
of 1: result = x.b
of 2: result = x.c
ma[^1] = 8.0
echo ma, ma[^2]
echo(^1)

View file

@ -7,6 +7,6 @@ discard """
proc test: string = proc test: string =
result = "blah" result = "blah"
result[1 .. -1] result[1 .. ^1]
echo test() echo test()

View file

@ -27,7 +27,7 @@ proc refExpr(exprNode: NimNode): string {.compileTime.} =
"expr" & $(exprNodes.len - 1) "expr" & $(exprNodes.len - 1)
proc derefExpr(exprRef: string): NimNode {.compileTime.} = proc derefExpr(exprRef: string): NimNode {.compileTime.} =
exprNodes[parseInt(exprRef[4 .. -1])] exprNodes[parseInt(exprRef[4 .. ^1])]
#=============================================================================== #===============================================================================
# Define a type that allows a callable expression to be mapped onto elements # Define a type that allows a callable expression to be mapped onto elements

View file

@ -0,0 +1,49 @@
# tests to see if a symbol returned from macros.getType() can
# be used as a type
import macros
macro testTypesym (t:stmt): expr =
var ty = t.getType
if ty.typekind == ntyTypedesc:
# skip typedesc get to the real type
ty = ty[1].getType
if ty.kind == nnkSym: return ty
assert ty.kind == nnkBracketExpr
assert ty[0].kind == nnkSym
result = ty[0]
return
type TestFN = proc(a,b:int):int
var iii: testTypesym(TestFN)
static: assert iii is TestFN
proc foo11 : testTypesym(void) =
echo "HI!"
static: assert foo11 is proc():void
var sss: testTypesym(seq[int])
static: assert sss is seq[int]
# very nice :>
static: assert array[2,int] is testTypesym(array[2,int])
static: assert(ref int is testTypesym(ref int))
static: assert(void is testTypesym(void))
macro tts2 (t:stmt, idx:int): expr =
var ty = t.getType
if ty.typekind == ntyTypedesc:
# skip typedesc get to the real type
ty = ty[1].getType
if ty.kind == nnkSym: return ty
assert ty.kind == nnkBracketExpr
return ty[idx.intval.int]
type TestFN2 = proc(a:int,b:float):string
static:
assert(tts2(TestFN2, 0) is TestFN2)
assert(tts2(TestFN2, 1) is string)
assert(tts2(TestFN2, 2) is int)
assert(tts2(TestFN2, 3) is float)

View file

@ -36,7 +36,7 @@ echo()
var myseq = @[1, 2, 3, 4, 5, 6] var myseq = @[1, 2, 3, 4, 5, 6]
myseq[0..2] = myseq[-3.. -1] myseq[0..2] = myseq[^3 .. ^1]
for x in items(myseq): stdout.write(x) for x in items(myseq): stdout.write(x)
echo() echo()
@ -46,7 +46,7 @@ echo mystr
mystr[4..4] = "u" mystr[4..4] = "u"
# test full replacement # test full replacement
mystr[.. -2] = "egerichtet" mystr[.. ^2] = "egerichtet"
echo mystr echo mystr
@ -54,6 +54,6 @@ mystr[0..2] = "ve"
echo mystr echo mystr
var s = "abcdef" var s = "abcdef"
s[1 .. -2] = "xyz" s[1 .. ^2] = "xyz"
assert s == "axyzf" assert s == "axyzf"

View file

@ -0,0 +1,14 @@
discard """
errormsg: "field not initialized: bar"
line: "13"
"""
# bug #2355
type
Foo = object
foo: string not nil
bar: string not nil
# Create instance without initializaing the `bar` field
var f = Foo(foo: "foo")
echo f.bar.isNil # true

View file

@ -22,35 +22,35 @@ proc delNimCache() =
removeDir(nimcacheDir) removeDir(nimcacheDir)
except OSError: except OSError:
echo "[Warning] could not delete: ", nimcacheDir echo "[Warning] could not delete: ", nimcacheDir
proc runRodFiles(r: var TResults, cat: Category, options: string) = proc runRodFiles(r: var TResults, cat: Category, options: string) =
template test(filename: expr): stmt = template test(filename: expr): stmt =
testSpec r, makeTest(rodfilesDir / filename, options, cat, actionRun) testSpec r, makeTest(rodfilesDir / filename, options, cat, actionRun)
delNimCache() delNimCache()
# test basic recompilation scheme: # test basic recompilation scheme:
test "hallo" test "hallo"
test "hallo" test "hallo"
# test incremental type information: # test incremental type information:
test "hallo2" test "hallo2"
delNimCache() delNimCache()
# test type converters: # test type converters:
test "aconv" test "aconv"
test "bconv" test "bconv"
delNimCache() delNimCache()
# test G, A, B example from the documentation; test init sections: # test G, A, B example from the documentation; test init sections:
test "deada" test "deada"
test "deada2" test "deada2"
delNimCache() delNimCache()
# test method generation: # test method generation:
test "bmethods" test "bmethods"
test "bmethods2" test "bmethods2"
delNimCache() delNimCache()
# test generics: # test generics:
test "tgeneric1" test "tgeneric1"
test "tgeneric2" test "tgeneric2"
@ -79,8 +79,8 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) =
options & " --app:lib -d:createNimRtl", cat) options & " --app:lib -d:createNimRtl", cat)
testSpec c, makeTest("tests/dll/server.nim", testSpec c, makeTest("tests/dll/server.nim",
options & " --app:lib -d:useNimRtl", cat) options & " --app:lib -d:useNimRtl", cat)
when defined(Windows): when defined(Windows):
# windows looks in the dir of the exe (yay!): # windows looks in the dir of the exe (yay!):
var nimrtlDll = DynlibFormat % "nimrtl" var nimrtlDll = DynlibFormat % "nimrtl"
safeCopyFile("lib" / nimrtlDll, "tests/dll" / nimrtlDll) safeCopyFile("lib" / nimrtlDll, "tests/dll" / nimrtlDll)
@ -91,14 +91,14 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) =
putEnv("LD_LIBRARY_PATH", "lib:" & libpath) putEnv("LD_LIBRARY_PATH", "lib:" & libpath)
var serverDll = DynlibFormat % "server" var serverDll = DynlibFormat % "server"
safeCopyFile("tests/dll" / serverDll, "lib" / serverDll) safeCopyFile("tests/dll" / serverDll, "lib" / serverDll)
testSpec r, makeTest("tests/dll/client.nim", options & " -d:useNimRtl", testSpec r, makeTest("tests/dll/client.nim", options & " -d:useNimRtl",
cat, actionRun) cat, actionRun)
proc dllTests(r: var TResults, cat: Category, options: string) = proc dllTests(r: var TResults, cat: Category, options: string) =
# dummy compile result: # dummy compile result:
var c = initResults() var c = initResults()
runBasicDLLTest c, r, cat, options runBasicDLLTest c, r, cat, options
runBasicDLLTest c, r, cat, options & " -d:release" runBasicDLLTest c, r, cat, options & " -d:release"
runBasicDLLTest c, r, cat, options & " --gc:boehm" runBasicDLLTest c, r, cat, options & " --gc:boehm"
@ -134,7 +134,7 @@ proc gcTests(r: var TResults, cat: Category, options: string) =
test "cycleleak" test "cycleleak"
test "closureleak" test "closureleak"
testWithoutMs "refarrayleak" testWithoutMs "refarrayleak"
test "stackrefleak" test "stackrefleak"
test "cyclecollector" test "cyclecollector"
@ -147,7 +147,7 @@ proc threadTests(r: var TResults, cat: Category, options: string) =
" -d:release", cat, actionRun) " -d:release", cat, actionRun)
testSpec r, makeTest("tests/threads" / filename, options & testSpec r, makeTest("tests/threads" / filename, options &
" --tlsEmulation:on", cat, actionRun) " --tlsEmulation:on", cat, actionRun)
test "tactors" test "tactors"
test "tactors2" test "tactors2"
test "threadex" test "threadex"
@ -182,7 +182,7 @@ proc jsTests(r: var TResults, cat: Category, options: string) =
actionRun, targetJS) actionRun, targetJS)
testSpec r, makeTest(filename, options & " -d:nodejs -d:release", cat, testSpec r, makeTest(filename, options & " -d:nodejs -d:release", cat,
actionRun, targetJS) actionRun, targetJS)
for t in os.walkFiles("tests/js/t*.nim"): for t in os.walkFiles("tests/js/t*.nim"):
test(t) test(t)
for testfile in ["exception/texceptions", "exception/texcpt1", for testfile in ["exception/texceptions", "exception/texcpt1",
@ -199,13 +199,13 @@ proc jsTests(r: var TResults, cat: Category, options: string) =
proc findMainFile(dir: string): string = proc findMainFile(dir: string): string =
# finds the file belonging to ".nim.cfg"; if there is no such file # finds the file belonging to ".nim.cfg"; if there is no such file
# it returns the some ".nim" file if there is only one: # it returns the some ".nim" file if there is only one:
const cfgExt = ".nim.cfg" const cfgExt = ".nim.cfg"
result = "" result = ""
var nimFiles = 0 var nimFiles = 0
for kind, file in os.walkDir(dir): for kind, file in os.walkDir(dir):
if kind == pcFile: if kind == pcFile:
if file.endsWith(cfgExt): return file[.. -(cfgExt.len+1)] & ".nim" if file.endsWith(cfgExt): return file[.. ^(cfgExt.len+1)] & ".nim"
elif file.endsWith(".nim"): elif file.endsWith(".nim"):
if result.len == 0: result = file if result.len == 0: result = file
inc nimFiles inc nimFiles
@ -236,7 +236,7 @@ type PackageFilter = enum
pfExtraOnly pfExtraOnly
pfAll pfAll
let let
nimbleExe = findExe("nimble") nimbleExe = findExe("nimble")
nimbleDir = getHomeDir() / ".nimble" nimbleDir = getHomeDir() / ".nimble"
packageDir = nimbleDir / "pkgs" packageDir = nimbleDir / "pkgs"

View file

@ -59,7 +59,7 @@ proc callCompiler(cmdTemplate, filename, options: string,
target: TTarget): TSpec = target: TTarget): TSpec =
let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target], let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target],
"options", options, "file", filename.quoteShell]) "options", options, "file", filename.quoteShell])
var p = startProcess(command=c[0], args=c[1.. -1], var p = startProcess(command=c[0], args=c[1.. ^1],
options={poStdErrToStdOut, poUseShell}) options={poStdErrToStdOut, poUseShell})
let outp = p.outputStream let outp = p.outputStream
var suc = "" var suc = ""
@ -284,7 +284,7 @@ proc main() =
let testsDir = "tests" & DirSep let testsDir = "tests" & DirSep
for kind, dir in walkDir(testsDir): for kind, dir in walkDir(testsDir):
assert testsDir.startsWith(testsDir) assert testsDir.startsWith(testsDir)
let cat = dir[testsDir.len .. -1] let cat = dir[testsDir.len .. ^1]
if kind == pcDir and cat notin ["testament", "testdata", "nimcache"]: if kind == pcDir and cat notin ["testament", "testdata", "nimcache"]:
processCategory(r, Category(cat), p.cmdLineRest.string) processCategory(r, Category(cat), p.cmdLineRest.string)
for a in AdditionalCategories: for a in AdditionalCategories:

View file

@ -1,20 +1,18 @@
version 0.10.4 version 0.10.4
============== ==============
- improve the parser; deal with echo $foo gotcha
- improve GC-unsafety warnings - improve GC-unsafety warnings
- make 'nil' work for 'add' and 'len' - make 'nil' work for 'add' and 'len'
- add "all threads are blocked" detection to 'spawn'
- overloading of '=' - overloading of '='
- disallow negative indexing
version 1.0 version 1.0
=========== ===========
- remove echo $foo gotcha
- add "all threads are blocked" detection to 'spawn'
- figure out why C++ bootstrapping is so much slower - figure out why C++ bootstrapping is so much slower
- nimsuggest: auto-completion needs to work in 'class' macros - nimsuggest: auto-completion needs to work in 'class' macros
- improve the docs for inheritance
- The bitwise 'not' operator will be renamed to 'bnot' to - The bitwise 'not' operator will be renamed to 'bnot' to
prevent 'not 4 == 5' from compiling. -> requires 'mixin' annotation for procs! prevent 'not 4 == 5' from compiling. -> requires 'mixin' annotation for procs!
- iterators always require a return type - iterators always require a return type

View file

@ -42,6 +42,33 @@ News
structure; for immediate macro parameters ``nkCall('addr', 'x')`` is structure; for immediate macro parameters ``nkCall('addr', 'x')`` is
produced instead of ``nkAddr('x')``. produced instead of ``nkAddr('x')``.
- ``concept`` is now a keyword and is used instead of ``generic``. - ``concept`` is now a keyword and is used instead of ``generic``.
- The ``inc``, ``dec``, ``+=``, ``-=`` builtins now produce OverflowError
exceptions. This means code like the following:
.. code-block:: nim
var x = low(T)
while x <= high(T):
echo x
inc x
Needs to be replaced by something like this:
.. code-block:: nim
var x = low(T).int
while x <= high(T).int:
echo x.T
inc x
- **Negative indexing for slicing does not work anymore!** Instead
of ``a[0.. -1]`` you can
use ``a[0.. ^1]``. This also works with accessing a single
element ``a[^1]``. Note that we cannot detect this reliably as it is
determined at **runtime** whether negative indexing is used!
``a[0.. -1]`` now produces the empty string/sequence.
- The compiler now warns about code like ``foo +=1`` which uses inconsistent
spacing around binary operators. Later versions of the language will parse
these as unary operators instead so that ``echo $foo`` finally can do what
people expect it to do.
Language Additions Language Additions
@ -84,6 +111,9 @@ News
varOrConst(x) # "var" varOrConst(x) # "var"
varOrConst(45) # "const" varOrConst(45) # "const"
- Array and seq indexing can now use the builtin ``^`` operator to access
things from backwards: ``a[^1]`` is like Python's ``a[-1]``.
Library additions Library additions
----------------- -----------------