mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-02 19:22:40 +00:00
Merge branch 'devel' into jpoirier-realtimeGCTest
This commit is contained in:
@@ -529,7 +529,7 @@ type
|
||||
TMagic* = enum # symbols that require compiler magic:
|
||||
mNone,
|
||||
mDefined, mDefinedInScope, mCompiles,
|
||||
mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf,
|
||||
mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof,
|
||||
mEcho, mShallowCopy, mSlurp, mStaticExec,
|
||||
mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
|
||||
mUnaryLt, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray,
|
||||
@@ -557,6 +557,7 @@ type
|
||||
mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr,
|
||||
mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet,
|
||||
mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mSlice,
|
||||
mDotDot, # this one is only necessary to give nice compile time warnings
|
||||
mFields, mFieldPairs, mOmpParFor,
|
||||
mAppendStrCh, mAppendStrStr, mAppendSeqElem,
|
||||
mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq,
|
||||
|
||||
@@ -1749,6 +1749,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
initLocExpr(p, x, a)
|
||||
initLocExpr(p, e.sons[2], b)
|
||||
genDeepCopy(p, a, b)
|
||||
of mDotDot: genCall(p, e, d)
|
||||
else: internalError(e.info, "genMagicExpr: " & $op)
|
||||
|
||||
proc genConstExpr(p: BProc, n: PNode): PRope
|
||||
|
||||
@@ -94,7 +94,7 @@ proc writeIntSet(a: IntSet, s: var string) =
|
||||
encodeVInt(x, s)
|
||||
inc i
|
||||
s.add('}')
|
||||
|
||||
|
||||
proc genMergeInfo*(m: BModule): PRope =
|
||||
if optSymbolFiles notin gGlobalOptions: return nil
|
||||
var s = "/*\tNIM_merge_INFO:"
|
||||
@@ -113,7 +113,7 @@ proc genMergeInfo*(m: BModule): PRope =
|
||||
s.add("*/")
|
||||
result = s.toRope
|
||||
|
||||
template `^`(pos: expr): expr = L.buf[pos]
|
||||
template `^`(pos: int): expr = L.buf[pos]
|
||||
|
||||
proc skipWhite(L: var TBaseLexer) =
|
||||
var pos = L.bufpos
|
||||
@@ -132,7 +132,7 @@ proc skipUntilCmd(L: var TBaseLexer) =
|
||||
of CR: pos = nimlexbase.handleCR(L, pos)
|
||||
of LF: pos = nimlexbase.handleLF(L, pos)
|
||||
of '\0': break
|
||||
of '/':
|
||||
of '/':
|
||||
if ^(pos+1) == '*' and ^(pos+2) == '\t':
|
||||
inc pos, 3
|
||||
break
|
||||
@@ -145,7 +145,7 @@ proc atEndMark(buf: cstring, pos: int): bool =
|
||||
while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
|
||||
result = s == NimMergeEndMark.len
|
||||
|
||||
proc readVerbatimSection(L: var TBaseLexer): PRope =
|
||||
proc readVerbatimSection(L: var TBaseLexer): PRope =
|
||||
var pos = L.bufpos
|
||||
var buf = L.buf
|
||||
var r = newStringOfCap(30_000)
|
||||
@@ -162,7 +162,7 @@ proc readVerbatimSection(L: var TBaseLexer): PRope =
|
||||
of '\0':
|
||||
internalError("ccgmerge: expected: " & NimMergeEndMark)
|
||||
break
|
||||
else:
|
||||
else:
|
||||
if atEndMark(buf, pos):
|
||||
inc pos, NimMergeEndMark.len
|
||||
break
|
||||
@@ -181,7 +181,7 @@ proc readKey(L: var TBaseLexer, result: var string) =
|
||||
if buf[pos] != ':': internalError("ccgmerge: ':' expected")
|
||||
L.bufpos = pos + 1 # skip ':'
|
||||
|
||||
proc newFakeType(id: int): PType =
|
||||
proc newFakeType(id: int): PType =
|
||||
new(result)
|
||||
result.id = id
|
||||
|
||||
@@ -227,8 +227,8 @@ proc processMergeInfo(L: var TBaseLexer, m: BModule) =
|
||||
|
||||
when not defined(nimhygiene):
|
||||
{.pragma: inject.}
|
||||
|
||||
template withCFile(cfilename: string, body: stmt) {.immediate.} =
|
||||
|
||||
template withCFile(cfilename: string, body: stmt) {.immediate.} =
|
||||
var s = llStreamOpen(cfilename, fmRead)
|
||||
if s == nil: return
|
||||
var L {.inject.}: TBaseLexer
|
||||
@@ -239,7 +239,7 @@ template withCFile(cfilename: string, body: stmt) {.immediate.} =
|
||||
if ^L.bufpos == '\0': break
|
||||
body
|
||||
closeBaseLexer(L)
|
||||
|
||||
|
||||
proc readMergeInfo*(cfilename: string, m: BModule) =
|
||||
## reads the merge meta information into `m`.
|
||||
withCFile(cfilename):
|
||||
@@ -257,7 +257,7 @@ proc readMergeSections(cfilename: string, m: var TMergeSections) =
|
||||
## reads the merge sections into `m`.
|
||||
withCFile(cfilename):
|
||||
readKey(L, k)
|
||||
if k == "NIM_merge_INFO":
|
||||
if k == "NIM_merge_INFO":
|
||||
discard
|
||||
elif ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
|
||||
inc(L.bufpos, 2)
|
||||
@@ -283,7 +283,7 @@ proc mergeRequired*(m: BModule): bool =
|
||||
#echo "not empty: ", i, " ", ropeToStr(m.s[i])
|
||||
return true
|
||||
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])
|
||||
return true
|
||||
|
||||
@@ -291,7 +291,7 @@ proc mergeFiles*(cfilename: string, m: BModule) =
|
||||
## merges the C file with the old version on hard disc.
|
||||
var old: TMergeSections
|
||||
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):
|
||||
m.s[i] = con(old.f[i], m.s[i])
|
||||
for i in low(TCProcSection)..high(TCProcSection):
|
||||
|
||||
@@ -131,11 +131,10 @@ proc semNodeKindConstraints*(p: PNode): PNode =
|
||||
result.strVal.add(ppEof)
|
||||
|
||||
type
|
||||
TSideEffectAnalysis = enum
|
||||
TSideEffectAnalysis* = enum
|
||||
seUnknown, seSideEffect, seNoSideEffect
|
||||
|
||||
proc checkForSideEffects(n: PNode): TSideEffectAnalysis =
|
||||
# XXX is 'raise' a side effect?
|
||||
proc checkForSideEffects*(n: PNode): TSideEffectAnalysis =
|
||||
case n.kind
|
||||
of nkCallKinds:
|
||||
# only calls can produce side effects:
|
||||
@@ -162,6 +161,8 @@ proc checkForSideEffects(n: PNode): TSideEffectAnalysis =
|
||||
# an atom cannot produce a side effect:
|
||||
result = seNoSideEffect
|
||||
else:
|
||||
# assume no side effect:
|
||||
result = seNoSideEffect
|
||||
for i in 0 .. <n.len:
|
||||
let ret = checkForSideEffects(n.sons[i])
|
||||
if ret == seSideEffect: return ret
|
||||
|
||||
@@ -241,9 +241,15 @@ proc isOperator(tok: TToken): bool =
|
||||
|
||||
proc isUnary(p: TParser): bool =
|
||||
## Check if the current parser token is a unary operator
|
||||
p.strongSpaces and p.tok.tokType in {tkOpr, tkDotDot} and
|
||||
p.tok.strongSpaceB == 0 and
|
||||
p.tok.strongSpaceA > 0
|
||||
if p.tok.tokType in {tkOpr, tkDotDot} and
|
||||
p.tok.strongSpaceB == 0 and
|
||||
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.} =
|
||||
## Check if the current parser token is a binary operator.
|
||||
|
||||
@@ -201,7 +201,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
|
||||
|
||||
elif nfDotSetter in n.flags:
|
||||
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)
|
||||
n.sons[0..1] = [callOp, n[1], calleeName]
|
||||
orig.sons[0..1] = [callOp, orig[1], calleeName]
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
|
||||
## This module contains the data structures for the semantic checking phase.
|
||||
|
||||
import
|
||||
import
|
||||
strutils, lists, intsets, options, lexer, ast, astalgo, trees, treetab,
|
||||
wordrecg,
|
||||
ropes, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math,
|
||||
wordrecg,
|
||||
ropes, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math,
|
||||
magicsys, nversion, nimsets, parser, times, passes, rodread, vmdef
|
||||
|
||||
type
|
||||
type
|
||||
TOptionEntry* = object of lists.TListEntry # entries to put on a
|
||||
# stack for pragma parsing
|
||||
options*: TOptions
|
||||
@@ -26,7 +26,7 @@ type
|
||||
|
||||
POptionEntry* = ref TOptionEntry
|
||||
PProcCon* = ref TProcCon
|
||||
TProcCon*{.final.} = object # procedure context; also used for top-level
|
||||
TProcCon* = object # procedure context; also used for top-level
|
||||
# statements
|
||||
owner*: PSym # the symbol this context belongs to
|
||||
resultSym*: PSym # the result symbol (if we are in a proc)
|
||||
@@ -36,12 +36,13 @@ type
|
||||
# in standalone ``except`` and ``finally``
|
||||
next*: PProcCon # used for stacking procedure contexts
|
||||
wasForwarded*: bool # whether the current proc has a separate header
|
||||
|
||||
bracketExpr*: PNode # current bracket expression (for ^ support)
|
||||
|
||||
TInstantiationPair* = object
|
||||
genericSym*: PSym
|
||||
inst*: PInstantiation
|
||||
|
||||
TExprFlag* = enum
|
||||
TExprFlag* = enum
|
||||
efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType,
|
||||
efAllowDestructor, efWantValue, efOperand, efNoSemCheck
|
||||
TExprFlags* = set[TExprFlag]
|
||||
@@ -57,7 +58,7 @@ type
|
||||
# this is used so that generic instantiations
|
||||
# can access private object fields
|
||||
instCounter*: int # to prevent endless instantiations
|
||||
|
||||
|
||||
ambiguousSymbols*: IntSet # ids of all ambiguous symbols (cannot
|
||||
# store this info in the syms themselves!)
|
||||
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;
|
||||
info: TLineInfo): PSym {.nimcall.}
|
||||
|
||||
|
||||
|
||||
proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
|
||||
result.genericSym = s
|
||||
result.inst = inst
|
||||
@@ -127,7 +128,7 @@ proc popOwner*()
|
||||
|
||||
var gOwners*: seq[PSym] = @[]
|
||||
|
||||
proc getCurrOwner(): PSym =
|
||||
proc getCurrOwner(): PSym =
|
||||
# owner stack (used for initializing the
|
||||
# owner field of syms)
|
||||
# the documentation comment always gets
|
||||
@@ -135,19 +136,19 @@ proc getCurrOwner(): PSym =
|
||||
# BUGFIX: global array is needed!
|
||||
result = gOwners[high(gOwners)]
|
||||
|
||||
proc pushOwner(owner: PSym) =
|
||||
proc pushOwner(owner: PSym) =
|
||||
add(gOwners, owner)
|
||||
|
||||
proc popOwner() =
|
||||
proc popOwner() =
|
||||
var length = len(gOwners)
|
||||
if length > 0: setLen(gOwners, length - 1)
|
||||
else: internalError("popOwner")
|
||||
|
||||
proc lastOptionEntry(c: PContext): POptionEntry =
|
||||
proc lastOptionEntry(c: PContext): POptionEntry =
|
||||
result = POptionEntry(c.optionStack.tail)
|
||||
|
||||
proc pushProcCon*(c: PContext, owner: PSym) {.inline.} =
|
||||
if owner == nil:
|
||||
proc pushProcCon*(c: PContext, owner: PSym) {.inline.} =
|
||||
if owner == nil:
|
||||
internalError("owner is nil")
|
||||
return
|
||||
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 newOptionEntry(): POptionEntry =
|
||||
proc newOptionEntry(): POptionEntry =
|
||||
new(result)
|
||||
result.options = gOptions
|
||||
result.defaultCC = ccDefault
|
||||
@@ -182,8 +183,8 @@ proc newContext(module: PSym): PContext =
|
||||
|
||||
proc inclSym(sq: var TSymSeq, s: PSym) =
|
||||
var L = len(sq)
|
||||
for i in countup(0, L - 1):
|
||||
if sq[i].id == s.id: return
|
||||
for i in countup(0, L - 1):
|
||||
if sq[i].id == s.id: return
|
||||
setLen(sq, L + 1)
|
||||
sq[L] = s
|
||||
|
||||
@@ -193,20 +194,20 @@ proc addConverter*(c: PContext, conv: PSym) =
|
||||
proc addPattern*(c: PContext, p: PSym) =
|
||||
inclSym(c.patterns, p)
|
||||
|
||||
proc newLib(kind: TLibKind): PLib =
|
||||
proc newLib(kind: TLibKind): PLib =
|
||||
new(result)
|
||||
result.kind = kind #initObjectSet(result.syms)
|
||||
|
||||
|
||||
proc addToLib(lib: PLib, sym: PSym) =
|
||||
#if sym.annex != nil and not isGenericRoutine(sym):
|
||||
# LocalError(sym.info, errInvalidPragma)
|
||||
sym.annex = lib
|
||||
|
||||
proc makePtrType(c: PContext, baseType: PType): PType =
|
||||
proc makePtrType(c: PContext, baseType: PType): PType =
|
||||
result = newTypeS(tyPtr, c)
|
||||
addSonSkipIntLit(result, baseType.assertNotNil)
|
||||
|
||||
proc makeVarType(c: PContext, baseType: PType): PType =
|
||||
proc makeVarType(c: PContext, baseType: PType): PType =
|
||||
result = newTypeS(tyVar, c)
|
||||
addSonSkipIntLit(result, baseType.assertNotNil)
|
||||
|
||||
@@ -286,7 +287,7 @@ proc errorNode*(c: PContext, n: PNode): PNode =
|
||||
result = newNodeI(nkEmpty, n.info)
|
||||
result.typ = errorType(c)
|
||||
|
||||
proc fillTypeS(dest: PType, kind: TTypeKind, c: PContext) =
|
||||
proc fillTypeS(dest: PType, kind: TTypeKind, c: PContext) =
|
||||
dest.kind = kind
|
||||
dest.owner = getCurrOwner()
|
||||
dest.size = - 1
|
||||
@@ -311,13 +312,13 @@ proc illFormedAst*(n: PNode) =
|
||||
proc illFormedAstLocal*(n: PNode) =
|
||||
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)
|
||||
|
||||
proc checkMinSonsLen*(n: PNode, length: int) =
|
||||
|
||||
proc checkMinSonsLen*(n: PNode, length: int) =
|
||||
if sonsLen(n) < length: illFormedAst(n)
|
||||
|
||||
proc isTopLevel*(c: PContext): bool {.inline.} =
|
||||
proc isTopLevel*(c: PContext): bool {.inline.} =
|
||||
result = c.currentScope.depthLevel <= 2
|
||||
|
||||
proc experimentalMode*(c: PContext): bool {.inline.} =
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# included from sem.nim
|
||||
|
||||
# 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)
|
||||
# 2) DestructorIsTrivial: completed the analysis before and determined
|
||||
# that the type has a trivial destructor
|
||||
@@ -41,7 +41,7 @@ proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
|
||||
if t.kind != tyGenericBody:
|
||||
localError(n.info, errDestructorNotGenericEnough)
|
||||
return
|
||||
|
||||
|
||||
t.destructor = s
|
||||
# automatically insert calls to base classes' destructors
|
||||
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]]))
|
||||
for i in countup(1, n.len - 1):
|
||||
# of A, B:
|
||||
var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2])
|
||||
|
||||
let stmt = destroyFieldOrFields(c, n[i].lastSon, holder)
|
||||
let ni = n[i]
|
||||
var caseBranch = newNode(ni.kind, ni.info, ni.sons[0..ni.len-2])
|
||||
|
||||
let stmt = destroyFieldOrFields(c, ni.lastSon, holder)
|
||||
if stmt == nil:
|
||||
caseBranch.addSon(newNode(nkStmtList, n[i].info, @[]))
|
||||
caseBranch.addSon(newNode(nkStmtList, ni.info, @[]))
|
||||
else:
|
||||
caseBranch.addSon(stmt)
|
||||
nonTrivialFields += stmt.len
|
||||
|
||||
|
||||
result.addSon(caseBranch)
|
||||
|
||||
|
||||
# maybe no fields were destroyed?
|
||||
if nonTrivialFields == 0:
|
||||
result = nil
|
||||
@@ -107,7 +108,7 @@ proc destroyFieldOrFields(c: PContext, field: PNode, holder: PNode): PNode =
|
||||
proc generateDestructor(c: PContext, t: PType): PNode =
|
||||
## generate a destructor for a user-defined object or tuple type
|
||||
## returns nil if the destructor turns out to be trivial
|
||||
|
||||
|
||||
# XXX: This may be true for some C-imported types such as
|
||||
# Tposix_spawnattr
|
||||
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 =
|
||||
# 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.
|
||||
# The destructor is either user-defined or automatically
|
||||
# generated by the compiler in a member-wise fashion.
|
||||
var t = skipTypes(typ, {tyConst, tyMutable}).skipGenericAlias
|
||||
let typeHoldingUserDefinition = if t.kind == tyGenericInst: t.base else: t
|
||||
|
||||
|
||||
if typeHoldingUserDefinition.destructor != nil:
|
||||
# XXX: This is not entirely correct for recursive types, but we need
|
||||
# it temporarily to hide the "destroy is already defined" problem
|
||||
@@ -135,7 +136,7 @@ proc instantiateDestructor(c: PContext, typ: PType): PType =
|
||||
return typeHoldingUserDefinition
|
||||
else:
|
||||
return nil
|
||||
|
||||
|
||||
t = t.skipTypes({tyGenericInst})
|
||||
case t.kind
|
||||
of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs:
|
||||
@@ -200,16 +201,16 @@ proc insertDestructors(c: PContext,
|
||||
varId = varSection[j][0]
|
||||
varTyp = varId.sym.typ
|
||||
info = varId.info
|
||||
|
||||
|
||||
if varTyp == nil or sfGlobal in varId.sym.flags: continue
|
||||
let destructableT = instantiateDestructor(c, varTyp)
|
||||
|
||||
|
||||
if destructableT != nil:
|
||||
var tryStmt = newNodeI(nkTryStmt, info)
|
||||
|
||||
if j < totalVars - 1:
|
||||
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)
|
||||
if outer != nil:
|
||||
tryStmt.addSon(outer)
|
||||
@@ -221,7 +222,7 @@ proc insertDestructors(c: PContext,
|
||||
else:
|
||||
result.inner = newNodeI(nkStmtList, info)
|
||||
tryStmt.addSon(result.inner)
|
||||
|
||||
|
||||
tryStmt.addSon(
|
||||
newNode(nkFinally, info, @[
|
||||
semStmt(c, newNode(nkCall, info, @[
|
||||
|
||||
@@ -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
|
||||
## checking of assignments
|
||||
if sonsLen(n) == 1:
|
||||
var x = semDeref(c, n)
|
||||
let x = semDeref(c, n)
|
||||
if x == nil: return nil
|
||||
result = newNodeIT(nkDerefExpr, x.info, x.typ)
|
||||
result.add(x[0])
|
||||
return
|
||||
checkMinSonsLen(n, 2)
|
||||
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
|
||||
of tyArray, tyOpenArray, tyVarargs, tyArrayConstr, tySequence, tyString,
|
||||
tyCString:
|
||||
if n.len != 2: return nil
|
||||
n.sons[0] = makeDeref(n.sons[0])
|
||||
c.p.bracketExpr = n.sons[0]
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
n.sons[i] = semExprWithType(c, n.sons[i],
|
||||
flags*{efInTypeof, efDetermineType})
|
||||
@@ -1166,6 +1167,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
of tyTuple:
|
||||
checkSonsLen(n, 2)
|
||||
n.sons[0] = makeDeref(n.sons[0])
|
||||
c.p.bracketExpr = n.sons[0]
|
||||
# [] operator for tuples requires constant expression:
|
||||
n.sons[1] = semConstExpr(c, n.sons[1])
|
||||
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:
|
||||
localError(n.info, errIndexTypesDoNotMatch)
|
||||
result = n
|
||||
else: discard
|
||||
else:
|
||||
c.p.bracketExpr = n.sons[0]
|
||||
|
||||
proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
let oldBracketExpr = c.p.bracketExpr
|
||||
result = semSubscript(c, n, flags)
|
||||
if result == nil:
|
||||
# overloaded [] operator:
|
||||
result = semExpr(c, buildOverloadedSubscripts(n, getIdent"[]"))
|
||||
c.p.bracketExpr = oldBracketExpr
|
||||
|
||||
proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
|
||||
var id = considerQuotedIdent(a[1])
|
||||
@@ -1249,11 +1254,15 @@ proc semAsgn(c: PContext, n: PNode): PNode =
|
||||
of nkBracketExpr:
|
||||
# a[i] = x
|
||||
# --> `[]=`(a, i, x)
|
||||
let oldBracketExpr = c.p.bracketExpr
|
||||
a = semSubscript(c, a, {efLValue})
|
||||
if a == nil:
|
||||
result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=")
|
||||
add(result, n[1])
|
||||
return semExprNoType(c, result)
|
||||
result = semExprNoType(c, result)
|
||||
c.p.bracketExpr = oldBracketExpr
|
||||
return result
|
||||
c.p.bracketExpr = oldBracketExpr
|
||||
of nkCurlyExpr:
|
||||
# a{i} = x --> `{}=`(a, i, x)
|
||||
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)
|
||||
else: internalError(info, "checkInitialized")
|
||||
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)
|
||||
else: internalError(info, "checkInitialized")
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
# this module folds constants; used by semantic checking phase
|
||||
# and evaluation phase
|
||||
|
||||
import
|
||||
strutils, lists, options, ast, astalgo, trees, treetab, nimsets, times,
|
||||
import
|
||||
strutils, lists, options, ast, astalgo, trees, treetab, nimsets, times,
|
||||
nversion, platform, math, msgs, os, condsyms, idents, renderer, types,
|
||||
commands, magicsys, saturate
|
||||
|
||||
@@ -41,7 +41,7 @@ proc newIntNodeT(intVal: BiggestInt, n: PNode): PNode =
|
||||
result.typ = n.typ
|
||||
result.info = n.info
|
||||
|
||||
proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode =
|
||||
proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode =
|
||||
result = newFloatNode(nkFloatLit, floatVal)
|
||||
if skipTypes(n.typ, abstractVarRange).kind == tyFloat:
|
||||
result.typ = getFloatLitType(result)
|
||||
@@ -49,27 +49,27 @@ proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode =
|
||||
result.typ = n.typ
|
||||
result.info = n.info
|
||||
|
||||
proc newStrNodeT(strVal: string, n: PNode): PNode =
|
||||
proc newStrNodeT(strVal: string, n: PNode): PNode =
|
||||
result = newStrNode(nkStrLit, strVal)
|
||||
result.typ = n.typ
|
||||
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
|
||||
# ordinal
|
||||
var x = getInt(a)
|
||||
|
||||
|
||||
var t = skipTypes(a.typ, abstractRange)
|
||||
case t.kind
|
||||
of tyChar:
|
||||
of tyChar:
|
||||
result = $chr(int(x) and 0xff)
|
||||
of tyEnum:
|
||||
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")
|
||||
var field = n.sons[i].sym
|
||||
if field.position == x:
|
||||
if field.ast == nil:
|
||||
if field.position == x:
|
||||
if field.ast == nil:
|
||||
return field.name.s
|
||||
else:
|
||||
return field.ast.strVal
|
||||
@@ -112,7 +112,7 @@ proc pickMaxInt(n: PNode): BiggestInt =
|
||||
else:
|
||||
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 maxA = max(first, last)
|
||||
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
|
||||
# work but the feature is very nice for reducing explicit conversions.
|
||||
result = n.typ
|
||||
|
||||
|
||||
template commutativeOp(opr: expr) {.immediate.} =
|
||||
let a = n.sons[1]
|
||||
let b = n.sons[2]
|
||||
@@ -146,7 +146,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
|
||||
result = makeRange(pickIntRange(a.typ, b.typ),
|
||||
opr(pickMinInt(a), pickMinInt(b)),
|
||||
opr(pickMaxInt(a), pickMaxInt(b)))
|
||||
|
||||
|
||||
template binaryOp(opr: expr) {.immediate.} =
|
||||
let a = n.sons[1]
|
||||
let b = n.sons[2]
|
||||
@@ -154,7 +154,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
|
||||
result = makeRange(a.typ,
|
||||
opr(pickMinInt(a), pickMinInt(b)),
|
||||
opr(pickMaxInt(a), pickMaxInt(b)))
|
||||
|
||||
|
||||
case m
|
||||
of mUnaryMinusI, mUnaryMinusI64:
|
||||
let a = n.sons[1].typ
|
||||
@@ -231,7 +231,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
|
||||
of mMaxI, mMaxI64:
|
||||
commutativeOp(max)
|
||||
else: discard
|
||||
|
||||
|
||||
discard """
|
||||
mShlI, mShlI64,
|
||||
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 n.sonsLen == 3 and
|
||||
n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
|
||||
|
||||
|
||||
let t1 = a.sym.typ
|
||||
|
||||
if n[2].kind in {nkStrLit..nkTripleStrLit}:
|
||||
@@ -250,12 +250,12 @@ proc evalIs(n, a: PNode): PNode =
|
||||
of "closure":
|
||||
let t = skipTypes(t1, abstractRange)
|
||||
result = newIntNode(nkIntLit, ord(t.kind == tyProc and
|
||||
t.callConv == ccClosure and
|
||||
t.callConv == ccClosure and
|
||||
tfIterator notin t.flags))
|
||||
of "iterator":
|
||||
let t = skipTypes(t1, abstractRange)
|
||||
result = newIntNode(nkIntLit, ord(t.kind == tyProc and
|
||||
t.callConv == ccClosure and
|
||||
t.callConv == ccClosure and
|
||||
tfIterator in t.flags))
|
||||
else: discard
|
||||
else:
|
||||
@@ -265,7 +265,7 @@ proc evalIs(n, a: PNode): PNode =
|
||||
result = newIntNode(nkIntLit, ord(match))
|
||||
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
|
||||
result = nil
|
||||
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 mLengthSeq, mLengthOpenArray: result = newIntNodeT(sonsLen(a), n) # BUGFIX
|
||||
of mUnaryPlusI, mUnaryPlusI64, mUnaryPlusF64: result = a # throw `+` away
|
||||
of mToFloat, mToBiggestFloat:
|
||||
of mToFloat, mToBiggestFloat:
|
||||
result = newFloatNodeT(toFloat(int(getInt(a))), n)
|
||||
of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n)
|
||||
of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n)
|
||||
of mAbsI, mAbsI64:
|
||||
of mAbsI, mAbsI64:
|
||||
if getInt(a) >= 0: result = a
|
||||
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
|
||||
result = newIntNodeT(getInt(a) and (`shl`(1, getSize(a.typ) * 8) - 1), 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 mSubI, mSubI64: 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)
|
||||
else: result = newIntNodeT(getInt(a), n)
|
||||
of mMaxI, mMaxI64:
|
||||
of mMaxI, mMaxI64:
|
||||
if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n)
|
||||
else: result = newIntNodeT(getInt(b), n)
|
||||
of mShlI, mShlI64:
|
||||
of mShlI, mShlI64:
|
||||
case skipTypes(n.typ, abstractRange).kind
|
||||
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 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)
|
||||
else: internalError(n.info, "constant folding for shl")
|
||||
of mShrI, mShrI64:
|
||||
of mShrI, mShrI64:
|
||||
case skipTypes(n.typ, abstractRange).kind
|
||||
of tyInt8: result = newIntNodeT(int8(getInt(a)) shr int8(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 mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n)
|
||||
of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n)
|
||||
of mDivF64:
|
||||
if getFloat(b) == 0.0:
|
||||
of mDivF64:
|
||||
if getFloat(b) == 0.0:
|
||||
if getFloat(a) == 0.0: result = newFloatNodeT(NaN, n)
|
||||
else: result = newFloatNodeT(Inf, n)
|
||||
else:
|
||||
else:
|
||||
result = newFloatNodeT(getFloat(a) / getFloat(b), n)
|
||||
of mMaxF64:
|
||||
of mMaxF64:
|
||||
if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(a), n)
|
||||
else: result = newFloatNodeT(getFloat(b), n)
|
||||
of mMinF64:
|
||||
of mMinF64:
|
||||
if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(b), n)
|
||||
else: result = newFloatNodeT(getFloat(a), 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)
|
||||
of mLeI, mLeI64, mLeB, mLeEnum, mLeCh:
|
||||
of mLeI, mLeI64, mLeB, mLeEnum, mLeCh:
|
||||
result = newIntNodeT(ord(getOrdValue(a) <= getOrdValue(b)), n)
|
||||
of mEqI, mEqI64, mEqB, mEqEnum, mEqCh:
|
||||
result = newIntNodeT(ord(getOrdValue(a) == getOrdValue(b)), n)
|
||||
of mEqI, mEqI64, mEqB, mEqEnum, mEqCh:
|
||||
result = newIntNodeT(ord(getOrdValue(a) == getOrdValue(b)), n)
|
||||
of mLtF64: 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 mLeStr: 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)
|
||||
of mLeU, mLeU64:
|
||||
of mLeU, mLeU64:
|
||||
result = newIntNodeT(ord(`<=%`(getOrdValue(a), getOrdValue(b))), 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)
|
||||
@@ -377,18 +377,18 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
|
||||
result = newIntNodeT(`/%`(getInt(a), y), n)
|
||||
of mLeSet: result = newIntNodeT(ord(containsSets(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)
|
||||
of mMulSet:
|
||||
of mMulSet:
|
||||
result = nimsets.intersectSets(a, b)
|
||||
result.info = n.info
|
||||
of mPlusSet:
|
||||
of mPlusSet:
|
||||
result = nimsets.unionSets(a, b)
|
||||
result.info = n.info
|
||||
of mMinusSet:
|
||||
of mMinusSet:
|
||||
result = nimsets.diffSets(a, b)
|
||||
result.info = n.info
|
||||
of mSymDiffSet:
|
||||
of mSymDiffSet:
|
||||
result = nimsets.symdiffSets(a, b)
|
||||
result.info = n.info
|
||||
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.
|
||||
discard
|
||||
of mIntToStr, mInt64ToStr: result = newStrNodeT($(getOrdValue(a)), n)
|
||||
of mBoolToStr:
|
||||
of mBoolToStr:
|
||||
if getOrdValue(a) == 0: result = newStrNodeT("false", n)
|
||||
else: result = newStrNodeT("true", n)
|
||||
of mCopyStr: result = newStrNodeT(substr(getStr(a), int(getOrdValue(b))), n)
|
||||
of mCopyStrLast:
|
||||
result = newStrNodeT(substr(getStr(a), int(getOrdValue(b)),
|
||||
of mCopyStrLast:
|
||||
result = newStrNodeT(substr(getStr(a), int(getOrdValue(b)),
|
||||
int(getOrdValue(c))), n)
|
||||
of mFloatToStr: result = newStrNodeT($getFloat(a), n)
|
||||
of mCStrToStr, mCharToStr: result = newStrNodeT(getStrOrChar(a), n)
|
||||
of mStrToStr: result = a
|
||||
of mEnumToStr: result = newStrNodeT(ordinalValToString(a), n)
|
||||
of mArrToSeq:
|
||||
of mArrToSeq:
|
||||
result = copyTree(a)
|
||||
result.typ = n.typ
|
||||
of mCompileOption:
|
||||
result = newIntNodeT(ord(commands.testCompileOption(a.getStr, n.info)), n)
|
||||
result = newIntNodeT(ord(commands.testCompileOption(a.getStr, n.info)), n)
|
||||
of mCompileOptionArg:
|
||||
result = newIntNodeT(ord(
|
||||
testCompileOptionArg(getStr(a), getStr(b), n.info)), n)
|
||||
of mNewString, mNewStringOfCap,
|
||||
mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh,
|
||||
mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq,
|
||||
mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait,
|
||||
of mNewString, mNewStringOfCap,
|
||||
mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh,
|
||||
mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq,
|
||||
mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, mDotDot,
|
||||
mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn, mParallel:
|
||||
discard
|
||||
else: internalError(a.info, "evalOp(" & $m & ')')
|
||||
|
||||
proc getConstIfExpr(c: PSym, n: PNode): PNode =
|
||||
|
||||
proc getConstIfExpr(c: PSym, n: PNode): PNode =
|
||||
result = nil
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
var it = n.sons[i]
|
||||
if it.len == 2:
|
||||
var e = getConstExpr(c, it.sons[0])
|
||||
if e == nil: return nil
|
||||
if getOrdValue(e) != 0:
|
||||
if result == nil:
|
||||
if getOrdValue(e) != 0:
|
||||
if result == nil:
|
||||
result = getConstExpr(c, it.sons[1])
|
||||
if result == nil: return
|
||||
if result == nil: return
|
||||
elif it.len == 1:
|
||||
if result == nil: result = getConstExpr(c, it.sons[0])
|
||||
else: internalError(it.info, "getConstIfExpr()")
|
||||
|
||||
proc partialAndExpr(c: PSym, n: PNode): PNode =
|
||||
proc partialAndExpr(c: PSym, n: PNode): PNode =
|
||||
# partial evaluation
|
||||
result = n
|
||||
var a = getConstExpr(c, n.sons[1])
|
||||
var b = getConstExpr(c, n.sons[2])
|
||||
if a != nil:
|
||||
if a != nil:
|
||||
if getInt(a) == 0: result = a
|
||||
elif b != nil: result = b
|
||||
else: result = n.sons[2]
|
||||
elif b != nil:
|
||||
elif b != nil:
|
||||
if getInt(b) == 0: result = b
|
||||
else: result = n.sons[1]
|
||||
|
||||
proc partialOrExpr(c: PSym, n: PNode): PNode =
|
||||
|
||||
proc partialOrExpr(c: PSym, n: PNode): PNode =
|
||||
# partial evaluation
|
||||
result = n
|
||||
var a = getConstExpr(c, n.sons[1])
|
||||
var b = getConstExpr(c, n.sons[2])
|
||||
if a != nil:
|
||||
if a != nil:
|
||||
if getInt(a) != 0: result = a
|
||||
elif b != nil: result = b
|
||||
else: result = n.sons[2]
|
||||
elif b != nil:
|
||||
elif b != nil:
|
||||
if getInt(b) != 0: result = b
|
||||
else: result = n.sons[1]
|
||||
|
||||
proc leValueConv(a, b: PNode): bool =
|
||||
|
||||
proc leValueConv(a, b: PNode): bool =
|
||||
result = false
|
||||
case a.kind
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
case b.kind
|
||||
of nkCharLit..nkUInt64Lit: result = a.intVal <= b.intVal
|
||||
of nkFloatLit..nkFloat128Lit: result = a.intVal <= round(b.floatVal)
|
||||
else: internalError(a.info, "leValueConv")
|
||||
of nkFloatLit..nkFloat128Lit:
|
||||
of nkFloatLit..nkFloat128Lit:
|
||||
case b.kind
|
||||
of nkFloatLit..nkFloat128Lit: result = a.floatVal <= b.floatVal
|
||||
of nkCharLit..nkUInt64Lit: result = a.floatVal <= toFloat(int(b.intVal))
|
||||
else: internalError(a.info, "leValueConv")
|
||||
else: internalError(a.info, "leValueConv")
|
||||
|
||||
|
||||
proc magicCall(m: PSym, n: PNode): PNode =
|
||||
if sonsLen(n) <= 1: return
|
||||
|
||||
var s = n.sons[0].sym
|
||||
var a = getConstExpr(m, n.sons[1])
|
||||
var b, c: PNode
|
||||
if a == nil: return
|
||||
if sonsLen(n) > 2:
|
||||
if a == nil: return
|
||||
if sonsLen(n) > 2:
|
||||
b = getConstExpr(m, n.sons[2])
|
||||
if b == nil: return
|
||||
if sonsLen(n) > 3:
|
||||
if b == nil: return
|
||||
if sonsLen(n) > 3:
|
||||
c = getConstExpr(m, n.sons[3])
|
||||
if c == nil: return
|
||||
if c == nil: return
|
||||
result = evalOp(s.magic, n, a, b, c)
|
||||
|
||||
|
||||
proc getAppType(n: PNode): PNode =
|
||||
if gGlobalOptions.contains(optGenDynLib):
|
||||
result = newStrNodeT("lib", n)
|
||||
@@ -510,48 +510,48 @@ proc rangeCheck(n: PNode, value: BiggestInt) =
|
||||
localError(n.info, errGenerated, "cannot convert " & $value &
|
||||
" to " & typeToString(n.typ))
|
||||
|
||||
proc foldConv*(n, a: PNode; check = false): PNode =
|
||||
proc foldConv*(n, a: PNode; check = false): PNode =
|
||||
# XXX range checks?
|
||||
case skipTypes(n.typ, abstractRange).kind
|
||||
of tyInt..tyInt64:
|
||||
of tyInt..tyInt64:
|
||||
case skipTypes(a.typ, abstractRange).kind
|
||||
of tyFloat..tyFloat64:
|
||||
result = newIntNodeT(int(getFloat(a)), n)
|
||||
of tyChar: result = newIntNodeT(getOrdValue(a), n)
|
||||
else:
|
||||
else:
|
||||
result = a
|
||||
result.typ = n.typ
|
||||
if check: rangeCheck(n, result.intVal)
|
||||
of tyFloat..tyFloat64:
|
||||
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)
|
||||
else:
|
||||
result = a
|
||||
result.typ = n.typ
|
||||
of tyOpenArray, tyVarargs, tyProc:
|
||||
of tyOpenArray, tyVarargs, tyProc:
|
||||
discard
|
||||
else:
|
||||
else:
|
||||
result = a
|
||||
result.typ = n.typ
|
||||
|
||||
|
||||
proc getArrayConstr(m: PSym, n: PNode): PNode =
|
||||
if n.kind == nkBracket:
|
||||
result = n
|
||||
else:
|
||||
result = getConstExpr(m, 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])
|
||||
if x == nil or x.typ.skipTypes({tyGenericInst}).kind == tyTypeDesc: return
|
||||
|
||||
|
||||
var y = getConstExpr(m, n.sons[1])
|
||||
if y == nil: return
|
||||
|
||||
|
||||
var idx = getOrdValue(y)
|
||||
case x.kind
|
||||
of nkPar:
|
||||
of nkPar:
|
||||
if idx >= 0 and idx < sonsLen(x):
|
||||
result = x.sons[int(idx)]
|
||||
if result.kind == nkExprColonExpr: result = result.sons[1]
|
||||
@@ -563,14 +563,14 @@ proc foldArrayAccess(m: PSym, n: PNode): PNode =
|
||||
else: localError(n.info, errIndexOutOfBounds)
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
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)])
|
||||
elif idx == len(x.strVal):
|
||||
elif idx == len(x.strVal):
|
||||
discard
|
||||
else:
|
||||
else:
|
||||
localError(n.info, errIndexOutOfBounds)
|
||||
else: discard
|
||||
|
||||
|
||||
proc foldFieldAccess(m: PSym, n: PNode): PNode =
|
||||
# a real field access; proc calls have already been transformed
|
||||
var x = getConstExpr(m, n.sons[0])
|
||||
@@ -584,15 +584,15 @@ proc foldFieldAccess(m: PSym, n: PNode): PNode =
|
||||
result = x.sons[field.position]
|
||||
if result.kind == nkExprColonExpr: result = result.sons[1]
|
||||
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]
|
||||
return
|
||||
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.strVal = ""
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
let a = getConstExpr(m, n.sons[i])
|
||||
if a == nil: return nil
|
||||
result.strVal.add(getStrOrChar(a))
|
||||
@@ -602,10 +602,10 @@ proc newSymNodeTypeDesc*(s: PSym; info: TLineInfo): PNode =
|
||||
result.typ = newType(tyTypeDesc, s.owner)
|
||||
result.typ.addSonSkipIntLit(s.typ)
|
||||
|
||||
proc getConstExpr(m: PSym, n: PNode): PNode =
|
||||
proc getConstExpr(m: PSym, n: PNode): PNode =
|
||||
result = nil
|
||||
case n.kind
|
||||
of nkSym:
|
||||
of nkSym:
|
||||
var s = n.sym
|
||||
case s.kind
|
||||
of skEnumField:
|
||||
@@ -636,14 +636,14 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
|
||||
else:
|
||||
result = newSymNodeTypeDesc(s, n.info)
|
||||
else: discard
|
||||
of nkCharLit..nkNilLit:
|
||||
of nkCharLit..nkNilLit:
|
||||
result = copyNode(n)
|
||||
of nkIfExpr:
|
||||
of nkIfExpr:
|
||||
result = getConstIfExpr(m, n)
|
||||
of nkCall, nkCommand, nkCallStrLit, nkPrefix, nkInfix:
|
||||
if n.sons[0].kind != nkSym: return
|
||||
of nkCall, nkCommand, nkCallStrLit, nkPrefix, nkInfix:
|
||||
if n.sons[0].kind != nkSym: return
|
||||
var s = n.sons[0].sym
|
||||
if s.kind != skProc: return
|
||||
if s.kind != skProc: return
|
||||
try:
|
||||
case s.magic
|
||||
of mNone:
|
||||
@@ -651,8 +651,8 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
|
||||
return
|
||||
of mSizeOf:
|
||||
var a = n.sons[1]
|
||||
if computeSize(a.typ) < 0:
|
||||
localError(a.info, errCannotEvalXBecauseIncompletelyDefined,
|
||||
if computeSize(a.typ) < 0:
|
||||
localError(a.info, errCannotEvalXBecauseIncompletelyDefined,
|
||||
"sizeof")
|
||||
result = nil
|
||||
elif skipTypes(a.typ, typedescInst).kind in
|
||||
@@ -662,21 +662,21 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
|
||||
else:
|
||||
result = nil
|
||||
# XXX: size computation for complex types is still wrong
|
||||
of mLow:
|
||||
of mLow:
|
||||
result = newIntNodeT(firstOrd(n.sons[1].typ), n)
|
||||
of mHigh:
|
||||
of mHigh:
|
||||
if skipTypes(n.sons[1].typ, abstractVar).kind notin
|
||||
{tySequence, tyString, tyCString, tyOpenArray, tyVarargs}:
|
||||
result = newIntNodeT(lastOrd(skipTypes(n[1].typ, abstractVar)), n)
|
||||
else:
|
||||
var a = getArrayConstr(m, n.sons[1])
|
||||
if a.kind == nkBracket:
|
||||
# we can optimize it away:
|
||||
# we can optimize it away:
|
||||
result = newIntNodeT(sonsLen(a)-1, n)
|
||||
of mLengthOpenArray:
|
||||
var a = getArrayConstr(m, n.sons[1])
|
||||
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)
|
||||
else:
|
||||
result = magicCall(m, n)
|
||||
@@ -694,33 +694,33 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
|
||||
result = evalIs(n, a)
|
||||
else:
|
||||
result = magicCall(m, n)
|
||||
except OverflowError:
|
||||
except OverflowError:
|
||||
localError(n.info, errOverOrUnderflow)
|
||||
except DivByZeroError:
|
||||
except DivByZeroError:
|
||||
localError(n.info, errConstantDivisionByZero)
|
||||
of nkAddr:
|
||||
of nkAddr:
|
||||
var a = getConstExpr(m, n.sons[0])
|
||||
if a != nil:
|
||||
if a != nil:
|
||||
result = n
|
||||
n.sons[0] = a
|
||||
of nkBracket:
|
||||
of nkBracket:
|
||||
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])
|
||||
if a == nil: return nil
|
||||
result.sons[i] = a
|
||||
incl(result.flags, nfAllConst)
|
||||
of nkRange:
|
||||
of nkRange:
|
||||
var a = getConstExpr(m, n.sons[0])
|
||||
if a == nil: return
|
||||
if a == nil: return
|
||||
var b = getConstExpr(m, n.sons[1])
|
||||
if b == nil: return
|
||||
if b == nil: return
|
||||
result = copyNode(n)
|
||||
addSon(result, a)
|
||||
addSon(result, b)
|
||||
of nkCurly:
|
||||
of nkCurly:
|
||||
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])
|
||||
if a == nil: return nil
|
||||
result.sons[i] = a
|
||||
@@ -735,33 +735,33 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
|
||||
of nkPar:
|
||||
# tuple constructor
|
||||
result = copyTree(n)
|
||||
if (sonsLen(n) > 0) and (n.sons[0].kind == nkExprColonExpr):
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
if (sonsLen(n) > 0) and (n.sons[0].kind == nkExprColonExpr):
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
var a = getConstExpr(m, n.sons[i].sons[1])
|
||||
if a == nil: return nil
|
||||
result.sons[i].sons[1] = a
|
||||
else:
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
else:
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
var a = getConstExpr(m, n.sons[i])
|
||||
if a == nil: return nil
|
||||
result.sons[i] = a
|
||||
incl(result.flags, nfAllConst)
|
||||
of nkChckRangeF, nkChckRange64, nkChckRange:
|
||||
of nkChckRangeF, nkChckRange64, nkChckRange:
|
||||
var a = getConstExpr(m, n.sons[0])
|
||||
if a == nil: return
|
||||
if leValueConv(n.sons[1], a) and leValueConv(a, n.sons[2]):
|
||||
if a == nil: return
|
||||
if leValueConv(n.sons[1], a) and leValueConv(a, n.sons[2]):
|
||||
result = a # a <= x and x <= b
|
||||
result.typ = n.typ
|
||||
else:
|
||||
else:
|
||||
localError(n.info, errGenerated, `%`(
|
||||
msgKindToString(errIllegalConvFromXtoY),
|
||||
msgKindToString(errIllegalConvFromXtoY),
|
||||
[typeToString(n.sons[0].typ), typeToString(n.typ)]))
|
||||
of nkStringToCString, nkCStringToString:
|
||||
of nkStringToCString, nkCStringToString:
|
||||
var a = getConstExpr(m, n.sons[0])
|
||||
if a == nil: return
|
||||
if a == nil: return
|
||||
result = a
|
||||
result.typ = n.typ
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
var a = getConstExpr(m, n.sons[1])
|
||||
if a == nil: return
|
||||
result = foldConv(n, a, check=n.kind == nkHiddenStdConv)
|
||||
|
||||
@@ -130,6 +130,18 @@ proc semLocals(c: PContext, n: PNode): PNode =
|
||||
result.add(a)
|
||||
|
||||
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,
|
||||
flags: TExprFlags): PNode =
|
||||
case n[0].sym.magic
|
||||
@@ -153,4 +165,35 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
of mProcCall:
|
||||
result = n
|
||||
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
|
||||
|
||||
@@ -1281,7 +1281,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
var tryStmt = newNodeI(nkTryStmt, n.sons[i].info)
|
||||
var body = newNodeI(nkStmtList, n.sons[i].info)
|
||||
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(deferPart)
|
||||
n.sons[i] = semTry(c, tryStmt)
|
||||
|
||||
@@ -1175,6 +1175,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
result = semAnyRef(c, n, tyPtr, prev)
|
||||
elif op.id == ord(wRef):
|
||||
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:
|
||||
result = semTypeExpr(c, n)
|
||||
of nkWhenStmt:
|
||||
|
||||
@@ -590,7 +590,7 @@ proc firstOrd(t: PType): BiggestInt =
|
||||
of tyUInt..tyUInt64: result = 0
|
||||
of tyEnum:
|
||||
# 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])
|
||||
else:
|
||||
assert(t.n.sons[0].kind == nkSym)
|
||||
|
||||
@@ -42,6 +42,7 @@ proc opSlurp*(file: string, info: TLineInfo, module: PSym): string =
|
||||
|
||||
proc atomicTypeX(name: string; t: PType; info: TLineInfo): PNode =
|
||||
let sym = newSym(skType, getIdent(name), t.owner, info)
|
||||
sym.typ = t
|
||||
result = newSymNode(sym)
|
||||
result.typ = t
|
||||
|
||||
|
||||
@@ -1008,7 +1008,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
c.gABC(n, opcCallSite, dest)
|
||||
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)
|
||||
of mExpandToAst:
|
||||
if n.len != 2:
|
||||
|
||||
@@ -121,7 +121,7 @@ different; for this a special setter syntax is needed:
|
||||
.. code-block:: nim
|
||||
|
||||
type
|
||||
Socket* = object of RootObj
|
||||
Socket* = ref object of RootObj
|
||||
FHost: int # cannot be accessed from the outside of the module
|
||||
# the `F` prefix is a convention to avoid clashes since
|
||||
# the accessors are named `host`
|
||||
@@ -134,8 +134,8 @@ different; for this a special setter syntax is needed:
|
||||
## getter of hostAddr
|
||||
s.FHost
|
||||
|
||||
var
|
||||
s: Socket
|
||||
var s: Socket
|
||||
new s
|
||||
s.host = 34 # same as `host=`(s, 34)
|
||||
|
||||
|
||||
@@ -351,32 +351,32 @@ dispatch.
|
||||
|
||||
.. code-block:: nim
|
||||
type
|
||||
Expression = object of RootObj ## abstract base class for an expression
|
||||
Literal = object of Expression
|
||||
Expression = ref object of RootObj ## abstract base class for an expression
|
||||
Literal = ref object of Expression
|
||||
x: int
|
||||
PlusExpr = object of Expression
|
||||
a, b: ref Expression
|
||||
|
||||
method eval(e: ref Expression): int =
|
||||
PlusExpr = ref object of Expression
|
||||
a, b: Expression
|
||||
|
||||
method eval(e: Expression): int =
|
||||
# override this base method
|
||||
quit "to override!"
|
||||
|
||||
method eval(e: ref Literal): int = return e.x
|
||||
|
||||
method eval(e: ref PlusExpr): int =
|
||||
method eval(e: Literal): int = return e.x
|
||||
|
||||
method eval(e: PlusExpr): int =
|
||||
# watch out: relies on dynamic binding
|
||||
result = eval(e.a) + eval(e.b)
|
||||
|
||||
proc newLit(x: int): ref Literal =
|
||||
proc newLit(x: int): Literal =
|
||||
new(result)
|
||||
result.x = x
|
||||
|
||||
proc newPlus(a, b: ref Expression): ref PlusExpr =
|
||||
|
||||
proc newPlus(a, b: Expression): PlusExpr =
|
||||
new(result)
|
||||
result.a = a
|
||||
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
|
||||
because they should use static binding, but ``eval`` is a method because it
|
||||
@@ -387,8 +387,8 @@ dispatching:
|
||||
|
||||
.. code-block:: nim
|
||||
type
|
||||
Thing = object of RootObj
|
||||
Unit = object of Thing
|
||||
Thing = ref object of RootObj
|
||||
Unit = ref object of Thing
|
||||
x: int
|
||||
|
||||
method collide(a, b: Thing) {.inline.} =
|
||||
@@ -400,8 +400,9 @@ dispatching:
|
||||
method collide(a: Unit, b: Thing) {.inline.} =
|
||||
echo "2"
|
||||
|
||||
var
|
||||
a, b: Unit
|
||||
var a, b: Unit
|
||||
new a
|
||||
new b
|
||||
collide(a, b) # output: 2
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
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
|
||||
|
||||
var
|
||||
|
||||
25
doc/tut2.txt
25
doc/tut2.txt
@@ -56,12 +56,12 @@ Objects have access to their type at runtime. There is an
|
||||
|
||||
.. code-block:: nim
|
||||
type
|
||||
Person = object of RootObj
|
||||
Person = ref object of RootObj
|
||||
name*: string # the * means that `name` is accessible from other modules
|
||||
age: int # no * means that the field is hidden from other modules
|
||||
|
||||
Student = object of Person # Student inherits from Person
|
||||
id: int # with an id field
|
||||
Student = ref object of Person # Student inherits from Person
|
||||
id: int # with an id field
|
||||
|
||||
var
|
||||
student: Student
|
||||
@@ -69,6 +69,7 @@ Objects have access to their type at runtime. There is an
|
||||
assert(student of Student) # is true
|
||||
# object construction:
|
||||
student = Student(name: "Anton", age: 5, id: 2)
|
||||
echo student[]
|
||||
|
||||
Object fields that should be visible from outside the defining module have to
|
||||
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
|
||||
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
|
||||
(*is-a* relation) for simple code reuse. Since objects are value types in
|
||||
@@ -228,7 +232,7 @@ is needed:
|
||||
.. code-block:: nim
|
||||
|
||||
type
|
||||
Socket* = object of RootObj
|
||||
Socket* = ref object of RootObj
|
||||
FHost: int # cannot be accessed from the outside of the module
|
||||
# the `F` prefix is a convention to avoid clashes since
|
||||
# the accessors are named `host`
|
||||
@@ -241,8 +245,8 @@ is needed:
|
||||
## getter of hostAddr
|
||||
s.FHost
|
||||
|
||||
var
|
||||
s: Socket
|
||||
var s: Socket
|
||||
new s
|
||||
s.host = 34 # same as `host=`(s, 34)
|
||||
|
||||
(The example also shows ``inline`` procedures.)
|
||||
@@ -313,8 +317,8 @@ dispatching:
|
||||
.. code-block:: nim
|
||||
|
||||
type
|
||||
Thing = object of RootObj
|
||||
Unit = object of Thing
|
||||
Thing = ref object of RootObj
|
||||
Unit = ref object of Thing
|
||||
x: int
|
||||
|
||||
method collide(a, b: Thing) {.inline.} =
|
||||
@@ -326,8 +330,9 @@ dispatching:
|
||||
method collide(a: Unit, b: Thing) {.inline.} =
|
||||
echo "2"
|
||||
|
||||
var
|
||||
a, b: Unit
|
||||
var a, b: Unit
|
||||
new a
|
||||
new b
|
||||
collide(a, b) # output: 2
|
||||
|
||||
|
||||
|
||||
@@ -253,16 +253,16 @@ proc product*[T](x: openArray[seq[T]]): seq[seq[T]] =
|
||||
while true:
|
||||
while indexes[index] == -1:
|
||||
indexes[index] = initial[index]
|
||||
index +=1
|
||||
index += 1
|
||||
if index == x.len: return
|
||||
indexes[index] -=1
|
||||
indexes[index] -= 1
|
||||
for ni, i in indexes:
|
||||
next[ni] = x[ni][i]
|
||||
var res: seq[T]
|
||||
shallowCopy(res, next)
|
||||
result.add(res)
|
||||
index = 0
|
||||
indexes[index] -=1
|
||||
indexes[index] -= 1
|
||||
|
||||
proc nextPermutation*[T](x: var openarray[T]): bool {.discardable.} =
|
||||
## Calculates the next lexicographic permutation, directly modifying ``x``.
|
||||
|
||||
@@ -61,8 +61,8 @@ proc pasv(ftp: AsyncFtpClient) {.async.} =
|
||||
assertReply(pasvMsg, "227")
|
||||
var betweenParens = captureBetween(pasvMsg.string, '(', ')')
|
||||
var nums = betweenParens.split(',')
|
||||
var ip = nums[0.. -3]
|
||||
var port = nums[-2.. -1]
|
||||
var ip = nums[0.. ^3]
|
||||
var port = nums[^2.. ^1]
|
||||
var properPort = port[0].parseInt()*256+port[1].parseInt()
|
||||
await ftp.dsock.connect(ip.join("."), Port(properPort.toU16))
|
||||
ftp.dsockConnected = true
|
||||
|
||||
@@ -18,8 +18,8 @@ import sockets, os
|
||||
## This module implements an asynchronous event loop together with asynchronous
|
||||
## sockets which use this event loop.
|
||||
## 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
|
||||
## ``register`` function which you should use to add the desired objects to 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
|
||||
## dispatcher which you created so
|
||||
## that you can receive the events associated with that module's object.
|
||||
##
|
||||
@@ -27,19 +27,19 @@ import sockets, os
|
||||
## function in a while loop.
|
||||
##
|
||||
## **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.
|
||||
##
|
||||
## **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
|
||||
## sockets at a time.
|
||||
##
|
||||
##
|
||||
## 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
|
||||
## ``RootObj``!
|
||||
##
|
||||
## **Note:** If you want to provide async ability to your module please do not
|
||||
## use the ``Delegate`` object, instead use ``AsyncSocket``. It is possible
|
||||
## **Note:** If you want to provide async ability to your module please do not
|
||||
## use the ``Delegate`` object, instead use ``AsyncSocket``. It is possible
|
||||
## that in the future this type's fields will not be exported therefore breaking
|
||||
## your code.
|
||||
##
|
||||
@@ -59,11 +59,11 @@ import sockets, os
|
||||
## 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
|
||||
## using the ``register`` procedure.
|
||||
##
|
||||
##
|
||||
## An example ``handleAccept`` follows:
|
||||
##
|
||||
##
|
||||
## .. code-block:: nim
|
||||
##
|
||||
##
|
||||
## var disp = newDispatcher()
|
||||
## ...
|
||||
## proc handleAccept(s: AsyncSocket) =
|
||||
@@ -74,7 +74,7 @@ import sockets, os
|
||||
## client.handleRead = ...
|
||||
## disp.register(client)
|
||||
## ...
|
||||
##
|
||||
##
|
||||
## For client sockets you should only be interested in the ``handleRead`` and
|
||||
## ``handleConnect`` events. The former gets called whenever the socket has
|
||||
## 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
|
||||
## =============================================
|
||||
##
|
||||
##
|
||||
## If you need a asynchronous server socket but you wish to process the clients
|
||||
## synchronously then you can use the ``getSocket`` converter to get
|
||||
## a ``Socket`` from the ``AsyncSocket`` object, this can then be combined
|
||||
## with ``accept`` like so:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
##
|
||||
##
|
||||
## proc handleAccept(s: AsyncSocket) =
|
||||
## var client: Socket
|
||||
## getSocket(s).accept(client)
|
||||
@@ -113,11 +113,11 @@ type
|
||||
handleWrite*: proc (h: RootRef) {.nimcall, gcsafe.}
|
||||
handleError*: proc (h: RootRef) {.nimcall, gcsafe.}
|
||||
hasDataBuffered*: proc (h: RootRef): bool {.nimcall, gcsafe.}
|
||||
|
||||
|
||||
open*: bool
|
||||
task*: proc (h: RootRef) {.nimcall, gcsafe.}
|
||||
mode*: FileMode
|
||||
|
||||
|
||||
Delegate* = ref DelegateObj
|
||||
|
||||
Dispatcher* = ref DispatcherObj
|
||||
@@ -144,7 +144,7 @@ type
|
||||
deleg: Delegate
|
||||
|
||||
SocketStatus* = enum
|
||||
SockIdle, SockConnecting, SockConnected, SockListening, SockClosed,
|
||||
SockIdle, SockConnecting, SockConnected, SockListening, SockClosed,
|
||||
SockUDPBound
|
||||
|
||||
{.deprecated: [TDelegate: DelegateObj, PDelegate: Delegate,
|
||||
@@ -176,8 +176,8 @@ proc newAsyncSocket(): AsyncSocket =
|
||||
result.lineBuffer = "".TaintedString
|
||||
result.sendBuffer = ""
|
||||
|
||||
proc asyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
|
||||
protocol: Protocol = IPPROTO_TCP,
|
||||
proc asyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
|
||||
protocol: Protocol = IPPROTO_TCP,
|
||||
buffered = true): AsyncSocket =
|
||||
## Initialises an AsyncSocket object. If a socket cannot be initialised
|
||||
## EOS is raised.
|
||||
@@ -236,7 +236,7 @@ proc asyncSockHandleWrite(h: RootRef) =
|
||||
if AsyncSocket(h).socket.isSSL and not
|
||||
AsyncSocket(h).socket.gotHandshake:
|
||||
return
|
||||
|
||||
|
||||
if AsyncSocket(h).info == SockConnecting:
|
||||
AsyncSocket(h).handleConnect(AsyncSocket(h))
|
||||
AsyncSocket(h).info = SockConnected
|
||||
@@ -256,10 +256,10 @@ proc asyncSockHandleWrite(h: RootRef) =
|
||||
# do nothing instead.
|
||||
discard
|
||||
elif bytesSent != sock.sendBuffer.len:
|
||||
sock.sendBuffer = sock.sendBuffer[bytesSent .. -1]
|
||||
sock.sendBuffer = sock.sendBuffer[bytesSent .. ^1]
|
||||
elif bytesSent == sock.sendBuffer.len:
|
||||
sock.sendBuffer = ""
|
||||
|
||||
|
||||
if AsyncSocket(h).handleWrite != nil:
|
||||
AsyncSocket(h).handleWrite(AsyncSocket(h))
|
||||
except OSError:
|
||||
@@ -284,7 +284,7 @@ when defined(ssl):
|
||||
else:
|
||||
# handshake will set socket's ``sslNoHandshake`` field.
|
||||
discard AsyncSocket(h).socket.handshake()
|
||||
|
||||
|
||||
|
||||
proc asyncSockTask(h: RootRef) =
|
||||
when defined(ssl):
|
||||
@@ -377,9 +377,9 @@ proc acceptAddr*(server: AsyncSocket, client: var AsyncSocket,
|
||||
|
||||
if c == invalidSocket: raiseSocketError(server.socket)
|
||||
c.setBlocking(false) # TODO: Needs to be tested.
|
||||
|
||||
|
||||
# deleg.open is set in ``toDelegate``.
|
||||
|
||||
|
||||
client.socket = c
|
||||
client.lineBuffer = "".TaintedString
|
||||
client.sendBuffer = ""
|
||||
@@ -393,7 +393,7 @@ proc accept*(server: AsyncSocket, client: var AsyncSocket) =
|
||||
proc acceptAddr*(server: AsyncSocket): tuple[sock: AsyncSocket,
|
||||
address: string] {.deprecated.} =
|
||||
## Equivalent to ``sockets.acceptAddr``.
|
||||
##
|
||||
##
|
||||
## **Deprecated since version 0.9.0:** Please use the function above.
|
||||
var client = newAsyncSocket()
|
||||
var address: string = ""
|
||||
@@ -441,17 +441,17 @@ proc isConnected*(s: AsyncSocket): bool =
|
||||
## Determines whether ``s`` is connected.
|
||||
return s.info == SockConnected
|
||||
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
|
||||
proc isConnecting*(s: AsyncSocket): bool =
|
||||
## Determines whether ``s`` is connecting.
|
||||
## Determines whether ``s`` is connecting.
|
||||
return s.info == SockConnecting
|
||||
proc isClosed*(s: AsyncSocket): bool =
|
||||
## Determines whether ``s`` has been closed.
|
||||
return s.info == SockClosed
|
||||
proc isSendDataBuffered*(s: AsyncSocket): bool =
|
||||
## 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
|
||||
|
||||
proc setHandleWrite*(s: AsyncSocket,
|
||||
@@ -550,7 +550,7 @@ proc send*(sock: AsyncSocket, data: string) =
|
||||
sock.sendBuffer.add(data)
|
||||
sock.deleg.mode = fmReadWrite
|
||||
elif bytesSent != data.len:
|
||||
sock.sendBuffer.add(data[bytesSent .. -1])
|
||||
sock.sendBuffer.add(data[bytesSent .. ^1])
|
||||
sock.deleg.mode = fmReadWrite
|
||||
|
||||
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) =
|
||||
FD_ZERO(fd)
|
||||
for i in items(s):
|
||||
for i in items(s):
|
||||
m = max(m, int(i.fd))
|
||||
FD_SET(i.fd, fd)
|
||||
|
||||
|
||||
proc pruneSocketSet(s: var seq[Delegate], fd: var TFdSet) =
|
||||
var i = 0
|
||||
var L = s.len
|
||||
@@ -576,16 +576,16 @@ proc pruneSocketSet(s: var seq[Delegate], fd: var TFdSet) =
|
||||
inc(i)
|
||||
setLen(s, L)
|
||||
|
||||
proc select(readfds, writefds, exceptfds: var seq[Delegate],
|
||||
proc select(readfds, writefds, exceptfds: var seq[Delegate],
|
||||
timeout = 500): int =
|
||||
var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
|
||||
|
||||
|
||||
var rd, wr, ex: TFdSet
|
||||
var m = 0
|
||||
createFdSet(rd, readfds, m)
|
||||
createFdSet(wr, writefds, m)
|
||||
createFdSet(ex, exceptfds, m)
|
||||
|
||||
|
||||
if timeout != -1:
|
||||
result = int(select(cint(m+1), addr(rd), addr(wr), addr(ex), addr(tv)))
|
||||
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`.
|
||||
## 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
|
||||
## 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 len = d.delegates.len
|
||||
var dc = 0
|
||||
|
||||
|
||||
while dc < len:
|
||||
let deleg = d.delegates[dc]
|
||||
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.
|
||||
d.delegates[dc] = d.delegates[len-1]
|
||||
dec len
|
||||
|
||||
|
||||
d.delegates.setLen(len)
|
||||
|
||||
|
||||
var hasDataBufferedCount = 0
|
||||
for d in d.delegates:
|
||||
if d.hasDataBuffered(d.deleVal):
|
||||
hasDataBufferedCount.inc()
|
||||
d.handleRead(d.deleVal)
|
||||
if hasDataBufferedCount > 0: return true
|
||||
|
||||
|
||||
if readDg.len() == 0 and writeDg.len() == 0:
|
||||
## TODO: Perhaps this shouldn't return if errorDg has something?
|
||||
return false
|
||||
|
||||
|
||||
if select(readDg, writeDg, errorDg, timeout) != 0:
|
||||
for i in 0..len(d.delegates)-1:
|
||||
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)
|
||||
if deleg notin errorDg:
|
||||
deleg.handleError(deleg.deleVal)
|
||||
|
||||
|
||||
# Execute tasks
|
||||
for i in items(d.delegates):
|
||||
i.task(i.deleVal)
|
||||
@@ -664,7 +664,7 @@ when isMainModule:
|
||||
|
||||
proc testConnect(s: AsyncSocket, no: int) =
|
||||
echo("Connected! " & $no)
|
||||
|
||||
|
||||
proc testRead(s: AsyncSocket, no: int) =
|
||||
echo("Reading! " & $no)
|
||||
var data = ""
|
||||
@@ -682,31 +682,31 @@ when isMainModule:
|
||||
var address = ""
|
||||
s.acceptAddr(client, address)
|
||||
echo("Accepted ", address)
|
||||
client.handleRead =
|
||||
client.handleRead =
|
||||
proc (s: AsyncSocket) =
|
||||
testRead(s, 2)
|
||||
disp.register(client)
|
||||
|
||||
proc main =
|
||||
var d = newDispatcher()
|
||||
|
||||
|
||||
var s = asyncSocket()
|
||||
s.connect("amber.tenthbit.net", Port(6667))
|
||||
s.handleConnect =
|
||||
s.handleConnect =
|
||||
proc (s: AsyncSocket) =
|
||||
testConnect(s, 1)
|
||||
s.handleRead =
|
||||
s.handleRead =
|
||||
proc (s: AsyncSocket) =
|
||||
testRead(s, 1)
|
||||
d.register(s)
|
||||
|
||||
|
||||
var server = asyncSocket()
|
||||
server.handleAccept =
|
||||
proc (s: AsyncSocket) =
|
||||
proc (s: AsyncSocket) =
|
||||
testAccept(s, d, 78)
|
||||
server.bindAddr(Port(5555))
|
||||
server.listen()
|
||||
d.register(server)
|
||||
|
||||
|
||||
while d.poll(-1): discard
|
||||
main()
|
||||
|
||||
@@ -204,7 +204,7 @@ proc nimFlowVarSignal(fv: FlowVarBase) {.compilerProc.} =
|
||||
inc fv.ai.cv.counter
|
||||
release(fv.ai.cv.L)
|
||||
signal(fv.ai.cv.c)
|
||||
if fv.usesSemaphore:
|
||||
if fv.usesSemaphore:
|
||||
signal(fv.cv)
|
||||
|
||||
proc awaitAndThen*[T](fv: FlowVar[T]; action: proc (x: T) {.closure.}) =
|
||||
|
||||
@@ -15,8 +15,8 @@ from rawsockets import nil
|
||||
from asyncdispatch import PFuture
|
||||
|
||||
## 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.
|
||||
## The asynchronous implementation requires you to use the ``asyncFTPClient``
|
||||
## 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.
|
||||
##
|
||||
## Here is some example usage of this module:
|
||||
##
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## var ftp = ftpClient("example.org", user = "user", pass = "pass")
|
||||
## ftp.connect()
|
||||
@@ -51,7 +51,7 @@ type
|
||||
port*: rawsockets.Port
|
||||
else:
|
||||
port*: Port
|
||||
|
||||
|
||||
jobInProgress*: bool
|
||||
job*: FTPJob[SockType]
|
||||
|
||||
@@ -91,7 +91,7 @@ type
|
||||
of EvLines:
|
||||
lines*: string ## Lines that have been transferred.
|
||||
of EvRetr, EvStore: ## Retr/Store operation finished.
|
||||
nil
|
||||
nil
|
||||
of EvTransferProgress:
|
||||
bytesTotal*: BiggestInt ## Bytes total.
|
||||
bytesFinished*: BiggestInt ## Bytes transferred.
|
||||
@@ -213,7 +213,7 @@ proc handleConnect(s: AsyncSocket, ftp: AsyncFTPClient) =
|
||||
proc handleRead(s: AsyncSocket, ftp: AsyncFTPClient) =
|
||||
assert ftp.jobInProgress
|
||||
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.
|
||||
assert(not ftp.job.prc(ftp, true))
|
||||
|
||||
@@ -236,13 +236,13 @@ proc pasv[T](ftp: FtpBase[T]) =
|
||||
ftp.disp.register(ftp.dsock)
|
||||
else:
|
||||
{.fatal: "Incorrect socket instantiation".}
|
||||
|
||||
|
||||
var pasvMsg = ftp.send("PASV").string.strip.TaintedString
|
||||
assertReply(pasvMsg, "227")
|
||||
var betweenParens = captureBetween(pasvMsg.string, '(', ')')
|
||||
var nums = betweenParens.split(',')
|
||||
var ip = nums[0.. -3]
|
||||
var port = nums[-2.. -1]
|
||||
var ip = nums[0.. ^3]
|
||||
var port = nums[^2.. ^1]
|
||||
var properPort = port[0].parseInt()*256+port[1].parseInt()
|
||||
ftp.dsock.connect(ip.join("."), Port(properPort.toU16))
|
||||
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")
|
||||
else:
|
||||
{.fatal: "Incorrect socket instantiation".}
|
||||
|
||||
|
||||
if not async:
|
||||
var readSocks: seq[Socket] = @[ftp.csock]
|
||||
# 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 =
|
||||
## Lists all files in ``dir``. If ``dir`` is ``""``, uses the current
|
||||
## 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.
|
||||
ftp.createJob(getLines[T], JRetrText)
|
||||
ftp.pasv()
|
||||
@@ -417,7 +417,7 @@ proc retrText*[T](ftp: FtpBase[T], file: string, async = false): string =
|
||||
ftp.createJob(getLines[T], JRetrText)
|
||||
ftp.pasv()
|
||||
assertReply ftp.send("RETR " & file.normalizePathSep), ["125", "150"]
|
||||
|
||||
|
||||
if not async:
|
||||
while not ftp.job.prc(ftp, false): discard
|
||||
result = ftp.job.lines
|
||||
@@ -436,7 +436,7 @@ proc getFile[T](ftp: FtpBase[T], async = false): bool =
|
||||
else:
|
||||
bytesRead = ftp.dsock.recvAsync(r, BufferSize)
|
||||
returned = bytesRead != -1
|
||||
else:
|
||||
else:
|
||||
bytesRead = ftp.dsock.recv(r, BufferSize)
|
||||
returned = true
|
||||
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) =
|
||||
## Downloads ``file`` and saves it to ``dest``. Usage of this function
|
||||
## 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
|
||||
## to ``file``.
|
||||
ftp.createJob(getFile[T], JRetr)
|
||||
@@ -471,7 +471,7 @@ proc retrFile*[T](ftp: FtpBase[T], file, dest: string, async = false) =
|
||||
var fileSize: BiggestInt
|
||||
if reply.string.captureBetween('(', ')').parseBiggestInt(fileSize) == 0:
|
||||
raise newException(ReplyError, "Reply has no file size.")
|
||||
|
||||
|
||||
ftp.job.total = fileSize
|
||||
ftp.job.lastProgressReport = epochTime()
|
||||
ftp.job.filename = file.normalizePathSep
|
||||
@@ -488,7 +488,7 @@ proc doUpload[T](ftp: FtpBase[T], async = false): bool =
|
||||
if bytesSent == ftp.job.toStore.len:
|
||||
ftp.job.toStore = ""
|
||||
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.oneSecond.inc(bytesSent)
|
||||
else:
|
||||
@@ -499,12 +499,12 @@ proc doUpload[T](ftp: FtpBase[T], async = false): bool =
|
||||
# File finished uploading.
|
||||
ftp.dsock.close()
|
||||
ftp.dsockConnected = false
|
||||
|
||||
|
||||
if not async:
|
||||
assertReply ftp.expectReply(), "226"
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
if not async:
|
||||
ftp.dsock.send(s)
|
||||
else:
|
||||
@@ -512,9 +512,9 @@ proc doUpload[T](ftp: FtpBase[T], async = false): bool =
|
||||
if bytesSent == 0:
|
||||
ftp.job.toStore.add(s)
|
||||
elif bytesSent != s.len:
|
||||
ftp.job.toStore.add(s[bytesSent .. -1])
|
||||
ftp.job.toStore.add(s[bytesSent .. ^1])
|
||||
len = bytesSent
|
||||
|
||||
|
||||
ftp.job.progress.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
|
||||
## function asynchronously is recommended to view the progress of
|
||||
## the download.
|
||||
## The ``EvStore`` event is passed to the specified ``handleEvent`` function
|
||||
## when the upload is finished, and the ``filename`` field will be
|
||||
## The ``EvStore`` event is passed to the specified ``handleEvent`` function
|
||||
## when the upload is finished, and the ``filename`` field will be
|
||||
## equal to ``file``.
|
||||
ftp.createJob(doUpload[T], JStore)
|
||||
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.filename = file
|
||||
ftp.pasv()
|
||||
|
||||
|
||||
assertReply ftp.send("STOR " & dest.normalizePathSep), ["125", "150"]
|
||||
|
||||
if not async:
|
||||
@@ -564,12 +564,12 @@ proc csockHandleRead(s: AsyncSocket, ftp: AsyncFTPClient) =
|
||||
if ftp.job.progress != ftp.job.total:
|
||||
raise newException(FTPError, "Didn't upload full file.")
|
||||
ftp.deleteJob()
|
||||
|
||||
|
||||
ftp.handleEvent(ftp, r)
|
||||
|
||||
proc asyncFTPClient*(address: string, port = Port(21),
|
||||
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 =
|
||||
## Create a ``AsyncFTPClient`` object.
|
||||
##
|
||||
@@ -617,7 +617,7 @@ when isMainModule:
|
||||
echo d.len
|
||||
else: assert(false)
|
||||
var ftp = asyncFTPClient("example.com", user = "foo", pass = "bar", handleEvent = hev)
|
||||
|
||||
|
||||
d.register(ftp)
|
||||
d.len.echo()
|
||||
ftp.connect()
|
||||
|
||||
@@ -227,7 +227,7 @@ proc parseResponse(s: Socket, getBody: bool, timeout: int): Response =
|
||||
inc(linei, le)
|
||||
# Status code
|
||||
linei.inc skipWhitespace(line, linei)
|
||||
result.status = line[linei .. -1]
|
||||
result.status = line[linei .. ^1]
|
||||
parsedStatus = true
|
||||
else:
|
||||
# Parse headers
|
||||
@@ -238,7 +238,7 @@ proc parseResponse(s: Socket, getBody: bool, timeout: int): Response =
|
||||
if line[linei] != ':': httpError("invalid headers")
|
||||
inc(linei) # Skip :
|
||||
|
||||
result.headers[name] = line[linei.. -1].strip()
|
||||
result.headers[name] = line[linei.. ^1].strip()
|
||||
if not fullyRead:
|
||||
httpError("Connection was closed before full request has been made")
|
||||
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``
|
||||
## | An optional timeout can be specified in miliseconds, if reading from the
|
||||
## 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)
|
||||
|
||||
proc redirection(status: string): bool =
|
||||
@@ -725,7 +725,7 @@ proc parseResponse(client: AsyncHttpClient,
|
||||
inc(linei, le)
|
||||
# Status code
|
||||
linei.inc skipWhitespace(line, linei)
|
||||
result.status = line[linei .. -1]
|
||||
result.status = line[linei .. ^1]
|
||||
parsedStatus = true
|
||||
else:
|
||||
# Parse headers
|
||||
@@ -736,7 +736,7 @@ proc parseResponse(client: AsyncHttpClient,
|
||||
if line[linei] != ':': httpError("invalid headers")
|
||||
inc(linei) # Skip :
|
||||
|
||||
result.headers[name] = line[linei.. -1].strip()
|
||||
result.headers[name] = line[linei.. ^1].strip()
|
||||
if not fullyRead:
|
||||
httpError("Connection was closed before full request has been made")
|
||||
if getBody:
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
## 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,
|
||||
## write your own.
|
||||
##
|
||||
##
|
||||
## Format strings support the following variables which must be prefixed with
|
||||
## the dollar operator (``$``):
|
||||
##
|
||||
@@ -21,13 +21,13 @@
|
||||
## $time Current time
|
||||
## $app ``os.getAppFilename()``
|
||||
## ============ =======================
|
||||
##
|
||||
##
|
||||
##
|
||||
## The following example demonstrates logging to three different handlers
|
||||
## simultaneously:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
##
|
||||
##
|
||||
## var L = newConsoleLogger()
|
||||
## var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
|
||||
## var rL = newRollingFileLogger("rolling.log", fmtStr = verboseFmtStr)
|
||||
@@ -64,20 +64,20 @@ const
|
||||
|
||||
type
|
||||
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
|
||||
fmtStr: string ## = defaultFmtStr by default, see substituteLog for $date etc.
|
||||
|
||||
|
||||
ConsoleLogger* = ref object of Logger ## logger that writes the messages to the
|
||||
## console
|
||||
|
||||
|
||||
FileLogger* = ref object of Logger ## logger that writes the messages to a 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
|
||||
## performs log rotation
|
||||
maxLines: int # maximum number of lines
|
||||
maxLines: int # maximum number of lines
|
||||
curLine : int
|
||||
baseName: string # initial filename
|
||||
baseMode: FileMode # initial file mode
|
||||
@@ -86,22 +86,22 @@ type
|
||||
{.deprecated: [TLevel: Level, PLogger: Logger, PConsoleLogger: ConsoleLogger,
|
||||
PFileLogger: FileLogger, PRollingFileLogger: RollingFileLogger].}
|
||||
|
||||
proc substituteLog(frmt: string): string =
|
||||
proc substituteLog(frmt: string): string =
|
||||
## converts $date to the current date
|
||||
## converts $time to the current time
|
||||
## converts $app to getAppFilename()
|
||||
## converts
|
||||
## converts
|
||||
result = newStringOfCap(frmt.len + 20)
|
||||
var i = 0
|
||||
while i < frmt.len:
|
||||
if frmt[i] != '$':
|
||||
while i < frmt.len:
|
||||
if frmt[i] != '$':
|
||||
result.add(frmt[i])
|
||||
inc(i)
|
||||
else:
|
||||
inc(i)
|
||||
var v = ""
|
||||
var app = getAppFilename()
|
||||
while frmt[i] in IdentChars:
|
||||
while frmt[i] in IdentChars:
|
||||
v.add(toLower(frmt[i]))
|
||||
inc(i)
|
||||
case v
|
||||
@@ -114,12 +114,12 @@ proc substituteLog(frmt: string): string =
|
||||
|
||||
method log*(logger: Logger, level: Level,
|
||||
frmt: string, args: varargs[string, `$`]) {.
|
||||
raises: [Exception],
|
||||
raises: [Exception],
|
||||
tags: [TimeEffect, WriteIOEffect, ReadIOEffect].} =
|
||||
## Override this method in custom loggers. Default implementation does
|
||||
## nothing.
|
||||
discard
|
||||
|
||||
|
||||
method log*(logger: ConsoleLogger, level: Level,
|
||||
frmt: string, args: varargs[string, `$`]) =
|
||||
## Logs to the console using ``logger`` only.
|
||||
@@ -127,14 +127,14 @@ method log*(logger: ConsoleLogger, level: Level,
|
||||
writeln(stdout, LevelNames[level], " ", substituteLog(logger.fmtStr),
|
||||
frmt % args)
|
||||
|
||||
method log*(logger: FileLogger, level: Level,
|
||||
method log*(logger: FileLogger, level: Level,
|
||||
frmt: string, args: varargs[string, `$`]) =
|
||||
## Logs to a file using ``logger`` only.
|
||||
if level >= logger.levelThreshold:
|
||||
writeln(logger.f, LevelNames[level], " ",
|
||||
substituteLog(logger.fmtStr), frmt % args)
|
||||
|
||||
proc defaultFilename*(): string =
|
||||
proc defaultFilename*(): string =
|
||||
## Returns the default filename for a logger.
|
||||
var (path, name, ext) = splitFile(getAppFilename())
|
||||
result = changeFileExt(path / name, "log")
|
||||
@@ -145,10 +145,10 @@ proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr): Console
|
||||
result.fmtStr = fmtStr
|
||||
result.levelThreshold = levelThreshold
|
||||
|
||||
proc newFileLogger*(filename = defaultFilename(),
|
||||
proc newFileLogger*(filename = defaultFilename(),
|
||||
mode: FileMode = fmAppend,
|
||||
levelThreshold = lvlAll,
|
||||
fmtStr = defaultFmtStr): FileLogger =
|
||||
fmtStr = defaultFmtStr): FileLogger =
|
||||
## Creates a new file logger. This logger logs to a file.
|
||||
new(result)
|
||||
result.levelThreshold = levelThreshold
|
||||
@@ -170,14 +170,14 @@ proc countFiles(filename: string): int =
|
||||
if kind == pcFile:
|
||||
let llfn = name & ext & ExtSep
|
||||
if path.extractFilename.startsWith(llfn):
|
||||
let numS = path.extractFilename[llfn.len .. -1]
|
||||
let numS = path.extractFilename[llfn.len .. ^1]
|
||||
try:
|
||||
let num = parseInt(numS)
|
||||
if num > result:
|
||||
result = num
|
||||
except ValueError: discard
|
||||
|
||||
proc newRollingFileLogger*(filename = defaultFilename(),
|
||||
proc newRollingFileLogger*(filename = defaultFilename(),
|
||||
mode: FileMode = fmReadWrite,
|
||||
levelThreshold = lvlAll,
|
||||
fmtStr = defaultFmtStr,
|
||||
@@ -192,9 +192,9 @@ proc newRollingFileLogger*(filename = defaultFilename(),
|
||||
result.curLine = 0
|
||||
result.baseName = filename
|
||||
result.baseMode = mode
|
||||
|
||||
|
||||
result.logFiles = countFiles(filename)
|
||||
|
||||
|
||||
if mode == fmAppend:
|
||||
# We need to get a line count because we will be appending to the file.
|
||||
result.curLine = countLogLines(result)
|
||||
@@ -206,7 +206,7 @@ proc rotate(logger: RollingFileLogger) =
|
||||
moveFile(dir / (name & ext & srcSuff),
|
||||
dir / (name & ext & ExtSep & $(i+1)))
|
||||
|
||||
method log*(logger: RollingFileLogger, level: Level,
|
||||
method log*(logger: RollingFileLogger, level: Level,
|
||||
frmt: string, args: varargs[string, `$`]) =
|
||||
## Logs to a file using rolling ``logger`` only.
|
||||
if level >= logger.levelThreshold:
|
||||
@@ -216,7 +216,7 @@ method log*(logger: RollingFileLogger, level: Level,
|
||||
logger.logFiles.inc
|
||||
logger.curLine = 0
|
||||
logger.f = open(logger.baseName, logger.baseMode)
|
||||
|
||||
|
||||
writeln(logger.f, LevelNames[level], " ",substituteLog(logger.fmtStr), frmt % args)
|
||||
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
|
||||
|
||||
proc logLoop(level: Level, frmt: string, args: varargs[string, `$`]) =
|
||||
for logger in items(handlers):
|
||||
for logger in items(handlers):
|
||||
if level >= logger.levelThreshold:
|
||||
log(logger, level, frmt, args)
|
||||
|
||||
@@ -235,7 +235,7 @@ template log*(level: Level, frmt: string, args: varargs[string, `$`]) =
|
||||
bind logLoop
|
||||
bind `%`
|
||||
bind logging.level
|
||||
|
||||
|
||||
if level >= logging.level:
|
||||
logLoop(level, frmt, args)
|
||||
|
||||
@@ -243,19 +243,19 @@ template debug*(frmt: string, args: varargs[string, `$`]) =
|
||||
## Logs a debug message to all registered handlers.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
log(lvlFatal, frmt, args)
|
||||
|
||||
@@ -287,5 +287,5 @@ when isMainModule:
|
||||
addHandler(rL)
|
||||
for i in 0 .. 25:
|
||||
info("hello" & $i, [])
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -222,13 +222,13 @@ type
|
||||
set*{.magic: "Set".}[T] ## Generic type to construct bit sets.
|
||||
|
||||
type
|
||||
Slice* {.final, pure.}[T] = object ## builtin slice type
|
||||
a*, b*: T ## the bounds
|
||||
Slice*[T] = object ## builtin slice type
|
||||
a*, b*: T ## the bounds
|
||||
|
||||
when defined(nimalias):
|
||||
{.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`
|
||||
## 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
|
||||
@@ -236,7 +236,7 @@ proc `..`*[T](a, b: T): Slice[T] {.noSideEffect, inline.} =
|
||||
result.a = a
|
||||
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]``
|
||||
result.b = b
|
||||
|
||||
@@ -2864,28 +2864,27 @@ template spliceImpl(s, a, L, b: expr): stmt {.immediate.} =
|
||||
|
||||
when hostOS != "standalone":
|
||||
proc `[]`*(s: string, x: Slice[int]): string {.inline.} =
|
||||
## slice operation for strings. Negative indexes are supported.
|
||||
result = s.substr(x.a-|s, x.b-|s)
|
||||
## slice operation for strings.
|
||||
result = s.substr(x.a, x.b)
|
||||
|
||||
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
|
||||
## by `x`, a `splice`:idx: is performed:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## var s = "abcdef"
|
||||
## s[1 .. -2] = "xyz"
|
||||
## s[1 .. ^2] = "xyz"
|
||||
## assert s == "axyzf"
|
||||
var a = x.a-|s
|
||||
var L = x.b-|s - a + 1
|
||||
var a = x.a
|
||||
var L = x.b - a + 1
|
||||
if L == b.len:
|
||||
for i in 0 .. <L: s[i+a] = b[i]
|
||||
else:
|
||||
spliceImpl(s, a, L, b)
|
||||
|
||||
proc `[]`*[Idx, T](a: array[Idx, T], x: Slice[int]): seq[T] =
|
||||
## slice operation for arrays. Negative indexes are **not** supported
|
||||
## because the array might have negative bounds.
|
||||
## slice operation for arrays.
|
||||
when low(a) < 0:
|
||||
{.error: "Slicing for arrays with negative indices is unsupported.".}
|
||||
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]
|
||||
|
||||
proc `[]=`*[Idx, T](a: var array[Idx, T], x: Slice[int], b: openArray[T]) =
|
||||
## slice assignment for arrays. Negative indexes are **not** supported
|
||||
## because the array might have negative bounds.
|
||||
## slice assignment for arrays.
|
||||
when low(a) < 0:
|
||||
{.error: "Slicing for arrays with negative indices is unsupported.".}
|
||||
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")
|
||||
|
||||
proc `[]`*[Idx, T](a: array[Idx, T], x: Slice[Idx]): seq[T] =
|
||||
## slice operation for arrays. Negative indexes are **not** supported
|
||||
## because the array might have negative bounds.
|
||||
## slice operation for arrays.
|
||||
var L = ord(x.b) - ord(x.a) + 1
|
||||
newSeq(result, L)
|
||||
for i in 0.. <L:
|
||||
result[i] = a[Idx(ord(x.a) + i)]
|
||||
|
||||
proc `[]=`*[Idx, T](a: var array[Idx, T], x: Slice[Idx], b: openArray[T]) =
|
||||
## slice assignment for arrays. Negative indexes are **not** supported
|
||||
## because the array might have negative bounds.
|
||||
## slice assignment for arrays.
|
||||
var L = ord(x.b) - ord(x.a) + 1
|
||||
if L == b.len:
|
||||
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")
|
||||
|
||||
proc `[]`*[T](s: seq[T], x: Slice[int]): seq[T] =
|
||||
## slice operation for sequences. Negative indexes are supported.
|
||||
var a = x.a-|s
|
||||
var L = x.b-|s - a + 1
|
||||
## slice operation for sequences.
|
||||
var a = x.a
|
||||
var L = x.b - a + 1
|
||||
newSeq(result, L)
|
||||
for i in 0.. <L: result[i] = s[i + a]
|
||||
|
||||
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
|
||||
## by `x`, a `splice`:idx: is performed.
|
||||
var a = x.a-|s
|
||||
var L = x.b-|s - a + 1
|
||||
var a = x.a
|
||||
var L = x.b - a + 1
|
||||
if L == b.len:
|
||||
for i in 0 .. <L: s[i+a] = b[i]
|
||||
else:
|
||||
@@ -3247,4 +3243,12 @@ proc procCall*(x: expr) {.magic: "ProcCall".} =
|
||||
## procCall someMethod(a, b)
|
||||
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.}
|
||||
|
||||
@@ -165,7 +165,7 @@ proc initGC() =
|
||||
init(gch.tempStack)
|
||||
init(gch.additionalRoots)
|
||||
when withBitvectors:
|
||||
Init(gch.allocated)
|
||||
init(gch.allocated)
|
||||
init(gch.marked)
|
||||
|
||||
var
|
||||
|
||||
36
tests/array/troof1.nim
Normal file
36
tests/array/troof1.nim
Normal 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
10
tests/array/troof2.nim
Normal 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
8
tests/array/troof3.nim
Normal 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
37
tests/array/troof4.nim
Normal 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)
|
||||
@@ -7,6 +7,6 @@ discard """
|
||||
|
||||
proc test: string =
|
||||
result = "blah"
|
||||
result[1 .. -1]
|
||||
result[1 .. ^1]
|
||||
|
||||
echo test()
|
||||
|
||||
@@ -27,7 +27,7 @@ proc refExpr(exprNode: NimNode): string {.compileTime.} =
|
||||
"expr" & $(exprNodes.len - 1)
|
||||
|
||||
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
|
||||
|
||||
49
tests/macros/typesapi2.nim
Normal file
49
tests/macros/typesapi2.nim
Normal 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)
|
||||
|
||||
@@ -36,7 +36,7 @@ echo()
|
||||
|
||||
|
||||
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)
|
||||
echo()
|
||||
@@ -46,7 +46,7 @@ echo mystr
|
||||
mystr[4..4] = "u"
|
||||
|
||||
# test full replacement
|
||||
mystr[.. -2] = "egerichtet"
|
||||
mystr[.. ^2] = "egerichtet"
|
||||
|
||||
echo mystr
|
||||
|
||||
@@ -54,6 +54,6 @@ mystr[0..2] = "ve"
|
||||
echo mystr
|
||||
|
||||
var s = "abcdef"
|
||||
s[1 .. -2] = "xyz"
|
||||
s[1 .. ^2] = "xyz"
|
||||
assert s == "axyzf"
|
||||
|
||||
|
||||
14
tests/notnil/tnotnil_in_objconstr.nim
Normal file
14
tests/notnil/tnotnil_in_objconstr.nim
Normal 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
|
||||
@@ -22,35 +22,35 @@ proc delNimCache() =
|
||||
removeDir(nimcacheDir)
|
||||
except OSError:
|
||||
echo "[Warning] could not delete: ", nimcacheDir
|
||||
|
||||
|
||||
proc runRodFiles(r: var TResults, cat: Category, options: string) =
|
||||
template test(filename: expr): stmt =
|
||||
testSpec r, makeTest(rodfilesDir / filename, options, cat, actionRun)
|
||||
|
||||
|
||||
delNimCache()
|
||||
|
||||
|
||||
# test basic recompilation scheme:
|
||||
test "hallo"
|
||||
test "hallo"
|
||||
# test incremental type information:
|
||||
test "hallo2"
|
||||
delNimCache()
|
||||
|
||||
|
||||
# test type converters:
|
||||
test "aconv"
|
||||
test "bconv"
|
||||
delNimCache()
|
||||
|
||||
|
||||
# test G, A, B example from the documentation; test init sections:
|
||||
test "deada"
|
||||
test "deada2"
|
||||
delNimCache()
|
||||
|
||||
|
||||
# test method generation:
|
||||
test "bmethods"
|
||||
test "bmethods2"
|
||||
delNimCache()
|
||||
|
||||
|
||||
# test generics:
|
||||
test "tgeneric1"
|
||||
test "tgeneric2"
|
||||
@@ -79,8 +79,8 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) =
|
||||
options & " --app:lib -d:createNimRtl", cat)
|
||||
testSpec c, makeTest("tests/dll/server.nim",
|
||||
options & " --app:lib -d:useNimRtl", cat)
|
||||
|
||||
when defined(Windows):
|
||||
|
||||
when defined(Windows):
|
||||
# windows looks in the dir of the exe (yay!):
|
||||
var nimrtlDll = DynlibFormat % "nimrtl"
|
||||
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)
|
||||
var serverDll = DynlibFormat % "server"
|
||||
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)
|
||||
|
||||
proc dllTests(r: var TResults, cat: Category, options: string) =
|
||||
# dummy compile result:
|
||||
var c = initResults()
|
||||
|
||||
|
||||
runBasicDLLTest c, r, cat, options
|
||||
runBasicDLLTest c, r, cat, options & " -d:release"
|
||||
runBasicDLLTest c, r, cat, options & " --gc:boehm"
|
||||
@@ -134,7 +134,7 @@ proc gcTests(r: var TResults, cat: Category, options: string) =
|
||||
test "cycleleak"
|
||||
test "closureleak"
|
||||
testWithoutMs "refarrayleak"
|
||||
|
||||
|
||||
test "stackrefleak"
|
||||
test "cyclecollector"
|
||||
|
||||
@@ -147,7 +147,7 @@ proc threadTests(r: var TResults, cat: Category, options: string) =
|
||||
" -d:release", cat, actionRun)
|
||||
testSpec r, makeTest("tests/threads" / filename, options &
|
||||
" --tlsEmulation:on", cat, actionRun)
|
||||
|
||||
|
||||
test "tactors"
|
||||
test "tactors2"
|
||||
test "threadex"
|
||||
@@ -182,7 +182,7 @@ proc jsTests(r: var TResults, cat: Category, options: string) =
|
||||
actionRun, targetJS)
|
||||
testSpec r, makeTest(filename, options & " -d:nodejs -d:release", cat,
|
||||
actionRun, targetJS)
|
||||
|
||||
|
||||
for t in os.walkFiles("tests/js/t*.nim"):
|
||||
test(t)
|
||||
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 =
|
||||
# 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"
|
||||
result = ""
|
||||
var nimFiles = 0
|
||||
for kind, file in os.walkDir(dir):
|
||||
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"):
|
||||
if result.len == 0: result = file
|
||||
inc nimFiles
|
||||
@@ -236,7 +236,7 @@ type PackageFilter = enum
|
||||
pfExtraOnly
|
||||
pfAll
|
||||
|
||||
let
|
||||
let
|
||||
nimbleExe = findExe("nimble")
|
||||
nimbleDir = getHomeDir() / ".nimble"
|
||||
packageDir = nimbleDir / "pkgs"
|
||||
|
||||
@@ -59,7 +59,7 @@ proc callCompiler(cmdTemplate, filename, options: string,
|
||||
target: TTarget): TSpec =
|
||||
let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target],
|
||||
"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})
|
||||
let outp = p.outputStream
|
||||
var suc = ""
|
||||
@@ -284,7 +284,7 @@ proc main() =
|
||||
let testsDir = "tests" & DirSep
|
||||
for kind, dir in walkDir(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"]:
|
||||
processCategory(r, Category(cat), p.cmdLineRest.string)
|
||||
for a in AdditionalCategories:
|
||||
|
||||
6
todo.txt
6
todo.txt
@@ -1,20 +1,18 @@
|
||||
version 0.10.4
|
||||
==============
|
||||
|
||||
- improve the parser; deal with echo $foo gotcha
|
||||
- improve GC-unsafety warnings
|
||||
- make 'nil' work for 'add' and 'len'
|
||||
- add "all threads are blocked" detection to 'spawn'
|
||||
- overloading of '='
|
||||
- disallow negative indexing
|
||||
|
||||
|
||||
version 1.0
|
||||
===========
|
||||
|
||||
- remove echo $foo gotcha
|
||||
- add "all threads are blocked" detection to 'spawn'
|
||||
- figure out why C++ bootstrapping is so much slower
|
||||
- nimsuggest: auto-completion needs to work in 'class' macros
|
||||
- improve the docs for inheritance
|
||||
- The bitwise 'not' operator will be renamed to 'bnot' to
|
||||
prevent 'not 4 == 5' from compiling. -> requires 'mixin' annotation for procs!
|
||||
- iterators always require a return type
|
||||
|
||||
30
web/news.txt
30
web/news.txt
@@ -42,6 +42,33 @@ News
|
||||
structure; for immediate macro parameters ``nkCall('addr', 'x')`` is
|
||||
produced instead of ``nkAddr('x')``.
|
||||
- ``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
|
||||
@@ -84,6 +111,9 @@ News
|
||||
varOrConst(x) # "var"
|
||||
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
|
||||
-----------------
|
||||
|
||||
Reference in New Issue
Block a user