diff options
84 files changed, 2190 insertions, 1641 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 1462d58d5..36e86a6f1 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -9,12 +9,12 @@ # abstract syntax tree + symbol table -import - msgs, hashes, nversion, options, strutils, crc, ropes, idents, lists, +import + msgs, hashes, nversion, options, strutils, crc, ropes, idents, lists, intsets, idgen type - TCallingConvention* = enum + TCallingConvention* = enum ccDefault, # proc has no explicit calling convention ccStdCall, # procedure is stdcall ccCDecl, # cdecl @@ -26,12 +26,12 @@ type ccClosure, # proc has a closure ccNoConvention # needed for generating proper C procs sometimes -const - CallingConvToStr*: array[TCallingConvention, string] = ["", "stdcall", +const + CallingConvToStr*: array[TCallingConvention, string] = ["", "stdcall", "cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "closure", "noconv"] -type +type TNodeKind* = enum # order is extremely important, because ranges are used # to check whether a node belongs to a certain class nkNone, # unknown node kind: indicates an error @@ -64,13 +64,13 @@ type # end of atoms nkMetaNode_Obsolete, # difficult to explain; represents itself # (used for macros) - nkDotCall, # used to temporarily flag a nkCall node; + nkDotCall, # used to temporarily flag a nkCall node; # this is used # for transforming ``s.len`` to ``len(s)`` nkCommand, # a call like ``p 2, 4`` without parenthesis nkCall, # a call like p(x, y) or an operation like +(a, b) - nkCallStrLit, # a call with a string literal + nkCallStrLit, # a call with a string literal # x"abc" has two sons: nkIdent, nkRStrLit # x"""abc""" has two sons: nkIdent, nkTripleStrLit nkInfix, # a call like (a + b) @@ -126,7 +126,7 @@ type nkAsgn, # a = b nkFastAsgn, # internal node for a fast ``a = b`` - # (no string copy) + # (no string copy) nkGenericParams, # generic parameters nkFormalParams, # formal parameters nkOfInherit, # inherited from symbol @@ -192,10 +192,11 @@ type nkWith, # distinct with `foo` nkWithout, # distinct without `foo` - + nkTypeOfExpr, # type(1+2) nkObjectTy, # object body nkTupleTy, # tuple body + nkTupleClassTy, # tuple type class nkTypeClassTy, # user-defined type class nkStaticTy, # ``static[T]`` nkRecList, # list of object parts @@ -226,7 +227,7 @@ type TSymFlag* = enum # already 32 flags! sfUsed, # read access of sym (for warnings) or simply used sfExported, # symbol is exported from module - sfFromGeneric, # symbol is instantiation of a generic; this is needed + sfFromGeneric, # symbol is instantiation of a generic; this is needed # for symbol file generation; such symbols should always # be written into the ROD file sfGlobal, # symbol is at global scope @@ -284,9 +285,9 @@ const sfAnon* = sfDiscardable # symbol name that was generated by the compiler - # the compiler will avoid printing such names + # the compiler will avoid printing such names # in user messages. - + sfNoForward* = sfRegister # forward declarations are not required (per module) @@ -300,7 +301,7 @@ const # getting ready for the future expr/stmt merge nkWhen* = nkWhenStmt nkWhenExpr* = nkWhenStmt - nkEffectList* = nkArgList + nkEffectList* = nkArgList # hacks ahead: an nkEffectList is a node with 4 children: exceptionEffects* = 0 # exceptions at position 0 usesEffects* = 1 # read effects at position 1 @@ -321,7 +322,7 @@ type # unless this is an instance of a generic alias type. # then realInstance will be the tyGenericInst of the # completely (recursively) resolved alias. - + tyGenericParam, # ``a`` in the above patterns tyDistinct, tyEnum, @@ -340,14 +341,14 @@ type tyInt, tyInt8, tyInt16, tyInt32, tyInt64, # signed integers tyFloat, tyFloat32, tyFloat64, tyFloat128, tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64, - tyBigNum, - tyConst, tyMutable, tyVarargs, + tyBigNum, + tyConst, tyMutable, tyVarargs, tyIter, # unused tyProxy # used as errornous type (for idetools) - + tyBuiltInTypeClass #\ # Type such as the catch-all object, tuple, seq, etc - + tyUserTypeClass #\ # the body of a user-defined type class @@ -357,24 +358,24 @@ type # tyGenericInst represents concrete types, while # this is still a "generic param" that will bind types # and resolves them during sigmatch and instantiation. - + tyCompositeTypeClass #\ # Type such as seq[Number] - # The notes for tyUserTypeClassInst apply here as well + # The notes for tyUserTypeClassInst apply here as well # sons[0]: the original expression used by the user. # sons[1]: fully expanded and instantiated meta type # (potentially following aliases) - + tyAnd, tyOr, tyNot #\ # boolean type classes such as `string|int`,`not seq`, # `Sortable and Enumable`, etc - + tyAnything #\ # a type class matching any type - + tyStatic #\ # a value known at compile type (the underlying type is .base) - + tyFromExpr #\ # This is a type representing an expression that depends # on generic parameters (the expression is stored in t.n) @@ -390,7 +391,7 @@ type # sons[0]: type of containing object or tuple # sons[1]: field type # .n: nkDotExpr storing the field name - + static: # remind us when TTypeKind stops to fit in a single 64-bit word assert TTypeKind.high.ord <= 63 @@ -408,7 +409,7 @@ const tyAnd, tyOr, tyNot, tyAnything} tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyExpr} + tyTypeClasses - + type TTypeKinds* = set[TTypeKind] @@ -429,7 +430,7 @@ type nfExprCall # this is an attempt to call a regular expression nfIsRef # this node is a 'ref' node; used for the VM nfIsCursor # this node is attached a cursor; used for idetools - + TNodeFlags* = set[TNodeFlag] TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 28) tfVarargs, # procedure has C styled varargs @@ -448,7 +449,7 @@ type # proc foo(L: static[int]): array[L, int] # can be attached to ranges to indicate that the range # depends on unresolved static params. - tfRetType, # marks return types in proc (used to detect type classes + tfRetType, # marks return types in proc (used to detect type classes # used as return types for return type inference) tfCapturesEnv, # whether proc really captures some environment tfByCopy, # pass object/tuple by copy (C backend) @@ -456,7 +457,7 @@ type tfIterator, # type is really an iterator, not a tyProc tfShared, # type is 'shared' tfNotNil, # type cannot be 'nil' - + tfNeedsInit, # type constains a "not nil" constraint somewhere or some # other type so that it requires inititalization tfVarIsPtr, # 'var' type is translated like 'ptr' even in C++ mode @@ -520,7 +521,7 @@ const tfGcSafe* = tfThread tfObjHasKids* = tfEnumHasHoles skError* = skUnknown - + # type flags that are essential for type equality: eqTypeFlags* = {tfIterator, tfShared, tfNotNil, tfVarIsPtr} @@ -531,29 +532,29 @@ type mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mEcho, mShallowCopy, mSlurp, mStaticExec, mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst, - mUnaryLt, mSucc, - mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray, - mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref, - mGCunref, mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64, + mUnaryLt, mSucc, + mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray, + mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref, + mGCunref, mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64, mDivI64, mModI64, mAddF64, mSubF64, mMulF64, mDivF64, - mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI, + mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI, mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinI64, mMaxI64, - mMinF64, mMaxF64, mAddU, mSubU, mMulU, + mMinF64, mMaxF64, mAddU, mSubU, mMulU, mDivU, mModU, mEqI, mLeI, - mLtI, - mEqI64, mLeI64, mLtI64, mEqF64, mLeF64, mLtF64, - mLeU, mLtU, mLeU64, mLtU64, - mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef, + mLtI, + mEqI64, mLeI64, mLtI64, mEqF64, mLeF64, mLtF64, + mLeU, mLtU, mLeU64, mLtU64, + mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mEqProc, mUnaryMinusI, - mUnaryMinusI64, mAbsI, mAbsI64, mNot, - mUnaryPlusI, mBitnotI, mUnaryPlusI64, - mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64, - mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32, - mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr, - mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, - mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet, - mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mConArrArr, mConArrT, + mUnaryMinusI64, mAbsI, mAbsI64, mNot, + mUnaryPlusI, mBitnotI, mUnaryPlusI64, + mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64, + mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32, + mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr, + mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, + mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet, + mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mConArrArr, mConArrT, mConTArr, mConTT, mSlice, mFields, mFieldPairs, mOmpParFor, mAppendStrCh, mAppendStrStr, mAppendSeqElem, @@ -584,36 +585,36 @@ type # things that we can evaluate safely at compile time, even if not asked for it: const - ctfeWhitelist* = {mNone, mUnaryLt, mSucc, - mPred, mInc, mDec, mOrd, mLengthOpenArray, - mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, - mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64, + ctfeWhitelist* = {mNone, mUnaryLt, mSucc, + mPred, mInc, mDec, mOrd, mLengthOpenArray, + mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, + mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64, mDivI64, mModI64, mAddF64, mSubF64, mMulF64, mDivF64, - mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI, + mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI, mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinI64, mMaxI64, - mMinF64, mMaxF64, mAddU, mSubU, mMulU, + mMinF64, mMaxF64, mAddU, mSubU, mMulU, mDivU, mModU, mEqI, mLeI, - mLtI, - mEqI64, mLeI64, mLtI64, mEqF64, mLeF64, mLtF64, - mLeU, mLtU, mLeU64, mLtU64, - mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef, - mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI, - mUnaryMinusI64, mAbsI, mAbsI64, mNot, - mUnaryPlusI, mBitnotI, mUnaryPlusI64, - mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64, - mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32, - mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr, - mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, - mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet, - mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mConArrArr, mConArrT, + mLtI, + mEqI64, mLeI64, mLtI64, mEqF64, mLeF64, mLtF64, + mLeU, mLtU, mLeU64, mLtU64, + mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef, + mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI, + mUnaryMinusI64, mAbsI, mAbsI64, mNot, + mUnaryPlusI, mBitnotI, mUnaryPlusI64, + mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64, + mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32, + mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr, + mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, + mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet, + mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mConArrArr, mConArrT, mConTArr, mConTT, - mAppendStrCh, mAppendStrStr, mAppendSeqElem, + mAppendStrCh, mAppendStrStr, mAppendSeqElem, mInRange, mInSet, mRepr, mCopyStr, mCopyStrLast} # magics that require special semantic checking and # thus cannot be overloaded (also documented in the spec!): SpecialSemMagics* = { - mDefined, mDefinedInScope, mCompiles, mLow, mHigh, mSizeOf, mIs, mOf, + mDefined, mDefinedInScope, mCompiles, mLow, mHigh, mSizeOf, mIs, mOf, mEcho, mShallowCopy, mExpandToAst, mParallel, mSpawn, mAstToStr} type @@ -634,21 +635,21 @@ type floatVal*: BiggestFloat of nkStrLit..nkTripleStrLit: strVal*: string - of nkSym: + of nkSym: sym*: PSym - of nkIdent: + of nkIdent: ident*: PIdent else: sons*: TNodeSeq comment*: string - + TSymSeq* = seq[PSym] TStrTable* = object # a table[PIdent] of PSym counter*: int data*: TSymSeq - + # -------------- backend information ------------------------------- - TLocKind* = enum + TLocKind* = enum locNone, # no location locTemp, # temporary location locLocalVar, # location is a local variable @@ -660,7 +661,7 @@ type locData, # location is a constant locCall, # location is a call expression locOther # location is something other - TLocFlag* = enum + TLocFlag* = enum lfIndirect, # backend introduced a pointer lfParamCopy, # backend introduced a parameter copy (LLVM) lfNoDeepCopy, # no need for a deep copy @@ -670,13 +671,13 @@ type lfHeader, # include header file for symbol lfImportCompilerProc, # ``importc`` of a compilerproc lfSingleUse # no location yet and will only be used once - TStorageLoc* = enum + TStorageLoc* = enum OnUnknown, # location is unknown (stack, heap or static) OnStack, # location is on hardware stack OnHeap # location is on heap or global # (reference counting needed) TLocFlags* = set[TLocFlag] - TLoc* = object + TLoc* = object k*: TLocKind # kind of location s*: TStorageLoc flags*: TLocFlags # location's flags @@ -688,7 +689,7 @@ type # ---------------- end of backend information ------------------------------ - TLibKind* = enum + TLibKind* = enum libHeader, libDynamic TLib* = object of lists.TListEntry # also misused for headers! kind*: TLibKind @@ -696,17 +697,17 @@ type isOverriden*: bool name*: PRope path*: PNode # can be a string literal! - + TInstantiation* = object sym*: PSym concreteTypes*: seq[PType] usedBy*: seq[int32] # list of modules using the generic # needed in caas mode for purging the cache - # XXX: it's possible to switch to a + # XXX: it's possible to switch to a # simple ref count here - + PInstantiation* = ref TInstantiation - + TScope* = object depthLevel*: int symbols*: TStrTable @@ -773,7 +774,7 @@ type constraint*: PNode # additional constraints like 'lit|result'; also # misused for the codegenDecl pragma in the hope # it won't cause problems - + TTypeSeq* = seq[PType] TLockLevel* = distinct int16 TType* {.acyclic.} = object of TIdObj # \ @@ -806,7 +807,7 @@ type lockLevel*: TLockLevel # lock level as required for deadlock checking loc*: TLoc - TPair*{.final.} = object + TPair*{.final.} = object key*, val*: RootRef TPairSeq* = seq[TPair] @@ -814,7 +815,7 @@ type counter*: int data*: TPairSeq - TIdPair*{.final.} = object + TIdPair*{.final.} = object key*: PIdObj val*: RootRef @@ -823,7 +824,7 @@ type counter*: int data*: TIdPairSeq - TIdNodePair*{.final.} = object + TIdNodePair*{.final.} = object key*: PIdObj val*: PNode @@ -832,7 +833,7 @@ type counter*: int data*: TIdNodePairSeq - TNodePair*{.final.} = object + TNodePair*{.final.} = object h*: THash # because it is expensive to compute! key*: PNode val*: int @@ -844,7 +845,7 @@ type data*: TNodePairSeq TObjectSeq* = seq[RootRef] - TObjectSet*{.final.} = object + TObjectSet*{.final.} = object counter*: int data*: TObjectSeq @@ -855,27 +856,27 @@ type # same name as an imported module. This is necessary because of # the poor naming choices in the standard library. -const +const OverloadableSyms* = {skProc, skMethod, skIterator, skClosureIterator, skConverter, skModule, skTemplate, skMacro} - GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody, + GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody, tyGenericParam} - - StructuralEquivTypes*: TTypeKinds = {tyArrayConstr, tyNil, tyTuple, tyArray, + + StructuralEquivTypes*: TTypeKinds = {tyArrayConstr, tyNil, tyTuple, tyArray, tySet, tyRange, tyPtr, tyRef, tyVar, tySequence, tyProc, tyOpenArray, tyVarargs} - + ConcreteTypes*: TTypeKinds = { # types of the expr that may occur in:: # var x = expr - tyBool, tyChar, tyEnum, tyArray, tyObject, + tyBool, tyChar, tyEnum, tyArray, tyObject, tySet, tyTuple, tyRange, tyPtr, tyRef, tyVar, tySequence, tyProc, - tyPointer, + tyPointer, tyOpenArray, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128, tyUInt..tyUInt64} IntegralTypes* = {tyBool, tyChar, tyEnum, tyInt..tyInt64, tyFloat..tyFloat128, tyUInt..tyUInt64} - ConstantDataTypes*: TTypeKinds = {tyArrayConstr, tyArray, tySet, + ConstantDataTypes*: TTypeKinds = {tyArrayConstr, tyArray, tySet, tyTuple, tySequence} NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence, tyProc, tyString, tyError} @@ -926,17 +927,17 @@ proc discardSons*(father: PNode) proc len*(n: PNode): int {.inline.} = if isNil(n.sons): result = 0 else: result = len(n.sons) - + proc safeLen*(n: PNode): int {.inline.} = ## works even for leaves. if n.kind in {nkNone..nkNilLit} or isNil(n.sons): result = 0 else: result = len(n.sons) - + proc add*(father, son: PNode) = assert son != nil if isNil(father.sons): father.sons = @[] add(father.sons, son) - + proc `[]`*(n: PNode, i: int): PNode {.inline.} = result = n.sons[i] @@ -946,13 +947,10 @@ template `{}=`*(n: PNode, i: int, s: PNode): stmt = n.sons[i -| n] = s when defined(useNodeIds): - const nodeIdToDebug* = -1 # 884953 # 612794 - #612840 # 612905 # 614635 # 614637 # 614641 - # 423408 - #429107 # 430443 # 441048 # 441090 # 441153 + const nodeIdToDebug* = -1 # 299750 # 300761 #300863 # 300879 var gNodeId: int -proc newNode*(kind: TNodeKind): PNode = +proc newNode*(kind: TNodeKind): PNode = new(result) result.kind = kind #result.info = UnknownLineInfo() inlined: @@ -966,24 +964,24 @@ proc newNode*(kind: TNodeKind): PNode = writeStackTrace() inc gNodeId -proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode = +proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode = result = newNode(kind) result.intVal = intVal -proc newIntTypeNode*(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode = +proc newIntTypeNode*(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode = result = newIntNode(kind, intVal) result.typ = typ -proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode = +proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode = result = newNode(kind) result.floatVal = floatVal -proc newStrNode*(kind: TNodeKind, strVal: string): PNode = +proc newStrNode*(kind: TNodeKind, strVal: string): PNode = result = newNode(kind) result.strVal = strVal proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym, - info: TLineInfo): PSym = + info: TLineInfo): PSym = # generates a symbol and initializes the hash field too new(result) result.name = name @@ -994,7 +992,7 @@ proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym, result.owner = owner result.offset = - 1 result.id = getID() - when debugIds: + when debugIds: registerId(result) #if result.id < 2000: # MessageOut(name.s & " has id: " & toString(result.id)) @@ -1036,54 +1034,54 @@ proc appendToModule*(m: PSym, n: PNode) = else: assert m.ast.kind == nkStmtList m.ast.sons.add(n) - + const # for all kind of hash tables: GrowthFactor* = 2 # must be power of 2, > 0 StartSize* = 8 # must be power of 2, > 0 -proc copyStrTable*(dest: var TStrTable, src: TStrTable) = +proc copyStrTable*(dest: var TStrTable, src: TStrTable) = dest.counter = src.counter - if isNil(src.data): return + if isNil(src.data): return setLen(dest.data, len(src.data)) for i in countup(0, high(src.data)): dest.data[i] = src.data[i] - -proc copyIdTable*(dest: var TIdTable, src: TIdTable) = + +proc copyIdTable*(dest: var TIdTable, src: TIdTable) = dest.counter = src.counter - if isNil(src.data): return + if isNil(src.data): return newSeq(dest.data, len(src.data)) for i in countup(0, high(src.data)): dest.data[i] = src.data[i] - -proc copyTable*(dest: var TTable, src: TTable) = + +proc copyTable*(dest: var TTable, src: TTable) = dest.counter = src.counter - if isNil(src.data): return + if isNil(src.data): return setLen(dest.data, len(src.data)) for i in countup(0, high(src.data)): dest.data[i] = src.data[i] - -proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) = + +proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) = dest.counter = src.counter - if isNil(src.data): return + if isNil(src.data): return setLen(dest.data, len(src.data)) for i in countup(0, high(src.data)): dest.data[i] = src.data[i] - -proc discardSons*(father: PNode) = + +proc discardSons*(father: PNode) = father.sons = nil proc withInfo*(n: PNode, info: TLineInfo): PNode = n.info = info return n -proc newIdentNode*(ident: PIdent, info: TLineInfo): PNode = +proc newIdentNode*(ident: PIdent, info: TLineInfo): PNode = result = newNode(nkIdent) result.ident = ident result.info = info -proc newSymNode*(sym: PSym): PNode = +proc newSymNode*(sym: PSym): PNode = result = newNode(nkSym) result.sym = sym result.typ = sym.typ result.info = sym.info -proc newSymNode*(sym: PSym, info: TLineInfo): PNode = +proc newSymNode*(sym: PSym, info: TLineInfo): PNode = result = newNode(nkSym) result.sym = sym result.typ = sym.typ @@ -1128,12 +1126,12 @@ proc newNode*(kind: TNodeKind, info: TLineInfo, sons: TNodeSeq = @[], writeStackTrace() inc gNodeId -proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode = +proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode = result = newNode(kind) result.info = info result.typ = typ -proc addSon*(father, son: PNode) = +proc addSon*(father, son: PNode) = assert son != nil if isNil(father.sons): father.sons = @[] add(father.sons, son) @@ -1159,7 +1157,7 @@ proc `$`*(x: TLockLevel): string = elif x.ord == UnknownLockLevel.ord: result = "<unknown>" else: result = $int16(x) -proc newType*(kind: TTypeKind, owner: PSym): PType = +proc newType*(kind: TTypeKind, owner: PSym): PType = new(result) result.kind = kind result.owner = owner @@ -1171,7 +1169,7 @@ proc newType*(kind: TTypeKind, owner: PSym): PType = registerId(result) #if result.id < 2000: # messageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id)) - + proc mergeLoc(a: var TLoc, b: TLoc) = if a.k == low(a.k): a.k = b.k if a.s == low(a.s): a.s = b.s @@ -1180,37 +1178,37 @@ proc mergeLoc(a: var TLoc, b: TLoc) = if a.r == nil: a.r = b.r #if a.a == 0: a.a = b.a -proc newSons*(father: PNode, length: int) = - if isNil(father.sons): +proc newSons*(father: PNode, length: int) = + if isNil(father.sons): newSeq(father.sons, length) else: setLen(father.sons, length) -proc newSons*(father: PType, length: int) = - if isNil(father.sons): +proc newSons*(father: PType, length: int) = + if isNil(father.sons): newSeq(father.sons, length) else: setLen(father.sons, length) -proc sonsLen*(n: PType): int = +proc sonsLen*(n: PType): int = if isNil(n.sons): result = 0 else: result = len(n.sons) -proc len*(n: PType): int = +proc len*(n: PType): int = if isNil(n.sons): result = 0 else: result = len(n.sons) - -proc sonsLen*(n: PNode): int = + +proc sonsLen*(n: PNode): int = if isNil(n.sons): result = 0 else: result = len(n.sons) - -proc lastSon*(n: PNode): PNode = + +proc lastSon*(n: PNode): PNode = result = n.sons[sonsLen(n) - 1] -proc lastSon*(n: PType): PType = +proc lastSon*(n: PType): PType = result = n.sons[sonsLen(n) - 1] -proc assignType*(dest, src: PType) = +proc assignType*(dest, src: PType) = dest.kind = src.kind dest.flags = src.flags dest.callConv = src.callConv @@ -1230,23 +1228,23 @@ proc assignType*(dest, src: PType) = dest.sym = src.sym newSons(dest, sonsLen(src)) for i in countup(0, sonsLen(src) - 1): dest.sons[i] = src.sons[i] - -proc copyType*(t: PType, owner: PSym, keepId: bool): PType = + +proc copyType*(t: PType, owner: PSym, keepId: bool): PType = result = newType(t.kind, owner) assignType(result, t) - if keepId: + if keepId: result.id = t.id - else: + else: when debugIds: registerId(result) result.sym = t.sym # backend-info should not be copied - -proc copySym*(s: PSym, keepId: bool = false): PSym = + +proc copySym*(s: PSym, keepId: bool = false): PSym = result = newSym(s.kind, s.name, s.owner, s.info) #result.ast = nil # BUGFIX; was: s.ast which made problems result.typ = s.typ if keepId: result.id = s.id - else: + else: result.id = getID() when debugIds: registerId(result) result.flags = s.flags @@ -1273,19 +1271,19 @@ proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo): PSym = result.annex = s.annex # XXX once usedGenerics is used, ensure module aliases keep working! assert s.usedGenerics == nil - -proc initStrTable*(x: var TStrTable) = + +proc initStrTable*(x: var TStrTable) = x.counter = 0 newSeq(x.data, StartSize) proc newStrTable*: TStrTable = initStrTable(result) -proc initTable(x: var TTable) = +proc initTable(x: var TTable) = x.counter = 0 newSeq(x.data, StartSize) -proc initIdTable*(x: var TIdTable) = +proc initIdTable*(x: var TIdTable) = x.counter = 0 newSeq(x.data, StartSize) @@ -1295,15 +1293,15 @@ proc resetIdTable*(x: var TIdTable) = setLen(x.data, 0) setLen(x.data, StartSize) -proc initObjectSet*(x: var TObjectSet) = +proc initObjectSet*(x: var TObjectSet) = x.counter = 0 newSeq(x.data, StartSize) -proc initIdNodeTable*(x: var TIdNodeTable) = +proc initIdNodeTable*(x: var TIdNodeTable) = x.counter = 0 newSeq(x.data, StartSize) -proc initNodeTable*(x: var TNodeTable) = +proc initNodeTable*(x: var TNodeTable) = x.counter = 0 newSeq(x.data, StartSize) @@ -1327,11 +1325,11 @@ proc propagateToOwner*(owner, elem: PType) = owner.flags.incl tfNotNil elif owner.kind notin HaveTheirOwnEmpty: owner.flags.incl tfNeedsInit - + if tfNeedsInit in elem.flags: if owner.kind in HaveTheirOwnEmpty: discard else: owner.flags.incl tfNeedsInit - + if elem.isMetaType: owner.flags.incl tfHasMeta @@ -1348,15 +1346,15 @@ proc addSonNilAllowed*(father, son: PNode) = if isNil(father.sons): father.sons = @[] add(father.sons, son) -proc delSon*(father: PNode, idx: int) = - if isNil(father.sons): return +proc delSon*(father: PNode, idx: int) = + if isNil(father.sons): return var length = sonsLen(father) for i in countup(idx, length - 2): father.sons[i] = father.sons[i + 1] setLen(father.sons, length - 1) -proc copyNode*(src: PNode): PNode = +proc copyNode*(src: PNode): PNode = # does not copy its sons! - if src == nil: + if src == nil: return nil result = newNode(src.kind) result.info = src.info @@ -1373,7 +1371,7 @@ proc copyNode*(src: PNode): PNode = of nkStrLit..nkTripleStrLit: result.strVal = src.strVal else: discard -proc shallowCopy*(src: PNode): PNode = +proc shallowCopy*(src: PNode): PNode = # does not copy its sons, but provides space for them: if src == nil: return nil result = newNode(src.kind) @@ -1391,9 +1389,9 @@ proc shallowCopy*(src: PNode): PNode = of nkStrLit..nkTripleStrLit: result.strVal = src.strVal else: newSeq(result.sons, sonsLen(src)) -proc copyTree*(src: PNode): PNode = +proc copyTree*(src: PNode): PNode = # copy a whole syntax tree; performs deep copying - if src == nil: + if src == nil: return nil result = newNode(src.kind) result.info = src.info @@ -1408,20 +1406,20 @@ proc copyTree*(src: PNode): PNode = of nkSym: result.sym = src.sym of nkIdent: result.ident = src.ident of nkStrLit..nkTripleStrLit: result.strVal = src.strVal - else: + else: newSeq(result.sons, sonsLen(src)) - for i in countup(0, sonsLen(src) - 1): + for i in countup(0, sonsLen(src) - 1): result.sons[i] = copyTree(src.sons[i]) -proc hasSonWith*(n: PNode, kind: TNodeKind): bool = - for i in countup(0, sonsLen(n) - 1): - if n.sons[i].kind == kind: +proc hasSonWith*(n: PNode, kind: TNodeKind): bool = + for i in countup(0, sonsLen(n) - 1): + if n.sons[i].kind == kind: return true result = false -proc hasNilSon*(n: PNode): bool = - for i in countup(0, safeLen(n) - 1): - if n.sons[i] == nil: +proc hasNilSon*(n: PNode): bool = + for i in countup(0, safeLen(n) - 1): + if n.sons[i] == nil: return true elif hasNilSon(n.sons[i]): return true @@ -1435,47 +1433,47 @@ proc containsNode*(n: PNode, kinds: TNodeKinds): bool = for i in countup(0, sonsLen(n) - 1): if n.kind in kinds or containsNode(n.sons[i], kinds): return true -proc hasSubnodeWith*(n: PNode, kind: TNodeKind): bool = +proc hasSubnodeWith*(n: PNode, kind: TNodeKind): bool = case n.kind of nkEmpty..nkNilLit: result = n.kind == kind - else: - for i in countup(0, sonsLen(n) - 1): - if (n.sons[i].kind == kind) or hasSubnodeWith(n.sons[i], kind): + else: + for i in countup(0, sonsLen(n) - 1): + if (n.sons[i].kind == kind) or hasSubnodeWith(n.sons[i], kind): return true result = false -proc replaceSons(n: PNode, oldKind, newKind: TNodeKind) = - for i in countup(0, sonsLen(n) - 1): +proc replaceSons(n: PNode, oldKind, newKind: TNodeKind) = + for i in countup(0, sonsLen(n) - 1): if n.sons[i].kind == oldKind: n.sons[i].kind = newKind - -proc sonsNotNil(n: PNode): bool = - for i in countup(0, sonsLen(n) - 1): - if n.sons[i] == nil: + +proc sonsNotNil(n: PNode): bool = + for i in countup(0, sonsLen(n) - 1): + if n.sons[i] == nil: return false result = true -proc getInt*(a: PNode): BiggestInt = +proc getInt*(a: PNode): BiggestInt = case a.kind of nkIntLit..nkUInt64Lit: result = a.intVal - else: + else: internalError(a.info, "getInt") result = 0 -proc getFloat*(a: PNode): BiggestFloat = +proc getFloat*(a: PNode): BiggestFloat = case a.kind of nkFloatLit..nkFloat128Lit: result = a.floatVal - else: + else: internalError(a.info, "getFloat") result = 0.0 -proc getStr*(a: PNode): string = +proc getStr*(a: PNode): string = case a.kind of nkStrLit..nkTripleStrLit: result = a.strVal - else: + else: internalError(a.info, "getStr") result = "" -proc getStrOrChar*(a: PNode): string = +proc getStrOrChar*(a: PNode): string = case a.kind of nkStrLit..nkTripleStrLit: result = a.strVal of nkCharLit..nkUInt64Lit: result = $chr(int(a.intVal)) @@ -1483,7 +1481,7 @@ proc getStrOrChar*(a: PNode): string = internalError(a.info, "getStrOrChar") result = "" -proc isGenericRoutine*(s: PSym): bool = +proc isGenericRoutine*(s: PSym): bool = case s.kind of skProcKinds: result = sfFromGeneric in s.flags or diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 8d132ab26..c53e53b88 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -203,9 +203,9 @@ proc mustRehash(length, counter: int): bool = assert(length > counter) result = (length * 2 < counter * 3) or (length - counter < 4) -proc spaces(x: int): PRope = +proc rspaces(x: int): PRope = # returns x spaces - result = toRope(repeatChar(x)) + result = toRope(spaces(x)) proc toYamlChar(c: char): string = case c @@ -253,7 +253,7 @@ proc typeToYamlAux(n: PType, marker: var IntSet, indent, maxRecDepth: int): PRope proc strTableToYaml(n: TStrTable, marker: var IntSet, indent: int, maxRecDepth: int): PRope = - var istr = spaces(indent + 2) + var istr = rspaces(indent + 2) result = toRope("[") var mycount = 0 for i in countup(0, high(n.data)): @@ -262,20 +262,20 @@ proc strTableToYaml(n: TStrTable, marker: var IntSet, indent: int, appf(result, "$N$1$2", [istr, symToYamlAux(n.data[i], marker, indent + 2, maxRecDepth - 1)]) inc(mycount) - if mycount > 0: appf(result, "$N$1", [spaces(indent)]) + if mycount > 0: appf(result, "$N$1", [rspaces(indent)]) app(result, "]") assert(mycount == n.counter) proc ropeConstr(indent: int, c: openArray[PRope]): PRope = # array of (name, value) pairs - var istr = spaces(indent + 2) + var istr = rspaces(indent + 2) result = toRope("{") var i = 0 while i <= high(c): if i > 0: app(result, ",") appf(result, "$N$1\"$2\": $3", [istr, c[i], c[i + 1]]) inc(i, 2) - appf(result, "$N$1}", [spaces(indent)]) + appf(result, "$N$1}", [rspaces(indent)]) proc symToYamlAux(n: PSym, marker: var IntSet, indent: int, maxRecDepth: int): PRope = @@ -310,9 +310,9 @@ proc typeToYamlAux(n: PType, marker: var IntSet, indent: int, result = toRope("[") for i in countup(0, sonsLen(n) - 1): if i > 0: app(result, ",") - appf(result, "$N$1$2", [spaces(indent + 4), typeToYamlAux(n.sons[i], + appf(result, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(n.sons[i], marker, indent + 4, maxRecDepth - 1)]) - appf(result, "$N$1]", [spaces(indent + 2)]) + appf(result, "$N$1]", [rspaces(indent + 2)]) else: result = toRope("null") result = ropeConstr(indent, [toRope("kind"), @@ -331,7 +331,7 @@ proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int, if n == nil: result = toRope("null") else: - var istr = spaces(indent + 2) + var istr = rspaces(indent + 2) result = ropef("{$N$1\"kind\": $2", [istr, makeYamlString($n.kind)]) if maxRecDepth != 0: appf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)]) @@ -359,12 +359,12 @@ proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int, appf(result, ",$N$1\"sons\": [", [istr]) for i in countup(0, sonsLen(n) - 1): if i > 0: app(result, ",") - appf(result, "$N$1$2", [spaces(indent + 4), treeToYamlAux(n.sons[i], + appf(result, "$N$1$2", [rspaces(indent + 4), treeToYamlAux(n.sons[i], marker, indent + 4, maxRecDepth - 1)]) appf(result, "$N$1]", [istr]) appf(result, ",$N$1\"typ\": $2", [istr, typeToYamlAux(n.typ, marker, indent + 2, maxRecDepth)]) - appf(result, "$N$1}", [spaces(indent)]) + appf(result, "$N$1}", [rspaces(indent)]) proc treeToYaml(n: PNode, indent: int = 0, maxRecDepth: int = - 1): PRope = var marker = initIntSet() @@ -408,7 +408,7 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int; if n == nil: result = toRope("null") else: - var istr = spaces(indent + 2) + var istr = rspaces(indent + 2) result = ropef("{$N$1\"kind\": $2", [istr, makeYamlString($n.kind)]) if maxRecDepth != 0: @@ -440,11 +440,11 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int; appf(result, ",$N$1\"sons\": [", [istr]) for i in countup(0, sonsLen(n) - 1): if i > 0: app(result, ",") - appf(result, "$N$1$2", [spaces(indent + 4), debugTree(n.sons[i], + appf(result, "$N$1$2", [rspaces(indent + 4), debugTree(n.sons[i], indent + 4, maxRecDepth - 1, renderType)]) appf(result, "$N$1]", [istr]) appf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)]) - appf(result, "$N$1}", [spaces(indent)]) + appf(result, "$N$1}", [rspaces(indent)]) proc debug(n: PSym) = if n == nil: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 5f5aa6308..564d1fd36 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -949,7 +949,7 @@ proc genEcho(p: BProc, n: PNode) = initLocExpr(p, n.sons[i], a) appf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)]) linefmt(p, cpsStmts, "printf($1$2);$n", - makeCString(repeatStr(n.len, "%s") & tnl), args) + makeCString(repeat("%s", n.len) & tnl), args) proc gcUsage(n: PNode) = if gSelectedGC == gcNone: message(n.info, warnGcMem, n.renderTree) diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index 59b9611fc..25c1a12e5 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -9,31 +9,31 @@ # This module declares some helpers for the C code generator. -import - ast, astalgo, ropes, lists, hashes, strutils, types, msgs, wordrecg, +import + ast, astalgo, ropes, lists, hashes, strutils, types, msgs, wordrecg, platform, trees proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode = case n.kind - of nkStmtList: - for i in 0 .. < n.len: + of nkStmtList: + for i in 0 .. < n.len: result = getPragmaStmt(n[i], w) if result != nil: break of nkPragma: - for i in 0 .. < n.len: + for i in 0 .. < n.len: if whichPragma(n[i]) == w: return n[i] else: discard proc stmtsContainPragma*(n: PNode, w: TSpecialWord): bool = result = getPragmaStmt(n, w) != nil -proc hashString*(s: string): BiggestInt = +proc hashString*(s: string): BiggestInt = # has to be the same algorithm as system.hashString! - if CPU[targetCPU].bit == 64: + if CPU[targetCPU].bit == 64: # we have to use the same bitwidth # as the target CPU var b = 0'i64 - for i in countup(0, len(s) - 1): + for i in countup(0, len(s) - 1): b = b +% ord(s[i]) b = b +% `shl`(b, 10) b = b xor `shr`(b, 6) @@ -41,9 +41,9 @@ proc hashString*(s: string): BiggestInt = b = b xor `shr`(b, 11) b = b +% `shl`(b, 15) result = b - else: + else: var a = 0'i32 - for i in countup(0, len(s) - 1): + for i in countup(0, len(s) - 1): a = a +% ord(s[i]).int32 a = a +% `shl`(a, 10'i32) a = a xor `shr`(a, 6'i32) @@ -52,11 +52,11 @@ proc hashString*(s: string): BiggestInt = a = a +% `shl`(a, 15'i32) result = a -var +var gTypeTable: array[TTypeKind, TIdTable] gCanonicalTypes: array[TTypeKind, PType] -proc initTypeTables() = +proc initTypeTables() = for i in countup(low(TTypeKind), high(TTypeKind)): initIdTable(gTypeTable[i]) proc resetCaches* = @@ -67,7 +67,7 @@ proc resetCaches* = when false: proc echoStats*() = - for i in countup(low(TTypeKind), high(TTypeKind)): + for i in countup(low(TTypeKind), high(TTypeKind)): echo i, " ", gTypeTable[i].counter proc slowSearch(key: PType; k: TTypeKind): PType = @@ -78,21 +78,21 @@ proc slowSearch(key: PType; k: TTypeKind): PType = if idTableHasObjectAsKey(gTypeTable[k], key): return key for h in countup(0, high(gTypeTable[k].data)): var t = PType(gTypeTable[k].data[h].key) - if t != nil and sameBackendType(t, key): + if t != nil and sameBackendType(t, key): return t idTablePut(gTypeTable[k], key, key) result = key -proc getUniqueType*(key: PType): PType = +proc getUniqueType*(key: PType): PType = # this is a hotspot in the compiler! - if key == nil: return + if key == nil: return var k = key.kind case k of tyBool, tyChar, tyInt..tyUInt64: # no canonicalization for integral types, so that e.g. ``pid_t`` is # produced instead of ``NI``. result = key - of tyEmpty, tyNil, tyExpr, tyStmt, tyPointer, tyString, + of tyEmpty, tyNil, tyExpr, tyStmt, tyPointer, tyString, tyCString, tyNone, tyBigNum: result = gCanonicalTypes[k] if result == nil: @@ -127,7 +127,7 @@ proc getUniqueType*(key: PType): PType = if tfFromGeneric notin key.flags: # fast case; lookup per id suffices: result = PType(idTableGet(gTypeTable[k], key)) - if result == nil: + if result == nil: idTablePut(gTypeTable[k], key, key) result = key else: @@ -138,10 +138,10 @@ proc getUniqueType*(key: PType): PType = if t != nil and sameBackendType(t, key): return t idTablePut(gTypeTable[k], key, key) - result = key + result = key of tyEnum: result = PType(idTableGet(gTypeTable[k], key)) - if result == nil: + if result == nil: idTablePut(gTypeTable[k], key, key) result = key of tyProc: @@ -151,16 +151,16 @@ proc getUniqueType*(key: PType): PType = # ugh, we need the canon here: result = slowSearch(key, k) -proc tableGetType*(tab: TIdTable, key: PType): RootRef = +proc tableGetType*(tab: TIdTable, key: PType): RootRef = # returns nil if we need to declare this type result = idTableGet(tab, key) - if (result == nil) and (tab.counter > 0): + if (result == nil) and (tab.counter > 0): # we have to do a slow linear search because types may need # to be compared by their structure: - for h in countup(0, high(tab.data)): + for h in countup(0, high(tab.data)): var t = PType(tab.data[h].key) - if t != nil: - if sameType(t, key): + if t != nil: + if sameType(t, key): return tab.data[h].val proc makeSingleLineCString*(s: string): string = @@ -193,16 +193,16 @@ proc mangle*(name: string): string = else: add(result, "HEX" & toHex(ord(c), 2)) -proc makeLLVMString*(s: string): PRope = +proc makeLLVMString*(s: string): PRope = const MaxLineLength = 64 result = nil var res = "c\"" - for i in countup(0, len(s) - 1): - if (i + 1) mod MaxLineLength == 0: + for i in countup(0, len(s) - 1): + if (i + 1) mod MaxLineLength == 0: app(result, toRope(res)) setLen(res, 0) case s[i] - of '\0'..'\x1F', '\x80'..'\xFF', '\"', '\\': + of '\0'..'\x1F', '\x80'..'\xFF', '\"', '\\': add(res, '\\') add(res, toHex(ord(s[i]), 2)) else: add(res, s[i]) diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index b98679ac6..b1a23802d 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -164,6 +164,7 @@ proc packObject(x: PNode, typ: PType, res: pointer) = let field = getField(typ.n, i) pack(it, field.typ, res +! field.offset) else: + # XXX: todo globalError(x.info, "cannot pack unnamed tuple") const maxPackDepth = 20 diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 29657a2ae..8959aa4df 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -10,7 +10,7 @@ ## Template evaluation engine. Now hygienic. import - strutils, options, ast, astalgo, msgs, os, idents, wordrecg, renderer, + strutils, options, ast, astalgo, msgs, os, idents, wordrecg, renderer, rodread type @@ -49,7 +49,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = result.add copyNode(c, templ, actual) else: var res = copyNode(c, templ, actual) - for i in countup(0, sonsLen(templ) - 1): + for i in countup(0, sonsLen(templ) - 1): evalTemplateAux(templ.sons[i], actual, c, res) result.add res @@ -86,9 +86,9 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym): PNode = ctx.owner = tmpl ctx.genSymOwner = genSymOwner initIdTable(ctx.mapping) - + let body = tmpl.getBody - if isAtom(body): + if isAtom(body): result = newNodeI(nkPar, body.info) evalTemplateAux(body, args, ctx, result) if result.len == 1: result = result.sons[0] @@ -102,5 +102,5 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym): PNode = if ctx.instLines: result.info = n.info for i in countup(0, safeLen(body) - 1): evalTemplateAux(body.sons[i], args, ctx, result) - + dec(evalTemplateCounter) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 5f6033e57..8888632b9 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -572,6 +572,9 @@ proc footprint(filename: string): TCrc32 = getCompileCFileCmd(filename, true) proc externalFileChanged(filename: string): bool = + if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}: + return false + var crcFile = toGeneratedFile(filename.withPackageName, "crc") var currentCrc = int(footprint(filename)) var f: File diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim index 7b975dbaa..5d1f73be4 100644 --- a/compiler/filter_tmpl.nim +++ b/compiler/filter_tmpl.nim @@ -37,11 +37,11 @@ const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'} proc newLine(p: var TTmplParser) = - llStreamWrite(p.outp, repeatChar(p.emitPar, ')')) + llStreamWrite(p.outp, repeat(')', p.emitPar)) p.emitPar = 0 if p.info.line > int16(1): llStreamWrite(p.outp, "\n") if p.pendingExprLine: - llStreamWrite(p.outp, repeatChar(2)) + llStreamWrite(p.outp, spaces(2)) p.pendingExprLine = false proc scanPar(p: var TTmplParser, d: int) = @@ -88,24 +88,24 @@ proc parseLine(p: var TTmplParser) = else: p.info.col = int16(j) localError(p.info, errXNotAllowedHere, "end") - llStreamWrite(p.outp, repeatChar(p.indent)) + llStreamWrite(p.outp, spaces(p.indent)) llStreamWrite(p.outp, "#end") of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator, wConverter, wMacro, wTemplate, wMethod: - llStreamWrite(p.outp, repeatChar(p.indent)) + llStreamWrite(p.outp, spaces(p.indent)) llStreamWrite(p.outp, substr(p.x, d)) inc(p.indent, 2) of wElif, wOf, wElse, wExcept, wFinally: - llStreamWrite(p.outp, repeatChar(p.indent - 2)) + llStreamWrite(p.outp, spaces(p.indent - 2)) llStreamWrite(p.outp, substr(p.x, d)) of wLet, wVar, wConst, wType: - llStreamWrite(p.outp, repeatChar(p.indent)) + llStreamWrite(p.outp, spaces(p.indent)) llStreamWrite(p.outp, substr(p.x, d)) if not p.x.contains({':', '='}): # no inline element --> treat as block: inc(p.indent, 2) else: - llStreamWrite(p.outp, repeatChar(p.indent)) + llStreamWrite(p.outp, spaces(p.indent)) llStreamWrite(p.outp, substr(p.x, d)) p.state = psDirective else: @@ -120,11 +120,11 @@ proc parseLine(p: var TTmplParser) = # next line of string literal: llStreamWrite(p.outp, p.conc) llStreamWrite(p.outp, "\n") - llStreamWrite(p.outp, repeatChar(p.indent + 2)) + llStreamWrite(p.outp, spaces(p.indent + 2)) llStreamWrite(p.outp, "\"") of psDirective: newLine(p) - llStreamWrite(p.outp, repeatChar(p.indent)) + llStreamWrite(p.outp, spaces(p.indent)) llStreamWrite(p.outp, p.emit) llStreamWrite(p.outp, "(\"") inc(p.emitPar) diff --git a/compiler/importer.nim b/compiler/importer.nim index fbf3be4f2..57a1e542b 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -27,7 +27,7 @@ proc getModuleName*(n: PNode): string = result = n.ident.s of nkSym: result = n.sym.name.s - of nkInfix: + of nkInfix, nkPrefix: if n.sons[0].kind == nkIdent and n.sons[0].ident.id == getIdent("as").id: # XXX hack ahead: n.kind = nkImportAs diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 34f842d4a..87847204f 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -780,7 +780,7 @@ proc genIf(p: PProc, n: PNode, r: var TCompRes) = moveInto(p, stmt, r) appf(p.body, "}$n" | "end$n") if p.target == targetJS: - app(p.body, repeatChar(toClose, '}') & tnl) + app(p.body, repeat('}', toClose) & tnl) else: for i in 1..toClose: appf(p.body, "end$n") diff --git a/compiler/msgs.nim b/compiler/msgs.nim index b8d49b65a..4ea67989a 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -786,7 +786,7 @@ proc rawMessage*(msg: TMsgKind, arg: string) = proc writeSurroundingSrc(info: TLineInfo) = const indent = " " msgWriteln(indent & info.sourceLine.ropeToStr) - msgWriteln(indent & repeatChar(info.col, ' ') & '^') + msgWriteln(indent & spaces(info.col) & '^') proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string = let frmt = case msg diff --git a/compiler/nimlexbase.nim b/compiler/nimlexbase.nim index e18e1c22a..f5db5ca4f 100644 --- a/compiler/nimlexbase.nim +++ b/compiler/nimlexbase.nim @@ -166,4 +166,4 @@ proc getCurrentLine(L: TBaseLexer, marker: bool = true): string = inc(i) result.add("\n") if marker: - result.add(repeatChar(getColNumber(L, L.bufpos)) & '^' & "\n") + result.add(spaces(getColNumber(L, L.bufpos)) & '^' & "\n") diff --git a/compiler/parser.nim b/compiler/parser.nim index 8fbf033d8..9e4d45cd2 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -865,6 +865,7 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode = #| [' optInd (identColonEquals (comma/semicolon)?)* optPar ']' #| extTupleDecl = 'tuple' #| COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)? + #| tupleClass = 'tuple' result = newNodeP(nkTupleTy, p) getTok(p) if p.tok.tokType == tkBracketLe: @@ -894,6 +895,8 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode = parMessage(p, errIdentifierExpected, p.tok) break if not sameInd(p): break + else: + result = newNodeP(nkTupleClassTy, p) proc parseParamList(p: var TParser, retColon = true): PNode = #| paramList = '(' declColonEquals ^* (comma/semicolon) ')' diff --git a/compiler/passes.nim b/compiler/passes.nim index 96088bd88..129d8ad47 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -10,15 +10,15 @@ # This module implements the passes functionality. A pass must implement the # `TPass` interface. -import - strutils, lists, options, ast, astalgo, llstream, msgs, platform, os, - condsyms, idents, renderer, types, extccomp, math, magicsys, nversion, +import + strutils, lists, options, ast, astalgo, llstream, msgs, platform, os, + condsyms, idents, renderer, types, extccomp, math, magicsys, nversion, nimsets, syntaxes, times, rodread, idgen -type +type TPassContext* = object of RootObj # the pass's context fromCache*: bool # true if created by "openCached" - + PPassContext* = ref TPassContext TPassOpen* = proc (module: PSym): PPassContext {.nimcall.} @@ -33,8 +33,8 @@ type TPassData* = tuple[input: PNode, closeOutput: PNode] TPasses* = openArray[TPass] -# a pass is a tuple of procedure vars ``TPass.close`` may produce additional -# nodes. These are passed to the other close procedures. +# a pass is a tuple of procedure vars ``TPass.close`` may produce additional +# nodes. These are passed to the other close procedures. # This mechanism used to be used for the instantiation of generics. proc makePass*(open: TPassOpen = nil, @@ -53,46 +53,46 @@ proc makePass*(open: TPassOpen = nil, proc processModule*(module: PSym, stream: PLLStream, rd: PRodReader) # the semantic checker needs these: -var +var gImportModule*: proc (m: PSym, fileIdx: int32): PSym {.nimcall.} gIncludeFile*: proc (m: PSym, fileIdx: int32): PNode {.nimcall.} # implementation -proc skipCodegen*(n: PNode): bool {.inline.} = - # can be used by codegen passes to determine whether they should do +proc skipCodegen*(n: PNode): bool {.inline.} = + # can be used by codegen passes to determine whether they should do # something with `n`. Currently, this ignores `n` and uses the global # error count instead. result = msgs.gErrorCounter > 0 -proc astNeeded*(s: PSym): bool = +proc astNeeded*(s: PSym): bool = # The ``rodwrite`` module uses this to determine if the body of a proc # needs to be stored. The passes manager frees s.sons[codePos] when # appropriate to free the procedure body's memory. This is important # to keep memory usage down. if (s.kind in {skMethod, skProc}) and ({sfCompilerProc, sfCompileTime} * s.flags == {}) and - (s.typ.callConv != ccInline) and - (s.ast.sons[genericParamsPos].kind == nkEmpty): + (s.typ.callConv != ccInline) and + (s.ast.sons[genericParamsPos].kind == nkEmpty): result = false # XXX this doesn't really make sense with excessive CTFE else: result = true - -const + +const maxPasses = 10 -type +type TPassContextArray = array[0..maxPasses - 1, PPassContext] -var +var gPasses: array[0..maxPasses - 1, TPass] gPassesLen*: int proc clearPasses* = gPassesLen = 0 -proc registerPass*(p: TPass) = +proc registerPass*(p: TPass) = gPasses[gPassesLen] = p inc(gPassesLen) @@ -109,48 +109,48 @@ proc carryPasses*(nodes: PNode, module: PSym, passes: TPasses) = passdata = carryPass(pass, module, passdata) proc openPasses(a: var TPassContextArray, module: PSym) = - for i in countup(0, gPassesLen - 1): - if not isNil(gPasses[i].open): + for i in countup(0, gPassesLen - 1): + if not isNil(gPasses[i].open): a[i] = gPasses[i].open(module) else: a[i] = nil - + proc openPassesCached(a: var TPassContextArray, module: PSym, rd: PRodReader) = - for i in countup(0, gPassesLen - 1): - if not isNil(gPasses[i].openCached): + for i in countup(0, gPassesLen - 1): + if not isNil(gPasses[i].openCached): a[i] = gPasses[i].openCached(module, rd) - if a[i] != nil: + if a[i] != nil: a[i].fromCache = true else: a[i] = nil - -proc closePasses(a: var TPassContextArray) = + +proc closePasses(a: var TPassContextArray) = var m: PNode = nil - for i in countup(0, gPassesLen - 1): + for i in countup(0, gPassesLen - 1): if not isNil(gPasses[i].close): m = gPasses[i].close(a[i], m) a[i] = nil # free the memory here - -proc processTopLevelStmt(n: PNode, a: var TPassContextArray): bool = + +proc processTopLevelStmt(n: PNode, a: var TPassContextArray): bool = # this implements the code transformation pipeline var m = n - for i in countup(0, gPassesLen - 1): - if not isNil(gPasses[i].process): + for i in countup(0, gPassesLen - 1): + if not isNil(gPasses[i].process): m = gPasses[i].process(a[i], m) if isNil(m): return false result = true - -proc processTopLevelStmtCached(n: PNode, a: var TPassContextArray) = + +proc processTopLevelStmtCached(n: PNode, a: var TPassContextArray) = # this implements the code transformation pipeline var m = n - for i in countup(0, gPassesLen - 1): + for i in countup(0, gPassesLen - 1): if not isNil(gPasses[i].openCached): m = gPasses[i].process(a[i], m) - -proc closePassesCached(a: var TPassContextArray) = + +proc closePassesCached(a: var TPassContextArray) = var m: PNode = nil - for i in countup(0, gPassesLen - 1): - if not isNil(gPasses[i].openCached) and not isNil(gPasses[i].close): + for i in countup(0, gPassesLen - 1): + if not isNil(gPasses[i].openCached) and not isNil(gPasses[i].close): m = gPasses[i].close(a[i], m) a[i] = nil # free the memory here - + proc processImplicits(implicits: seq[string], nodeKind: TNodeKind, a: var TPassContextArray) = for module in items(implicits): @@ -159,45 +159,45 @@ proc processImplicits(implicits: seq[string], nodeKind: TNodeKind, str.info = gCmdLineInfo importStmt.addSon str if not processTopLevelStmt(importStmt, a): break - + proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) = - var + var p: TParsers a: TPassContextArray s: PLLStream fileIdx = module.fileIdx - if rd == nil: + if rd == nil: openPasses(a, module) - if stream == nil: + if stream == nil: let filename = fileIdx.toFullPathConsiderDirty if module.name.s == "-": module.name.s = "stdinfile" s = llStreamOpen(stdin) else: s = llStreamOpen(filename, fmRead) - if s == nil: + if s == nil: rawMessage(errCannotOpenFile, filename) return else: s = stream - while true: + while true: openParsers(p, fileIdx, s) if sfSystemModule notin module.flags: - # XXX what about caching? no processing then? what if I change the + # XXX what about caching? no processing then? what if I change the # modules to include between compilation runs? we'd need to track that # in ROD files. I think we should enable this feature only # for the interactive mode. processImplicits implicitImports, nkImportStmt, a processImplicits implicitIncludes, nkIncludeStmt, a - while true: + while true: var n = parseTopLevelStmt(p) - if n.kind == nkEmpty: break + if n.kind == nkEmpty: break if not processTopLevelStmt(n, a): break closeParsers(p) - if s.kind != llsStdIn: break + if s.kind != llsStdIn: break closePasses(a) # id synchronization point for more consistent code generation: idSynchronizationPoint(1000) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 204bfbf94..ccf3837ed 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -92,7 +92,7 @@ proc addTok(g: var TSrcGen, kind: TTokType, s: string) = proc addPendingNL(g: var TSrcGen) = if g.pendingNL >= 0: - addTok(g, tkSpaces, "\n" & repeatChar(g.pendingNL)) + addTok(g, tkSpaces, "\n" & spaces(g.pendingNL)) g.lineLen = g.pendingNL g.pendingNL = - 1 @@ -190,7 +190,7 @@ proc putComment(g: var TSrcGen, s: string) = if not isCode and (g.lineLen + (j - i) > MaxLineLen): put(g, tkComment, com) optNL(g, ind) - com = '#' & repeatChar(comIndent) + com = '#' & spaces(comIndent) while s[i] > ' ': add(com, s[i]) inc(i) @@ -280,7 +280,7 @@ proc gcom(g: var TSrcGen, n: PNode) = (g.lineLen < LineCommentColumn): var ml = maxLineLength(n.comment) if ml + LineCommentColumn <= MaxLineLen: - put(g, tkSpaces, repeatChar(LineCommentColumn - g.lineLen)) + put(g, tkSpaces, spaces(LineCommentColumn - g.lineLen)) putComment(g, n.comment) #assert(g.comStack[high(g.comStack)] = n); proc gcoms(g: var TSrcGen) = @@ -395,6 +395,7 @@ proc lsub(n: PNode): int = of nkClosedSymChoice, nkOpenSymChoice: result = lsons(n) + len("()") + sonsLen(n) - 1 of nkTupleTy: result = lcomma(n) + len("tuple[]") + of nkTupleClassTy: result = len("tuple") of nkDotExpr: result = lsons(n) + 1 of nkBind: result = lsons(n) + len("bind_") of nkBindStmt: result = lcomma(n) + len("bind_") @@ -1292,10 +1293,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gsub(g, n.sons[0]) of nkTupleTy: put(g, tkTuple, "tuple") - if sonsLen(n) > 0: - put(g, tkBracketLe, "[") - gcomma(g, n) - put(g, tkBracketRi, "]") + put(g, tkBracketLe, "[") + gcomma(g, n) + put(g, tkBracketRi, "]") + of nkTupleClassTy: + put(g, tkTuple, "tuple") of nkMetaNode_Obsolete: put(g, tkParLe, "(META|") gsub(g, n.sons[0]) diff --git a/compiler/sem.nim b/compiler/sem.nim index a90948245..36c0342cd 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -65,8 +65,8 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode; echo "passing to safeSemExpr: ", renderTree(n) discard safeSemExpr(c, n) -proc typeMismatch(n: PNode, formal, actual: PType) = - if formal.kind != tyError and actual.kind != tyError: +proc typeMismatch(n: PNode, formal, actual: PType) = + if formal.kind != tyError and actual.kind != tyError: localError(n.info, errGenerated, msgKindToString(errTypeMismatch) & typeToString(actual) & ") " & `%`(msgKindToString(errButExpectedX), [typeToString(formal)])) @@ -113,7 +113,7 @@ proc commonType*(x, y: PType): PType = else: result = newType(tyTypeDesc, a.owner) rawAddSon(result, newType(tyNone, a.owner)) - elif b.kind in {tyArray, tyArrayConstr, tySet, tySequence} and + elif b.kind in {tyArray, tyArrayConstr, tySet, tySequence} and a.kind == b.kind: # check for seq[empty] vs. seq[int] let idx = ord(b.kind in {tyArray, tyArrayConstr}) @@ -163,7 +163,7 @@ proc commonType*(x, y: PType): PType = result = newType(k, r.owner) result.addSonSkipIntLit(r) -proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym = +proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym = result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info) proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = @@ -182,7 +182,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, allowed: TSymFlags): PSym # identifier with visability -proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, +proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, allowed: TSymFlags): PSym proc semStmtScope(c: PContext, n: PNode): PNode @@ -190,7 +190,7 @@ proc typeAllowedCheck(info: TLineInfo; typ: PType; kind: TSymKind) = let t = typeAllowed(typ, kind) if t != nil: if t == typ: localError(info, "invalid type: '" & typeToString(typ) & "'") - else: localError(info, "invalid type: '" & typeToString(t) & + else: localError(info, "invalid type: '" & typeToString(t) & "' in this context: '" & typeToString(typ) & "'") proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} = @@ -226,7 +226,7 @@ when false: result = newSymNode(getSysSym"void") else: result.typ = makeTypeDesc(c, result.typ) - + result.handleIsOperator = proc (n: PNode): PNode = result = isOpImpl(c, n) @@ -248,7 +248,7 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode = if result == nil: result = arg # for 'tcnstseq' we support [] to become 'seq' - if eOrig.typ.skipTypes(abstractInst).kind == tySequence and + if eOrig.typ.skipTypes(abstractInst).kind == tySequence and arg.typ.skipTypes(abstractInst).kind == tyArrayConstr: arg.typ = eOrig.typ @@ -320,7 +320,7 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym, else: case s.typ.sons[0].kind of tyExpr: - # BUGFIX: we cannot expect a type here, because module aliases would not + # BUGFIX: we cannot expect a type here, because module aliases would not # work then (see the ``tmodulealias`` test) # semExprWithType(c, result) result = semExpr(c, result, flags) @@ -355,18 +355,18 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, result = semAfterMacroCall(c, result, sym, flags) popInfoContext() -proc forceBool(c: PContext, n: PNode): PNode = +proc forceBool(c: PContext, n: PNode): PNode = result = fitNode(c, getSysType(tyBool), n) if result == nil: result = n -proc semConstBoolExpr(c: PContext, n: PNode): PNode = +proc semConstBoolExpr(c: PContext, n: PNode): PNode = let nn = semExprWithType(c, n) result = fitNode(c, getSysType(tyBool), nn) if result == nil: localError(n.info, errConstExprExpected) return nn result = getConstExpr(c.module, result) - if result == nil: + if result == nil: localError(n.info, errConstExprExpected) result = nn @@ -403,9 +403,9 @@ proc myOpen(module: PSym): PPassContext = pushOwner(c.module) c.importTable = openScope(c) c.importTable.addSym(module) # a module knows itself - if sfSystemModule in module.flags: + if sfSystemModule in module.flags: magicsys.systemModule = module # set global variable! - else: + else: c.importTable.addSym magicsys.systemModule # import the "System" identifier importAllSymbols(c, magicsys.systemModule) c.topLevelScope = openScope(c) @@ -415,13 +415,13 @@ proc myOpenCached(module: PSym, rd: PRodReader): PPassContext = result = myOpen(module) for m in items(rd.methods): methodDef(m, true) -proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode = +proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode = result = semStmt(c, n) # BUGFIX: process newly generated generics here, not at the end! if c.lastGenericIdx < c.generics.len: var a = newNodeI(nkStmtList, n.info) addCodeForGenerics(c, a) - if sonsLen(a) > 0: + if sonsLen(a) > 0: # a generic has been added to `a`: if result.kind != nkEmpty: addSon(a, result) result = a @@ -429,17 +429,17 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode = if gCmd == cmdInteractive and not isEmptyType(result.typ): result = buildEchoStmt(c, result) result = transformStmt(c.module, result) - -proc recoverContext(c: PContext) = + +proc recoverContext(c: PContext) = # clean up in case of a semantic error: We clean up the stacks, etc. This is - # faster than wrapping every stack operation in a 'try finally' block and + # faster than wrapping every stack operation in a 'try finally' block and # requires far less code. c.currentScope = c.topLevelScope while getCurrOwner().kind != skModule: popOwner() while c.p != nil and c.p.owner.kind != skModule: c.p = c.p.next -proc myProcess(context: PPassContext, n: PNode): PNode = - var c = PContext(context) +proc myProcess(context: PPassContext, n: PNode): PNode = + var c = PContext(context) # no need for an expensive 'try' if we stop after the first error anyway: if msgs.gErrorMax <= 1: result = semStmtAndGenerateGenerics(c, n) @@ -455,8 +455,8 @@ proc myProcess(context: PPassContext, n: PNode): PNode = if getCurrentException() of ESuggestDone: result = nil else: result = ast.emptyNode #if gCmd == cmdIdeTools: findSuggest(c, n) - -proc myClose(context: PPassContext, n: PNode): PNode = + +proc myClose(context: PPassContext, n: PNode): PNode = var c = PContext(context) closeScope(c) # close module's scope rawCloseScope(c) # imported symbols; don't check for unused ones! diff --git a/compiler/semcall.nim b/compiler/semcall.nim index d92e1ab20..647e75b3a 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -7,16 +7,16 @@ # distribution, for details about the copyright. # -## This module implements semantic checking for calls. +## This module implements semantic checking for calls. # included from sem.nim proc sameMethodDispatcher(a, b: PSym): bool = result = false - if a.kind == skMethod and b.kind == skMethod: + if a.kind == skMethod and b.kind == skMethod: var aa = lastSon(a.ast) var bb = lastSon(b.ast) if aa.kind == nkSym and bb.kind == nkSym: - if aa.sym == bb.sym: + if aa.sym == bb.sym: result = true else: discard @@ -31,7 +31,7 @@ proc sameMethodDispatcher(a, b: PSym): bool = # to avoid subtle problems, the call remains ambiguous and needs to # be disambiguated by the programmer; this way the right generic is # instantiated. - + proc determineType(c: PContext, s: PSym) proc pickBestCandidate(c: PContext, headSymbol: PNode, @@ -41,73 +41,80 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, best, alt: var TCandidate, errors: var CandidateErrors) = var o: TOverloadIter - var sym = initOverloadIter(o, c, headSymbol) + # thanks to the lazy semchecking for operands, we need to iterate over the + # symbol table *before* any call to 'initCandidate' which might invoke + # semExpr which might modify the symbol table in cases like + # 'init(a, 1, (var b = new(Type2); b))'. + var symx = initOverloadIter(o, c, headSymbol) let symScope = o.lastOverloadScope + var syms: seq[tuple[a: PSym, b: int]] = @[] + while symx != nil: + if symx.kind in filter: syms.add((symx, o.lastOverloadScope)) + symx = nextOverloadIter(o, c, headSymbol) + if syms.len == 0: return + var z: TCandidate - - if sym == nil: return - initCandidate(c, best, sym, initialBinding, symScope) - initCandidate(c, alt, sym, initialBinding, symScope) + initCandidate(c, best, syms[0][0], initialBinding, symScope) + initCandidate(c, alt, syms[0][0], initialBinding, symScope) best.state = csNoMatch - - while sym != nil: - if sym.kind in filter: - determineType(c, sym) - initCandidate(c, z, sym, initialBinding, o.lastOverloadScope) - z.calleeSym = sym + for i in 0 .. <syms.len: + let sym = syms[i][0] + determineType(c, sym) + initCandidate(c, z, sym, initialBinding, syms[i][1]) + z.calleeSym = sym + + #if sym.name.s == "*" and (n.info ?? "temp5.nim") and n.info.line == 140: + # gDebug = true + matches(c, n, orig, z) + if errors != nil: + errors.safeAdd(sym) + if z.errors != nil: + for err in z.errors: + errors.add(err) + if z.state == csMatch: + # little hack so that iterators are preferred over everything else: + if sym.kind in skIterators: inc(z.exactMatches, 200) + case best.state + of csEmpty, csNoMatch: best = z + of csMatch: + var cmp = cmpCandidates(best, z) + if cmp < 0: best = z # x is better than the best so far + elif cmp == 0: alt = z # x is as good as the best so far + else: discard #if sym.name.s == "*" and (n.info ?? "temp5.nim") and n.info.line == 140: - # gDebug = true - matches(c, n, orig, z) - if errors != nil: - errors.safeAdd(sym) - if z.errors != nil: - for err in z.errors: - errors.add(err) - if z.state == csMatch: - # little hack so that iterators are preferred over everything else: - if sym.kind in skIterators: inc(z.exactMatches, 200) - case best.state - of csEmpty, csNoMatch: best = z - of csMatch: - var cmp = cmpCandidates(best, z) - if cmp < 0: best = z # x is better than the best so far - elif cmp == 0: alt = z # x is as good as the best so far - else: discard - #if sym.name.s == "*" and (n.info ?? "temp5.nim") and n.info.line == 140: - # echo "Matches ", n.info, " ", typeToString(sym.typ) - # debug sym - # writeMatches(z) - # for i in 1 .. <len(z.call): - # z.call[i].typ.debug - # quit 1 - sym = nextOverloadIter(o, c, headSymbol) + # echo "Matches ", n.info, " ", typeToString(sym.typ) + # debug sym + # writeMatches(z) + # for i in 1 .. <len(z.call): + # z.call[i].typ.debug + # quit 1 proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = # Gives a detailed error message; this is separated from semOverloadedCall, # as semOverlodedCall is already pretty slow (and we need this information # only in case of an error). - if c.inCompilesContext > 0: + if c.inCompilesContext > 0: # fail fast: globalError(n.info, errTypeMismatch, "") if errors.isNil or errors.len == 0: localError(n.info, errExprXCannotBeCalled, n[0].renderTree) return - # to avoid confusing errors like: + # to avoid confusing errors like: # got (SslPtr, SocketHandle) - # but expected one of: + # but expected one of: # openssl.SSL_set_fd(ssl: SslPtr, fd: SocketHandle): cint # we do a pre-analysis. If all types produce the same string, we will add # module information. let proto = describeArgs(c, n, 1, preferName) - + var prefer = preferName for err in errors: var errProto = "" let n = err.typ.n - for i in countup(1, n.len - 1): + for i in countup(1, n.len - 1): var p = n.sons[i] if p.kind == nkSym: add(errProto, typeToString(p.sym.typ, preferName)) @@ -159,9 +166,9 @@ proc resolveOverloads(c: PContext, n, orig: PNode, n.sons.insert(hiddenArg, 1) orig.sons.insert(hiddenArg, 1) - + pickBest(f) - + if result.state != csMatch: n.sons.delete(1) orig.sons.delete(1) @@ -179,7 +186,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, # we are going to try multiple variants n.sons[0..1] = [nil, n[1], calleeName] orig.sons[0..1] = [nil, orig[1], calleeName] - + template tryOp(x) = let op = newIdentNode(getIdent(x), n.info) n.sons[0] = op @@ -188,7 +195,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, if nfExplicitCall in n.flags: tryOp ".()" - + if result.state in {csEmpty, csNoMatch}: tryOp "." @@ -199,7 +206,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, n.sons[0..1] = [callOp, n[1], calleeName] orig.sons[0..1] = [callOp, orig[1], calleeName] pickBest(callOp) - + if overloadsState == csEmpty and result.state == csEmpty: localError(n.info, errUndeclaredIdentifier, considerQuotedIdent(f).s) return @@ -224,7 +231,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, internalAssert result.state == csMatch #writeMatches(result) #writeMatches(alt) - if c.inCompilesContext > 0: + if c.inCompilesContext > 0: # quick error message for performance of 'compiles' built-in: globalError(n.info, errGenerated, "ambiguous call") elif gErrorCounter == 0: @@ -254,7 +261,7 @@ proc instGenericConvertersSons*(c: PContext, n: PNode, x: TCandidate) = for i in 1 .. <n.len: instGenericConvertersArg(c, n.sons[i], x) -proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode = +proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode = var m: TCandidate initCandidate(c, m, f) result = paramTypesMatch(m, f, a, arg, nil) @@ -325,7 +332,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, # get rid of the deref again for a better error message: n.sons[1] = n.sons[1].sons[0] notFoundError(c, n, errors) - else: + else: notFoundError(c, n, errors) # else: result = errorNode(c, n) @@ -341,7 +348,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = styleCheckUse(n.info, s) result = newSymNode(newInst, n.info) -proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = +proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = assert n.kind == nkBracketExpr for i in 1..sonsLen(n)-1: n.sons[i].typ = semTypeNode(c, n.sons[i], nil) @@ -361,11 +368,11 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = # XXX I think this could be improved by reusing sigmatch.paramTypesMatch. # It's good enough for now. result = newNodeI(a.kind, n.info) - for i in countup(0, len(a)-1): + for i in countup(0, len(a)-1): var candidate = a.sons[i].sym if candidate.kind in {skProc, skMethod, skConverter, skIterator, skClosureIterator}: - # it suffices that the candidate has the proper number of generic + # it suffices that the candidate has the proper number of generic # type parameters: if safeLen(candidate.ast.sons[genericParamsPos]) == n.len-1: result.add(explicitGenericSym(c, n, candidate)) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 89469ae50..3d2ba2568 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -24,7 +24,7 @@ proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # same as 'semExprWithType' but doesn't check for proc vars result = semExpr(c, n, flags + {efOperand}) - if result.kind == nkEmpty: + if result.kind == nkEmpty: # do not produce another redundant error message: #raiseRecoverableError("") result = errorNode(c, n) @@ -34,19 +34,19 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = elif efWantStmt in flags: result.typ = newTypeS(tyEmpty, c) else: - localError(n.info, errExprXHasNoType, + localError(n.info, errExprXHasNoType, renderTree(result, {renderNoComments})) result.typ = errorType(c) proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = semExpr(c, n, flags+{efWantValue}) - if result.isNil or result.kind == nkEmpty: + if result.isNil or result.kind == nkEmpty: # do not produce another redundant error message: #raiseRecoverableError("") result = errorNode(c, n) if result.typ == nil or result.typ == enforceVoidContext: # we cannot check for 'void' in macros ... - localError(n.info, errExprXHasNoType, + localError(n.info, errExprXHasNoType, renderTree(result, {renderNoComments})) result.typ = errorType(c) else: @@ -61,7 +61,7 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # do not produce another redundant error message: result = errorNode(c, n) if result.typ == nil: - localError(n.info, errExprXHasNoType, + localError(n.info, errExprXHasNoType, renderTree(result, {renderNoComments})) result.typ = errorType(c) else: @@ -70,19 +70,19 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = result = symChoice(c, n, s, scClosed) - + proc inlineConst(n: PNode, s: PSym): PNode {.inline.} = result = copyTree(s.ast) result.typ = s.typ result.info = n.info - -proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = + +proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = case s.kind of skConst: markUsed(n.info, s) styleCheckUse(n.info, s) case skipTypes(s.typ, abstractInst-{tyTypeDesc}).kind - of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, + of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, tyTuple, tySet, tyUInt..tyUInt64: if s.magic == mNone: result = inlineConst(n, s) else: result = newSymNode(s, n.info) @@ -167,7 +167,7 @@ proc checkConversionBetweenObjects(castDest, src: PType): TConvStatus = else: convOK -const +const IntegralTypes = {tyBool, tyEnum, tyChar, tyInt..tyUInt64} proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus = @@ -201,10 +201,10 @@ proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus = proc isCastable(dst, src: PType): bool = ## Checks whether the source type can be casted to the destination type. - ## Casting is very unrestrictive; casts are allowed as long as + ## Casting is very unrestrictive; casts are allowed as long as ## castDest.size >= src.size, and typeAllowed(dst, skParam) #const - # castableTypeKinds = {tyInt, tyPtr, tyRef, tyCstring, tyString, + # castableTypeKinds = {tyInt, tyPtr, tyRef, tyCstring, tyString, # tySequence, tyPointer, tyNil, tyOpenArray, # tyProc, tySet, tyEnum, tyBool, tyChar} if skipTypes(dst, abstractInst-{tyOpenArray}).kind == tyOpenArray: @@ -213,7 +213,7 @@ proc isCastable(dst, src: PType): bool = dstSize = computeSize(dst) srcSize = computeSize(src) - if dstSize < 0: + if dstSize < 0: result = false elif srcSize < 0: result = false @@ -225,7 +225,7 @@ proc isCastable(dst, src: PType): bool = (skipTypes(src, abstractInst-{tyTypeDesc}).kind in IntegralTypes) if result and src.kind == tyNil: result = dst.size <= platform.ptrSize - + proc isSymChoice(n: PNode): bool {.inline.} = result = n.kind in nkSymChoices @@ -249,7 +249,7 @@ proc semConv(c: PContext, n: PNode): PNode = maybeLiftType(targetType, c, n[0].info) result.addSon copyTree(n.sons[0]) var op = semExprWithType(c, n.sons[1]) - + if targetType.isMetaType: let final = inferWithMetatype(c, targetType, op, true) result.addSon final @@ -258,7 +258,7 @@ proc semConv(c: PContext, n: PNode): PNode = result.typ = targetType addSon(result, op) - + if not isSymChoice(op): let status = checkConvertible(c, result.typ, op.typ) case status @@ -286,7 +286,7 @@ proc semConv(c: PContext, n: PNode): PNode = return it localError(n.info, errUseQualifier, op.sons[0].sym.name.s) -proc semCast(c: PContext, n: PNode): PNode = +proc semCast(c: PContext, n: PNode): PNode = ## Semantically analyze a casting ("cast[type](param)") if optSafeCode in gGlobalOptions: localError(n.info, errCastNotInSafeMode) #incl(c.p.owner.flags, sfSideEffect) @@ -295,25 +295,25 @@ proc semCast(c: PContext, n: PNode): PNode = result.typ = semTypeNode(c, n.sons[0], nil) addSon(result, copyTree(n.sons[0])) addSon(result, semExprWithType(c, n.sons[1])) - if not isCastable(result.typ, result.sons[1].typ): - localError(result.info, errExprCannotBeCastedToX, + if not isCastable(result.typ, result.sons[1].typ): + localError(result.info, errExprCannotBeCastedToX, typeToString(result.typ)) -proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = - const +proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = + const opToStr: array[mLow..mHigh, string] = ["low", "high"] - if sonsLen(n) != 2: + if sonsLen(n) != 2: localError(n.info, errXExpectsTypeOrValue, opToStr[m]) - else: + else: n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType}) var typ = skipTypes(n.sons[1].typ, abstractVarRange + {tyTypeDesc, tyFieldAccessor}) case typ.kind - of tySequence, tyString, tyCString, tyOpenArray, tyVarargs: + of tySequence, tyString, tyCString, tyOpenArray, tyVarargs: n.typ = getSysType(tyInt) - of tyArrayConstr, tyArray: + of tyArrayConstr, tyArray: n.typ = typ.sons[0] # indextype - of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt8, tyUInt16, tyUInt32: + of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt8, tyUInt16, tyUInt32: # do not skip the range! n.typ = n.sons[1].typ.skipTypes(abstractVar + {tyFieldAccessor}) of tyGenericParam: @@ -334,8 +334,8 @@ proc semSizeof(c: PContext, n: PNode): PNode = n.typ = getSysType(tyInt) result = n -proc semOf(c: PContext, n: PNode): PNode = - if sonsLen(n) == 3: +proc semOf(c: PContext, n: PNode): PNode = + if sonsLen(n) == 3: n.sons[1] = semExprWithType(c, n.sons[1]) n.sons[2] = semExprWithType(c, n.sons[2], {efDetermineType}) #restoreOldStyleType(n.sons[1]) @@ -373,7 +373,7 @@ proc isOpImpl(c: PContext, n: PNode): PNode = internalAssert n.sonsLen == 3 and n[1].typ != nil and n[1].typ.kind == tyTypeDesc and n[2].kind in {nkStrLit..nkTripleStrLit, nkType} - + let t1 = n[1].typ.skipTypes({tyTypeDesc, tyFieldAccessor}) if n[2].kind in {nkStrLit..nkTripleStrLit}: @@ -381,7 +381,7 @@ proc isOpImpl(c: PContext, n: 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)) else: discard else: @@ -400,7 +400,7 @@ proc semIs(c: PContext, n: PNode): PNode = result = n n.typ = getSysType(tyBool) - + n.sons[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator}) if n[2].kind notin {nkStrLit..nkTripleStrLit}: let t2 = semTypeNode(c, n[2], nil) @@ -420,7 +420,7 @@ proc semOpAux(c: PContext, n: PNode) = const flags = {efDetermineType} for i in countup(1, n.sonsLen-1): var a = n.sons[i] - if a.kind == nkExprEqExpr and sonsLen(a) == 2: + if a.kind == nkExprEqExpr and sonsLen(a) == 2: var info = a.sons[0].info a.sons[0] = newIdentNode(considerQuotedIdent(a.sons[0]), info) a.sons[1] = semExprWithType(c, a.sons[1], flags) @@ -428,7 +428,7 @@ proc semOpAux(c: PContext, n: PNode) = else: n.sons[i] = semExprWithType(c, a, flags) -proc overloadedCallOpr(c: PContext, n: PNode): PNode = +proc overloadedCallOpr(c: PContext, n: PNode): PNode = # quick check if there is *any* () operator overloaded: var par = getIdent("()") if searchInScopes(c, par) == nil: @@ -457,7 +457,7 @@ proc changeType(n: PNode, newType: PType, check: bool) = return if tup.n != nil: var f = getSymFromList(newType.n, m.sym.name) - if f == nil: + if f == nil: internalError(m.info, "changeType(): invalid identifier") return changeType(n.sons[i].sons[1], f.typ, check) @@ -481,10 +481,10 @@ proc changeType(n: PNode, newType: PType, check: bool) = else: discard n.typ = newType -proc arrayConstrType(c: PContext, n: PNode): PType = +proc arrayConstrType(c: PContext, n: PNode): PType = var typ = newTypeS(tyArrayConstr, c) rawAddSon(typ, nil) # index type - if sonsLen(n) == 0: + if sonsLen(n) == 0: rawAddSon(typ, newTypeS(tyEmpty, c)) # needs an empty basetype! else: var x = n.sons[0] @@ -494,35 +494,35 @@ proc arrayConstrType(c: PContext, n: PNode): PType = typ.sons[0] = makeRangeType(c, 0, sonsLen(n) - 1, n.info) result = typ -proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = +proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = result = newNodeI(nkBracket, n.info) result.typ = newTypeS(tyArrayConstr, c) rawAddSon(result.typ, nil) # index type - if sonsLen(n) == 0: + if sonsLen(n) == 0: rawAddSon(result.typ, newTypeS(tyEmpty, c)) # needs an empty basetype! else: var x = n.sons[0] var lastIndex: BiggestInt = 0 var indexType = getSysType(tyInt) - if x.kind == nkExprColonExpr and sonsLen(x) == 2: + if x.kind == nkExprColonExpr and sonsLen(x) == 2: var idx = semConstExpr(c, x.sons[0]) lastIndex = getOrdValue(idx) indexType = idx.typ x = x.sons[1] - + let yy = semExprWithType(c, x) var typ = yy.typ addSon(result, yy) #var typ = skipTypes(result.sons[0].typ, {tyGenericInst, tyVar, tyOrdinal}) - for i in countup(1, sonsLen(n) - 1): + for i in countup(1, sonsLen(n) - 1): x = n.sons[i] - if x.kind == nkExprColonExpr and sonsLen(x) == 2: + if x.kind == nkExprColonExpr and sonsLen(x) == 2: var idx = semConstExpr(c, x.sons[0]) idx = fitNode(c, indexType, idx) if lastIndex+1 != getOrdValue(idx): localError(x.info, errInvalidOrderInArrayConstructor) x = x.sons[1] - + let xx = semExprWithType(c, x, flags*{efAllowDestructor}) result.add xx typ = commonType(typ, xx.typ) @@ -534,21 +534,21 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = result.sons[i] = fitNode(c, typ, result.sons[i]) result.typ.sons[0] = makeRangeType(c, 0, sonsLen(result) - 1, n.info) -proc fixAbstractType(c: PContext, n: PNode) = +proc fixAbstractType(c: PContext, n: PNode) = # XXX finally rewrite that crap! - for i in countup(1, sonsLen(n) - 1): + for i in countup(1, sonsLen(n) - 1): var it = n.sons[i] case it.kind of nkHiddenStdConv, nkHiddenSubConv: if it.sons[1].kind == nkBracket: it.sons[1].typ = arrayConstrType(c, it.sons[1]) #it.sons[1] = semArrayConstr(c, it.sons[1]) - if skipTypes(it.typ, abstractVar).kind in {tyOpenArray, tyVarargs}: + if skipTypes(it.typ, abstractVar).kind in {tyOpenArray, tyVarargs}: #if n.sons[0].kind == nkSym and IdentEq(n.sons[0].sym.name, "[]="): # debug(n) - + var s = skipTypes(it.sons[1].typ, abstractVar) - if s.kind == tyArrayConstr and s.sons[1].kind == tyEmpty: + if s.kind == tyArrayConstr and s.sons[1].kind == tyEmpty: s = copyType(s, getCurrOwner(), false) skipTypes(s, abstractVar).sons[1] = elemType( skipTypes(it.typ, abstractVar)) @@ -558,32 +558,32 @@ proc fixAbstractType(c: PContext, n: PNode) = skipTypes(s, abstractVar).sons[0] = elemType( skipTypes(it.typ, abstractVar)) it.sons[1].typ = s - + elif skipTypes(it.sons[1].typ, abstractVar).kind in - {tyNil, tyArrayConstr, tyTuple, tySet}: + {tyNil, tyArrayConstr, tyTuple, tySet}: var s = skipTypes(it.typ, abstractVar) if s.kind != tyExpr: changeType(it.sons[1], s, check=true) n.sons[i] = it.sons[1] - of nkBracket: + of nkBracket: # an implicitly constructed array (passed to an open array): n.sons[i] = semArrayConstr(c, it, {}) - else: + else: discard - #if (it.typ == nil): - # InternalError(it.info, "fixAbstractType: " & renderTree(it)) - -proc skipObjConv(n: PNode): PNode = + #if (it.typ == nil): + # InternalError(it.info, "fixAbstractType: " & renderTree(it)) + +proc skipObjConv(n: PNode): PNode = case n.kind - of nkHiddenStdConv, nkHiddenSubConv, nkConv: - if skipTypes(n.sons[1].typ, abstractPtrs).kind in {tyTuple, tyObject}: + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + if skipTypes(n.sons[1].typ, abstractPtrs).kind in {tyTuple, tyObject}: result = n.sons[1] - else: + else: result = n of nkObjUpConv, nkObjDownConv: result = n.sons[0] else: result = n -proc isAssignable(c: PContext, n: PNode): TAssignableResult = +proc isAssignable(c: PContext, n: PNode): TAssignableResult = result = parampatterns.isAssignable(c.p.owner, n) proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = @@ -597,49 +597,49 @@ proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = if isAssignable(c, n) notin {arLValue, arLocalLValue}: localError(n.info, errVarForOutParamNeeded) -proc analyseIfAddressTaken(c: PContext, n: PNode): PNode = +proc analyseIfAddressTaken(c: PContext, n: PNode): PNode = result = n case n.kind of nkSym: # n.sym.typ can be nil in 'check' mode ... if n.sym.typ != nil and - skipTypes(n.sym.typ, abstractInst-{tyTypeDesc}).kind != tyVar: + skipTypes(n.sym.typ, abstractInst-{tyTypeDesc}).kind != tyVar: incl(n.sym.flags, sfAddrTaken) result = newHiddenAddrTaken(c, n) - of nkDotExpr: + of nkDotExpr: checkSonsLen(n, 2) - if n.sons[1].kind != nkSym: + if n.sons[1].kind != nkSym: internalError(n.info, "analyseIfAddressTaken") return - if skipTypes(n.sons[1].sym.typ, abstractInst-{tyTypeDesc}).kind != tyVar: + if skipTypes(n.sons[1].sym.typ, abstractInst-{tyTypeDesc}).kind != tyVar: incl(n.sons[1].sym.flags, sfAddrTaken) result = newHiddenAddrTaken(c, n) - of nkBracketExpr: + of nkBracketExpr: checkMinSonsLen(n, 1) - if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind != tyVar: + if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind != tyVar: if n.sons[0].kind == nkSym: incl(n.sons[0].sym.flags, sfAddrTaken) result = newHiddenAddrTaken(c, n) - else: + else: result = newHiddenAddrTaken(c, n) - -proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = + +proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = checkMinSonsLen(n, 1) - const - FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl, - mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap, + const + FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl, + mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap, mAppendSeqElem, mNewSeq, mReset, mShallowCopy, mDeepCopy} - + # get the real type of the callee # it may be a proc var with a generic alias type, so we skip over them var t = n.sons[0].typ.skipTypes({tyGenericInst}) - if n.sons[0].kind == nkSym and n.sons[0].sym.magic in FakeVarParams: + if n.sons[0].kind == nkSym and n.sons[0].sym.magic in FakeVarParams: # BUGFIX: check for L-Value still needs to be done for the arguments! # note sometimes this is eval'ed twice so we check for nkHiddenAddr here: - for i in countup(1, sonsLen(n) - 1): + for i in countup(1, sonsLen(n) - 1): if i < sonsLen(t) and t.sons[i] != nil and - skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar: - if isAssignable(c, n.sons[i]) notin {arLValue, arLocalLValue}: + skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar: + if isAssignable(c, n.sons[i]) notin {arLValue, arLocalLValue}: if n.sons[i].kind != nkHiddenAddr: localError(n.sons[i].info, errVarForOutParamNeeded) return @@ -653,14 +653,14 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar: if n.sons[i].kind != nkHiddenAddr: n.sons[i] = analyseIfAddressTaken(c, n.sons[i]) - + include semmagic proc evalAtCompileTime(c: PContext, n: PNode): PNode = result = n if n.kind notin nkCallKinds or n.sons[0].kind != nkSym: return var callee = n.sons[0].sym - + # constant folding that is necessary for correctness of semantic pass: if callee.magic != mNone and callee.magic in ctfeWhitelist and n.typ != nil: var call = newNodeIT(nkCall, n.info, n.typ) @@ -678,7 +678,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = if result.isNil: result = n else: return result result.typ = semfold.getIntervalType(callee.magic, call) - + block maybeLabelAsStatic: # XXX: temporary work-around needed for tlateboundstatic. # This is certainly not correct, but it will get the job @@ -696,15 +696,15 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = # optimization pass: not necessary for correctness of the semantic pass if {sfNoSideEffect, sfCompileTime} * callee.flags != {} and {sfForward, sfImportc} * callee.flags == {} and n.typ != nil: - if sfCompileTime notin callee.flags and + if sfCompileTime notin callee.flags and optImplicitStatic notin gOptions: return if callee.magic notin ctfeWhitelist: return if callee.kind notin {skProc, skConverter} or callee.isGenericRoutine: return - + if n.typ != nil and typeAllowed(n.typ, skConst) != nil: return - + var call = newNodeIT(nkCall, n.info, n.typ) call.add(n.sons[0]) for i in 1 .. < n.len: @@ -714,7 +714,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = #echo "NOW evaluating at compile time: ", call.renderTree if sfCompileTime in callee.flags: result = evalStaticExpr(c.module, call, c.p.owner) - if result.isNil: + if result.isNil: localError(n.info, errCannotInterpretNodeX, renderTree(call)) else: result = fixupTypeAfterEval(c, result, n) else: @@ -737,17 +737,17 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, flags: TExprFlags): PNode = if flags*{efInTypeof, efWantIterator} != {}: # consider: 'for x in pReturningArray()' --> we don't want the restriction - # to 'skIterators' anymore; skIterators are preferred in sigmatch already + # to 'skIterators' anymore; skIterators are preferred in sigmatch already # for typeof support. # for ``type(countup(1,3))``, see ``tests/ttoseq``. result = semOverloadedCall(c, n, nOrig, {skProc, skMethod, skConverter, skMacro, skTemplate}+skIterators) else: - result = semOverloadedCall(c, n, nOrig, + result = semOverloadedCall(c, n, nOrig, {skProc, skMethod, skConverter, skMacro, skTemplate}) - + if result != nil: - if result.sons[0].kind != nkSym: + if result.sons[0].kind != nkSym: internalError("semOverloadedCallAnalyseEffects") return let callee = result.sons[0].sym @@ -759,7 +759,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, # error correction, prevents endless for loop elimination in transf. # See bug #2051: result.sons[0] = newSymNode(errorSym(c, n)) - if sfNoSideEffect notin callee.flags: + if sfNoSideEffect notin callee.flags: if {sfImportc, sfSideEffect} * callee.flags != {}: incl(c.p.owner.flags, sfSideEffect) @@ -808,11 +808,11 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = else: var hasErrorType = false var msg = msgKindToString(errTypeMismatch) - for i in countup(1, sonsLen(n) - 1): + for i in countup(1, sonsLen(n) - 1): if i > 1: add(msg, ", ") let nt = n.sons[i].typ add(msg, typeToString(nt)) - if nt.kind == tyError: + if nt.kind == tyError: hasErrorType = true break if not hasErrorType: @@ -824,7 +824,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = else: result = m.call instGenericConvertersSons(c, result, m) - # we assume that a procedure that calls something indirectly + # we assume that a procedure that calls something indirectly # has side-effects: if tfNoSideEffect notin t.flags: incl(c.p.owner.flags, sfSideEffect) elif t != nil and t.kind == tyTypeDesc: @@ -834,7 +834,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = result = overloadedCallOpr(c, n) # Now that nkSym does not imply an iteration over the proc/iterator space, # the old ``prc`` (which is likely an nkIdent) has to be restored: - if result == nil: + if result == nil: # XXX: hmm, what kind of symbols will end up here? # do we really need to try the overload resolution? n.sons[0] = prc @@ -869,7 +869,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode = if c.inTypeClass == 0: result = evalAtCompileTime(c, result) -proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = +proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = # this seems to be a hotspot in the compiler! let nOrig = n.copyTree #semLazyOpAux(c, n) @@ -877,7 +877,7 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = if result != nil: result = afterCallActions(c, result, nOrig, flags) else: result = errorNode(c, n) -proc buildEchoStmt(c: PContext, n: PNode): PNode = +proc buildEchoStmt(c: PContext, n: PNode): PNode = # we MUST not check 'n' for semantics again here! But for now we give up: result = newNodeI(nkCall, n.info) var e = strTableGet(magicsys.systemModule.tab, getIdent"echo") @@ -892,8 +892,8 @@ proc buildEchoStmt(c: PContext, n: PNode): PNode = proc semExprNoType(c: PContext, n: PNode): PNode = result = semExpr(c, n, {efWantStmt}) discardCheck(c, result) - -proc isTypeExpr(n: PNode): bool = + +proc isTypeExpr(n: PNode): bool = case n.kind of nkType, nkTypeOfExpr: result = true of nkSym: result = n.sym.kind == skType @@ -904,24 +904,24 @@ proc createSetType(c: PContext; baseType: PType): PType = result = newTypeS(tySet, c) rawAddSon(result, baseType) -proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent, - check: var PNode): PSym = +proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent, + check: var PNode): PSym = # transform in a node that contains the runtime check for the # field, if it is in a case-part... result = nil case r.kind - of nkRecList: - for i in countup(0, sonsLen(r) - 1): + of nkRecList: + for i in countup(0, sonsLen(r) - 1): result = lookupInRecordAndBuildCheck(c, n, r.sons[i], field, check) - if result != nil: return - of nkRecCase: + if result != nil: return + of nkRecCase: checkMinSonsLen(r, 2) if (r.sons[0].kind != nkSym): illFormedAst(r) result = lookupInRecordAndBuildCheck(c, n, r.sons[0], field, check) - if result != nil: return + if result != nil: return let setType = createSetType(c, r.sons[0].typ) var s = newNodeIT(nkCurly, r.info, setType) - for i in countup(1, sonsLen(r) - 1): + for i in countup(1, sonsLen(r) - 1): var it = r.sons[i] case it.kind of nkOfBranch: @@ -957,14 +957,14 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent, addSon(check, notExpr) return else: illFormedAst(it) - of nkSym: + of nkSym: if r.sym.name.id == field.id: result = r.sym else: illFormedAst(n) -proc makeDeref(n: PNode): PNode = +proc makeDeref(n: PNode): PNode = var t = skipTypes(n.typ, {tyGenericInst}) result = n - if t.kind == tyVar: + if t.kind == tyVar: result = newNodeIT(nkHiddenDeref, n.info, t.sons[0]) addSon(result, n) t = skipTypes(t.sons[0], {tyGenericInst}) @@ -1079,7 +1079,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = check.sons[0] = n check.typ = n.typ result = check - elif ty.kind == tyTuple and ty.n != nil: + elif ty.kind == tyTuple and ty.n != nil: f = getSymFromList(ty.n, i) if f != nil: markUsed(n.sons[1].info, f) @@ -1106,8 +1106,8 @@ proc dotTransformation(c: PContext, n: PNode): PNode = result.flags.incl nfDotField addSon(result, newIdentNode(i, n[1].info)) addSon(result, copyTree(n[0])) - -proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = + +proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = # this is difficult, because the '.' is used in many different contexts # in Nim. We first allow types in the semantic checking. result = builtinFieldAccess(c, n, flags) @@ -1118,7 +1118,7 @@ proc buildOverloadedSubscripts(n: PNode, ident: PIdent): PNode = result = newNodeI(nkCall, n.info) result.add(newIdentNode(ident, n.info)) for i in 0 .. n.len-1: result.add(n[i]) - + proc semDeref(c: PContext, n: PNode): PNode = checkSonsLen(n, 1) n.sons[0] = semExprWithType(c, n.sons[0]) @@ -1127,12 +1127,12 @@ proc semDeref(c: PContext, n: PNode): PNode = case t.kind of tyRef, tyPtr: n.typ = t.lastSon else: result = nil - #GlobalError(n.sons[0].info, errCircumNeedsPointer) + #GlobalError(n.sons[0].info, errCircumNeedsPointer) 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: + if sonsLen(n) == 1: var x = semDeref(c, n) if x == nil: return nil result = newNodeIT(nkDerefExpr, x.info, x.typ) @@ -1142,12 +1142,12 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = n.sons[0] = semExprWithType(c, n.sons[0]) var arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyPtr, tyRef}) case arr.kind - of tyArray, tyOpenArray, tyVarargs, tyArrayConstr, tySequence, tyString, + of tyArray, tyOpenArray, tyVarargs, tyArrayConstr, tySequence, tyString, tyCString: if n.len != 2: return nil n.sons[0] = makeDeref(n.sons[0]) - for i in countup(1, sonsLen(n) - 1): - n.sons[i] = semExprWithType(c, n.sons[i], + for i in countup(1, sonsLen(n) - 1): + n.sons[i] = semExprWithType(c, n.sons[i], flags*{efInTypeof, efDetermineType}) var indexType = if arr.kind == tyArray: arr.sons[0] else: getSysType(tyInt) var arg = indexTypesMatch(c, indexType, n.sons[1].typ, n.sons[1]) @@ -1157,28 +1157,28 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = result.typ = elemType(arr) #GlobalError(n.info, errIndexTypesDoNotMatch) of tyTypeDesc: - # The result so far is a tyTypeDesc bound + # The result so far is a tyTypeDesc bound # a tyGenericBody. The line below will substitute # it with the instantiated type. result = n result.typ = makeTypeDesc(c, semTypeNode(c, n, nil)) #result = symNodeFromType(c, semTypeNode(c, n, nil), n.info) - of tyTuple: + of tyTuple: checkSonsLen(n, 2) n.sons[0] = makeDeref(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 - {tyInt..tyInt64}: + {tyInt..tyInt64}: var idx = getOrdValue(n.sons[1]) if idx >= 0 and idx < sonsLen(arr): n.typ = arr.sons[int(idx)] else: localError(n.info, errInvalidIndexValueForTuple) - else: + else: localError(n.info, errIndexTypesDoNotMatch) result = n else: discard - -proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = + +proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = result = semSubscript(c, n, flags) if result == nil: # overloaded [] operator: @@ -1195,7 +1195,7 @@ proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode = result.flags.incl nfDotSetter let orig = newNode(nkCall, n.info, sons = @[setterId, aOrig[0], nOrig[1]]) result = semOverloadedCallAnalyseEffects(c, result, orig, {}) - + if result != nil: result = afterCallActions(c, result, nOrig, {}) #fixAbstractType(c, result) @@ -1216,7 +1216,7 @@ proc takeImplicitAddr(c: PContext, n: PNode): PNode = localError(n.info, errExprHasNoAddress) result = newNodeIT(nkHiddenAddr, n.info, makePtrType(c, n.typ)) result.add(n) - + proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = if le.kind == nkHiddenDeref: var x = le.sons[0] @@ -1265,7 +1265,7 @@ proc semAsgn(c: PContext, n: PNode): PNode = # a = b # both are vars, means: a[] = b[] # a = b # b no 'var T' means: a = addr(b) var le = a.typ - if skipTypes(le, {tyGenericInst}).kind != tyVar and + if skipTypes(le, {tyGenericInst}).kind != tyVar and isAssignable(c, a) == arNone: # Direct assignment to a discriminant is allowed! localError(a.info, errXCannotBeAssignedTo, @@ -1275,7 +1275,7 @@ proc semAsgn(c: PContext, n: PNode): PNode = lhs = n.sons[0] lhsIsResult = lhs.kind == nkSym and lhs.sym.kind == skResult var - rhs = semExprWithType(c, n.sons[1], + rhs = semExprWithType(c, n.sons[1], if lhsIsResult: {efAllowDestructor} else: {}) if lhsIsResult: n.typ = enforceVoidContext @@ -1300,13 +1300,13 @@ proc semReturn(c: PContext, n: PNode): PNode = skClosureIterator}: if n.sons[0].kind != nkEmpty: # transform ``return expr`` to ``result = expr; return`` - if c.p.resultSym != nil: + if c.p.resultSym != nil: var a = newNodeI(nkAsgn, n.sons[0].info) addSon(a, newSymNode(c.p.resultSym)) addSon(a, n.sons[0]) n.sons[0] = semAsgn(c, a) # optimize away ``result = result``: - if n[0][1].kind == nkSym and n[0][1].sym == c.p.resultSym: + if n[0][1].kind == nkSym and n[0][1].sym == c.p.resultSym: n.sons[0] = ast.emptyNode else: localError(n.info, errNoReturnTypeDeclared) @@ -1315,7 +1315,7 @@ proc semReturn(c: PContext, n: PNode): PNode = proc semProcBody(c: PContext, n: PNode): PNode = openScope(c) - + result = semExpr(c, n) if c.p.resultSym != nil and not isEmptyType(result.typ): # transform ``expr`` to ``result = expr``, but not if the expr is already @@ -1339,7 +1339,7 @@ proc semProcBody(c: PContext, n: PNode): PNode = result = semAsgn(c, a) else: discardCheck(c, result) - + if c.p.owner.kind notin {skMacro, skTemplate} and c.p.resultSym != nil and c.p.resultSym.typ.isMetaType: if isEmptyType(result.typ): @@ -1362,14 +1362,14 @@ proc semYieldVarResult(c: PContext, n: PNode, restype: PType) = if e.kind == tyVar: if n.sons[0].kind == nkPar: n.sons[0].sons[i] = takeImplicitAddr(c, n.sons[0].sons[i]) - elif n.sons[0].kind in {nkHiddenStdConv, nkHiddenSubConv} and + elif n.sons[0].kind in {nkHiddenStdConv, nkHiddenSubConv} and n.sons[0].sons[1].kind == nkPar: var a = n.sons[0].sons[1] a.sons[i] = takeImplicitAddr(c, a.sons[i]) else: localError(n.sons[0].info, errXExpected, "tuple constructor") else: discard - + proc semYield(c: PContext, n: PNode): PNode = result = n checkSonsLen(n, 1) @@ -1387,14 +1387,14 @@ proc semYield(c: PContext, n: PNode): PNode = if adjustedRes.kind != tyExpr: n.sons[0] = fitNode(c, adjustedRes, n.sons[0]) if n.sons[0].typ == nil: internalError(n.info, "semYield") - + if resultTypeIsInferrable(adjustedRes): let inferred = n.sons[0].typ if restype.kind == tyIter: restype.sons[0] = inferred else: iterType.sons[0] = inferred - + semYieldVarResult(c, n, adjustedRes) else: localError(n.info, errCannotReturnExpr) @@ -1402,25 +1402,25 @@ proc semYield(c: PContext, n: PNode): PNode = localError(n.info, errGenerated, "yield statement must yield a value") proc lookUpForDefined(c: PContext, i: PIdent, onlyCurrentScope: bool): PSym = - if onlyCurrentScope: + if onlyCurrentScope: result = localSearchInScope(c, i) - else: + else: result = searchInScopes(c, i) # no need for stub loading -proc lookUpForDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PSym = +proc lookUpForDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PSym = case n.kind - of nkIdent: + of nkIdent: result = lookUpForDefined(c, n.ident, onlyCurrentScope) of nkDotExpr: result = nil - if onlyCurrentScope: return + if onlyCurrentScope: return checkSonsLen(n, 2) var m = lookUpForDefined(c, n.sons[0], onlyCurrentScope) if m != nil and m.kind == skModule: let ident = considerQuotedIdent(n[1]) if m == c.module: result = strTableGet(c.topLevelScope.symbols, ident) - else: + else: result = strTableGet(m.tab, ident) of nkAccQuoted: result = lookUpForDefined(c, considerQuotedIdent(n), onlyCurrentScope) @@ -1432,7 +1432,7 @@ proc lookUpForDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PSym = localError(n.info, errIdentifierExpected, renderTree(n)) result = nil -proc semDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PNode = +proc semDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PNode = checkSonsLen(n, 2) # we replace this node by a 'true' or 'false' node: result = newIntNode(nkIntLit, 0) @@ -1441,7 +1441,7 @@ proc semDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PNode = localError(n.info, "obsolete usage of 'defined', use 'declared' instead") elif condsyms.isDefined(n.sons[1].ident): result.intVal = 1 - elif lookUpForDefined(c, n.sons[1], onlyCurrentScope) != nil: + elif lookUpForDefined(c, n.sons[1], onlyCurrentScope) != nil: result.intVal = 1 result.info = n.info result.typ = getSysType(tyBool) @@ -1544,7 +1544,7 @@ proc processQuotations(n: var PNode, op: string, n.sons[0] = newIdentNode(getIdent(examinedOp.substr(op.len)), n.info) elif n.kind == nkAccQuoted and op == "``": returnQuote n[0] - + for i in 0 .. <n.safeLen: processQuotations(n.sons[i], op, quotes, ids) @@ -1556,7 +1556,7 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = doBlk = n{-1} op = if n.len == 3: expectString(c, n[1]) else: "``" quotes = newSeq[PNode](1) - # the quotes will be added to a nkCall statement + # the quotes will be added to a nkCall statement # leave some room for the callee symbol ids = newSeq[PNode]() # this will store the generated param names @@ -1565,7 +1565,7 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = localError(n.info, errXExpected, "block") processQuotations(doBlk.sons[bodyPos], op, quotes, ids) - + doBlk.sons[namePos] = newAnonSym(skTemplate, n.info).newSymNode if ids.len > 0: doBlk.sons[paramsPos] = newNodeI(nkFormalParams, n.info) @@ -1573,7 +1573,7 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = ids.add getSysSym("expr").newSymNode # params type ids.add emptyNode # no default value doBlk[paramsPos].add newNode(nkIdentDefs, n.info, ids) - + var tmpl = semTemplateDef(c, doBlk) quotes[0] = tmpl[namePos] result = newNode(nkCall, n.info, @[ @@ -1588,7 +1588,7 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = inc c.inCompilesContext # do not halt after first error: msgs.gErrorMax = high(int) - + # open a scope for temporary symbol inclusions: let oldScope = c.currentScope openScope(c) @@ -1597,7 +1597,7 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = let oldErrorOutputs = errorOutputs errorOutputs = {} let oldContextLen = msgs.getInfoContextLen() - + let oldInGenericContext = c.inGenericContext let oldInUnrolledContext = c.inUnrolledContext let oldInGenericInst = c.inGenericInst @@ -1625,7 +1625,7 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode = # we replace this node by a 'true' or 'false' node: if sonsLen(n) != 2: return semDirectOp(c, n, flags) - + result = newIntNode(nkIntLit, ord(tryExpr(c, n[1], flags) != nil)) result.info = n.info result.typ = getSysType(tyBool) @@ -1652,7 +1652,7 @@ proc instantiateCreateFlowVarCall(c: PContext; t: PType; let sym = magicsys.getCompilerProc("nimCreateFlowVar") if sym == nil: localError(info, errSystemNeeds, "nimCreateFlowVar") - var bindings: TIdTable + var bindings: TIdTable initIdTable(bindings) bindings.idTablePut(sym.ast[genericParamsPos].sons[0].typ, t) result = c.semGenerateInstance(c, sym, bindings, info) @@ -1662,12 +1662,12 @@ proc instantiateCreateFlowVarCall(c: PContext; t: PType; result.flags = result.flags - {sfCompilerProc, sfExportC, sfImportC} result.loc.r = nil -proc setMs(n: PNode, s: PSym): PNode = +proc setMs(n: PNode, s: PSym): PNode = result = n n.sons[0] = newSymNode(s) n.sons[0].info = n.info -proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = +proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = # this is a hotspot in the compiler! # DON'T forget to update ast.SpecialSemMagics if you add a magic here! result = n @@ -1713,36 +1713,36 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = # If semCheck is set to false, ``when`` will return the verbatim AST of # the correct branch. Otherwise the AST will be passed through semStmt. result = nil - + template setResult(e: expr) = if semCheck: result = semStmt(c, e) # do not open a new scope! else: result = e - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): var it = n.sons[i] case it.kind - of nkElifBranch, nkElifExpr: + of nkElifBranch, nkElifExpr: checkSonsLen(it, 2) var e = semConstExpr(c, it.sons[0]) - if e.kind != nkIntLit: + if e.kind != nkIntLit: # can happen for cascading errors, assume false # InternalError(n.info, "semWhen") discard elif e.intVal != 0 and result == nil: - setResult(it.sons[1]) + setResult(it.sons[1]) of nkElse, nkElseExpr: checkSonsLen(it, 1) - if result == nil: + if result == nil: setResult(it.sons[0]) else: illFormedAst(n) if result == nil: - result = newNodeI(nkEmpty, n.info) + result = newNodeI(nkEmpty, n.info) # The ``when`` statement implements the mechanism for platform dependent # code. Thus we try to ensure here consistent ID allocation after the # ``when`` statement. idSynchronizationPoint(200) -proc semSetConstr(c: PContext, n: PNode): PNode = +proc semSetConstr(c: PContext, n: PNode): PNode = result = newNodeI(nkCurly, n.info) result.typ = newTypeS(tySet, c) if sonsLen(n) == 0: @@ -1750,31 +1750,31 @@ proc semSetConstr(c: PContext, n: PNode): PNode = else: # only semantic checking for all elements, later type checking: var typ: PType = nil - for i in countup(0, sonsLen(n) - 1): - if isRange(n.sons[i]): + for i in countup(0, sonsLen(n) - 1): + if isRange(n.sons[i]): checkSonsLen(n.sons[i], 3) n.sons[i].sons[1] = semExprWithType(c, n.sons[i].sons[1]) n.sons[i].sons[2] = semExprWithType(c, n.sons[i].sons[2]) - if typ == nil: - typ = skipTypes(n.sons[i].sons[1].typ, + if typ == nil: + typ = skipTypes(n.sons[i].sons[1].typ, {tyGenericInst, tyVar, tyOrdinal}) n.sons[i].typ = n.sons[i].sons[2].typ # range node needs type too elif n.sons[i].kind == nkRange: # already semchecked if typ == nil: - typ = skipTypes(n.sons[i].sons[0].typ, + typ = skipTypes(n.sons[i].sons[0].typ, {tyGenericInst, tyVar, tyOrdinal}) else: n.sons[i] = semExprWithType(c, n.sons[i]) - if typ == nil: + if typ == nil: typ = skipTypes(n.sons[i].typ, {tyGenericInst, tyVar, tyOrdinal}) if not isOrdinalType(typ): localError(n.info, errOrdinalTypeExpected) typ = makeRangeType(c, 0, MaxSetElements-1, n.info) - elif lengthOrd(typ) > MaxSetElements: + elif lengthOrd(typ) > MaxSetElements: typ = makeRangeType(c, 0, MaxSetElements-1, n.info) addSonSkipIntLit(result.typ, typ) - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): var m: PNode if isRange(n.sons[i]): m = newNodeI(nkRange, n.sons[i].info) @@ -1786,7 +1786,7 @@ proc semSetConstr(c: PContext, n: PNode): PNode = addSon(result, m) proc semTableConstr(c: PContext, n: PNode): PNode = - # we simply transform ``{key: value, key2, key3: value}`` to + # we simply transform ``{key: value, key2, key3: value}`` to # ``[(key, value), (key2, value2), (key3, value2)]`` result = newNodeI(nkBracket, n.info) var lastKey = 0 @@ -1815,7 +1815,7 @@ type proc checkPar(n: PNode): TParKind = var length = sonsLen(n) - if length == 0: + if length == 0: result = paTuplePositions # () elif length == 1: if n.sons[0].kind == nkExprColonExpr: result = paTupleFields @@ -1845,7 +1845,7 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = var id: PIdent if n.sons[i].sons[0].kind == nkIdent: id = n.sons[i].sons[0].ident else: id = n.sons[i].sons[0].sym.name - if containsOrIncl(ids, id.id): + if containsOrIncl(ids, id.id): localError(n.sons[i].info, errFieldInitTwice, id.s) n.sons[i].sons[1] = semExprWithType(c, n.sons[i].sons[1], flags*{efAllowDestructor}) @@ -1858,14 +1858,22 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = addSon(result, n.sons[i]) result.typ = typ -proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = +proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = result = n # we don't modify n, but compute the type: var typ = newTypeS(tyTuple, c) # leave typ.n nil! - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): n.sons[i] = semExprWithType(c, n.sons[i], flags*{efAllowDestructor}) addSonSkipIntLit(typ, n.sons[i].typ) result.typ = typ +proc isTupleType(n: PNode): bool = + if n.len == 0: + return false # don't interpret () as type + for i in countup(0, n.len - 1): + if n[i].typ == nil or n[i].typ.kind != tyTypeDesc: + return false + return true + proc checkInitialized(n: PNode, ids: IntSet, info: TLineInfo) = case n.kind of nkRecList: @@ -1991,10 +1999,10 @@ proc setGenericParams(c: PContext, n: PNode) = for i in 1 .. <n.len: n[i].typ = semTypeNode(c, n[i], nil) -proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = +proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = n if gCmd == cmdIdeTools: suggestExpr(c, n) - if nfSem in n.flags: return + if nfSem in n.flags: return case n.kind of nkIdent, nkAccQuoted: var s = lookUp(c, n) @@ -2011,43 +2019,43 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # because of the changed symbol binding, this does not mean that we # don't have to check the symbol for semantics here again! result = semSym(c, n, n.sym, flags) - of nkEmpty, nkNone, nkCommentStmt: + of nkEmpty, nkNone, nkCommentStmt: discard - of nkNilLit: + of nkNilLit: result.typ = getSysType(tyNil) of nkIntLit: if result.typ == nil: setIntLitType(result) of nkInt8Lit: if result.typ == nil: result.typ = getSysType(tyInt8) - of nkInt16Lit: + of nkInt16Lit: if result.typ == nil: result.typ = getSysType(tyInt16) - of nkInt32Lit: + of nkInt32Lit: if result.typ == nil: result.typ = getSysType(tyInt32) - of nkInt64Lit: + of nkInt64Lit: if result.typ == nil: result.typ = getSysType(tyInt64) of nkUIntLit: if result.typ == nil: result.typ = getSysType(tyUInt) - of nkUInt8Lit: + of nkUInt8Lit: if result.typ == nil: result.typ = getSysType(tyUInt8) - of nkUInt16Lit: + of nkUInt16Lit: if result.typ == nil: result.typ = getSysType(tyUInt16) - of nkUInt32Lit: + of nkUInt32Lit: if result.typ == nil: result.typ = getSysType(tyUInt32) - of nkUInt64Lit: + of nkUInt64Lit: if result.typ == nil: result.typ = getSysType(tyUInt64) - of nkFloatLit: + of nkFloatLit: if result.typ == nil: result.typ = getFloatLitType(result) - of nkFloat32Lit: + of nkFloat32Lit: if result.typ == nil: result.typ = getSysType(tyFloat32) - of nkFloat64Lit: + of nkFloat64Lit: if result.typ == nil: result.typ = getSysType(tyFloat64) - of nkFloat128Lit: + of nkFloat128Lit: if result.typ == nil: result.typ = getSysType(tyFloat128) - of nkStrLit..nkTripleStrLit: + of nkStrLit..nkTripleStrLit: if result.typ == nil: result.typ = getSysType(tyString) - of nkCharLit: + of nkCharLit: if result.typ == nil: result.typ = getSysType(tyChar) - of nkDotExpr: + of nkDotExpr: result = semFieldAccess(c, n, flags) if result.kind == nkDotCall: result.kind = nkCall @@ -2055,11 +2063,11 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkBind: message(n.info, warnDeprecated, "bind") result = semExpr(c, n.sons[0], flags) - of nkTypeOfExpr, nkTupleTy, nkRefTy..nkEnumTy, nkStaticTy: + of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy: var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc, tyIter}) result.typ = makeTypeDesc(c, typ) #result = symNodeFromType(c, typ, n.info) - of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: + of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: # check if it is an expression macro: checkMinSonsLen(n, 1) let mode = if nfDotField in n.flags: {} else: {checkUndeclared} @@ -2084,7 +2092,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = semConv(c, n) elif n.len == 1: result = semObjConstr(c, n, flags) - elif contains(c.ambiguousSymbols, s.id): + elif contains(c.ambiguousSymbols, s.id): localError(n.info, errUseQualifier, s.name.s) elif s.magic == mNone: result = semDirectOp(c, n, flags) else: result = semMagic(c, n, s, flags) @@ -2123,13 +2131,20 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = semArrayAccess(c, n, flags) of nkCurlyExpr: result = semExpr(c, buildOverloadedSubscripts(n, getIdent"{}"), flags) - of nkPragmaExpr: + of nkPragmaExpr: # which pragmas are allowed for expressions? `likely`, `unlikely` internalError(n.info, "semExpr() to implement") # XXX: to implement - of nkPar: + of nkPar: case checkPar(n) of paNone: result = errorNode(c, n) - of paTuplePositions: result = semTuplePositionsConstr(c, n, flags) + of paTuplePositions: + var tupexp = semTuplePositionsConstr(c, n, flags) + if isTupleType(tupexp): + # reinterpret as type + var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc, tyIter}) + result.typ = makeTypeDesc(c, typ) + else: + result = tupexp of paTupleFields: result = semTupleFieldsConstr(c, n, flags) of paSingle: result = semExpr(c, n.sons[0], flags) of nkCurly: result = semSetConstr(c, n) @@ -2142,7 +2157,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = n checkSonsLen(n, 1) n.sons[0] = semExprWithType(c, n.sons[0]) - if isAssignable(c, n.sons[0]) notin {arLValue, arLocalLValue}: + if isAssignable(c, n.sons[0]) notin {arLValue, arLocalLValue}: localError(n.info, errExprHasNoAddress) n.typ = makePtrType(c, n.sons[0].typ) of nkHiddenAddr, nkHiddenDeref: @@ -2150,13 +2165,13 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = n.sons[0] = semExpr(c, n.sons[0], flags) of nkCast: result = semCast(c, n) of nkIfExpr, nkIfStmt: result = semIf(c, n) - of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkHiddenCallConv: + of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkHiddenCallConv: checkSonsLen(n, 2) - of nkStringToCString, nkCStringToString, nkObjDownConv, nkObjUpConv: + of nkStringToCString, nkCStringToString, nkObjDownConv, nkObjUpConv: checkSonsLen(n, 1) - of nkChckRangeF, nkChckRange64, nkChckRange: + of nkChckRangeF, nkChckRange64, nkChckRange: checkSonsLen(n, 3) - of nkCheckedFieldExpr: + of nkCheckedFieldExpr: checkMinSonsLen(n, 2) of nkTableConstr: result = semTableConstr(c, n) @@ -2191,16 +2206,16 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkConverterDef: result = semConverterDef(c, n) of nkMacroDef: result = semMacroDef(c, n) of nkTemplateDef: result = semTemplateDef(c, n) - of nkImportStmt: + of nkImportStmt: if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "import") result = evalImport(c, n) of nkImportExceptStmt: if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "import") result = evalImportExcept(c, n) - of nkFromStmt: + of nkFromStmt: if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "from") result = evalFrom(c, n) - of nkIncludeStmt: + of nkIncludeStmt: if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "include") result = evalInclude(c, n) of nkExportStmt, nkExportExceptStmt: diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 2601f05ac..db910600b 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -356,7 +356,7 @@ proc semGenericStmt(c: PContext, n: PNode, of nkIdent: a = n.sons[i] else: illFormedAst(n) addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c)) - of nkObjectTy, nkTupleTy: + of nkObjectTy, nkTupleTy, nkTupleClassTy: discard of nkFormalParams: checkMinSonsLen(n, 1) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index a10f27519..dc36ecf34 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -11,12 +11,12 @@ # included from sem.nim proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, - entry: var TInstantiation) = - if n.kind != nkGenericParams: + entry: var TInstantiation) = + if n.kind != nkGenericParams: internalError(n.info, "instantiateGenericParamList; no generic params") newSeq(entry.concreteTypes, n.len) for i, a in n.pairs: - if a.kind != nkSym: + if a.kind != nkSym: internalError(a.info, "instantiateGenericParamList; no symbol") var q = a.sym if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses: @@ -27,13 +27,13 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, var t = PType(idTableGet(pt, q.typ)) if t == nil: if tfRetType in q.typ.flags: - # keep the generic type and allow the return type to be bound + # keep the generic type and allow the return type to be bound # later by semAsgn in return type inference scenario t = q.typ else: localError(a.info, errCannotInstantiateX, s.name.s) t = errorType(c) - elif t.kind == tyGenericParam: + elif t.kind == tyGenericParam: localError(a.info, errCannotInstantiateX, q.name.s) t = errorType(c) elif t.kind == tyGenericInvocation: @@ -58,17 +58,17 @@ proc genericCacheGet(genericSym: PSym, entry: TInstantiation): PSym = if sameInstantiation(entry, inst[]): return inst.sym -proc removeDefaultParamValues(n: PNode) = +proc removeDefaultParamValues(n: PNode) = # we remove default params, because they cannot be instantiated properly # and they are not needed anyway for instantiation (each param is already # provided). when false: - for i in countup(1, sonsLen(n)-1): + for i in countup(1, sonsLen(n)-1): var a = n.sons[i] if a.kind != nkIdentDefs: IllFormedAst(a) var L = a.len if a.sons[L-1].kind != nkEmpty and a.sons[L-2].kind != nkEmpty: - # ``param: typ = defaultVal``. + # ``param: typ = defaultVal``. # We don't need defaultVal for semantic checking and it's wrong for # ``cmp: proc (a, b: T): int = cmp``. Hm, for ``cmp = cmp`` that is # not possible... XXX We don't solve this issue here. @@ -97,7 +97,7 @@ proc addProcDecls(c: PContext, fn: PSym) = var param = fn.typ.n.sons[i].sym param.owner = fn addParamOrResult(c, param, fn.kind) - + maybeAddResult(c, fn, fn.ast) proc instantiateBody(c: PContext, n, params: PNode, result: PSym) = @@ -132,9 +132,9 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) = closeScope(c) popInfoContext() -proc sideEffectsCheck(c: PContext, s: PSym) = +proc sideEffectsCheck(c: PContext, s: PSym) = if {sfNoSideEffect, sfSideEffect} * s.flags == - {sfNoSideEffect, sfSideEffect}: + {sfNoSideEffect, sfSideEffect}: localError(s.info, errXhasSideEffects, s.name.s) proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, @@ -162,18 +162,18 @@ proc instantiateProcType(c: PContext, pt: TIdTable, # Alas, doing this here is probably not enough, because another # proc signature could appear in the params: # proc foo[T](a: proc (x: T, b: type(x.y)) - # + # # The solution would be to move this logic into semtypinst, but # at this point semtypinst have to become part of sem, because it # will need to use openScope, addDecl, etc. addDecl(c, prc) - + pushInfoContext(info) var cl = initTypeVars(c, pt, info) var result = instCopyType(cl, prc.typ) let originalParams = result.n result.n = originalParams.shallowCopy - + for i in 1 .. <result.len: # twrong_field_caching requires these 'resetIdTable' calls: if i > 1: resetIdTable(cl.symMap) @@ -198,10 +198,10 @@ proc instantiateProcType(c: PContext, pt: TIdTable, resetIdTable(cl.symMap) result.sons[0] = replaceTypeVarsT(cl, result.sons[0]) result.n.sons[0] = originalParams[0].copyTree - + eraseVoidParams(result) skipIntLiteralParams(result) - + prc.typ = result maybeAddResult(c, prc, prc.ast) popInfoContext() diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 60153e052..14644a8d6 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -138,7 +138,7 @@ proc guardDotAccess(a: PEffects; n: PNode) = if g.kind == skUnknown: var field: PSym = nil var ty = n.sons[0].typ.skipTypes(abstractPtrs) - if ty.kind == tyTuple: + if ty.kind == tyTuple and not ty.n.isNil: field = lookupInRecord(ty.n, g.name) else: while ty != nil and ty.kind == tyObject: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 4a2e107d7..374302165 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -695,7 +695,7 @@ proc typeSectionFinalPass(c: PContext, n: PNode) = assignType(s.typ, t) s.typ.id = t.id # same id checkConstructedType(s.info, s.typ) - if s.typ.kind in {tyObject, tyTuple}: + if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil: checkForMetaFields(s.typ.n) let aa = a.sons[2] if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 0735b76ce..ac0636211 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -345,8 +345,14 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = localError(n.info, errIdentifierExpected) result = errorSym(c, n) -proc semTuple(c: PContext, n: PNode, prev: PType): PType = - if n.sonsLen == 0: return newConstraint(c, tyTuple) +proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType = + if sonsLen(n) == 0: + localError(n.info, errTypeExpected) + result = newOrPrevType(tyTuple, prev, c) + for i in countup(0, sonsLen(n) - 1): + addSonSkipIntLit(result, semTypeNode(c, n.sons[i], nil)) + +proc semTuple(c: PContext, n: PNode, prev: PType): PType = var typ: PType result = newOrPrevType(tyTuple, prev, c) result.n = newNodeI(nkRecList, n.info) @@ -1117,9 +1123,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of nkPar: if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev) else: - # XXX support anon tuple here - localError(n.info, errTypeExpected) - result = newOrPrevType(tyError, prev, c) + result = semAnonTuple(c, n, prev) of nkCallKinds: if isRange(n): result = semRangeAux(c, n, prev) @@ -1227,6 +1231,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = newOrPrevType(tyError, prev, c) of nkObjectTy: result = semObjectNode(c, n, prev) of nkTupleTy: result = semTuple(c, n, prev) + of nkTupleClassTy: result = newConstraint(c, tyTuple) of nkTypeClassTy: result = semTypeClass(c, n, prev) of nkRefTy: result = semAnyRef(c, n, tyRef, prev) of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 57aa6305e..012782730 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -341,6 +341,8 @@ proc skipIntLiteralParams*(t: PType) = proc propagateFieldFlags(t: PType, n: PNode) = # This is meant for objects and tuples # The type must be fully instantiated! + if n.isNil: + return internalAssert n.kind != nkRecWhen case n.kind of nkSym: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index a1b5c8dc9..6fb50f8e5 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -10,7 +10,7 @@ ## This module implements the signature matching for resolving ## the call to overloaded procs, generic procs and operators. -import +import intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst, magicsys, condsyms, idents, lexer, options, parampatterns, strutils, trees, nimfix.pretty @@ -19,7 +19,7 @@ when not defined(noDocgen): import docgen type - TCandidateState* = enum + TCandidateState* = enum csEmpty, csMatch, csNoMatch CandidateErrors* = seq[PSym] @@ -62,10 +62,10 @@ type isGeneric, isFromIntLit, # conversion *from* int literal; proven safe isEqual - + const isNilConversion = isConvertible # maybe 'isIntConv' fits better? - + proc markUsed*(info: TLineInfo, s: PSym) template hasFauxMatch*(c: TCandidate): bool = c.fauxMatch != tyNone @@ -128,7 +128,7 @@ proc newCandidate*(ctx: PContext, callee: PSym, proc newCandidate*(ctx: PContext, callee: PType): TCandidate = initCandidate(ctx, result, callee) -proc copyCandidate(a: var TCandidate, b: TCandidate) = +proc copyCandidate(a: var TCandidate, b: TCandidate) = a.c = b.c a.exactMatches = b.exactMatches a.subtypeMatches = b.subtypeMatches @@ -176,7 +176,7 @@ proc complexDisambiguation(a, b: PType): int = let bb = b.sons[1].sumGeneric var a = a var b = b - + if aa < bb: swap(a, b) # all must be better for i in 2 .. <min(a.len, b.len): @@ -203,7 +203,7 @@ proc cmpCandidates*(a, b: TCandidate): int = # prefer more specialized generic over more general generic: result = complexDisambiguation(a.callee, b.callee) -proc writeMatches*(c: TCandidate) = +proc writeMatches*(c: TCandidate) = writeln(stdout, "exact matches: " & $c.exactMatches) writeln(stdout, "generic matches: " & $c.genericMatches) writeln(stdout, "subtype matches: " & $c.subtypeMatches) @@ -225,7 +225,7 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1; result = "" for i in countup(startIdx, n.len - 1): var arg = n.sons[i] - if n.sons[i].kind == nkExprEqExpr: + if n.sons[i].kind == nkExprEqExpr: add(result, renderTree(n.sons[i].sons[0])) add(result, ": ") if arg.typ.isNil: @@ -241,9 +241,9 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1; if i != sonsLen(n) - 1: add(result, ", ") proc typeRel*(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation -proc concreteType(c: TCandidate, t: PType): PType = +proc concreteType(c: TCandidate, t: PType): PType = case t.kind - of tyArrayConstr: + of tyArrayConstr: # make it an array result = newType(tyArray, t.owner) addSonSkipIntLit(result, t.sons[0]) # XXX: t.owner is wrong for ID! @@ -255,7 +255,7 @@ proc concreteType(c: TCandidate, t: PType): PType = else: result = t of tyGenericParam, tyAnything: result = t - while true: + while true: result = PType(idTableGet(c.bindings, t)) if result == nil: break # it's ok, no match @@ -267,15 +267,15 @@ proc concreteType(c: TCandidate, t: PType): PType = result = t else: result = t # Note: empty is valid here - -proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation = - if a.kind == f.kind: + +proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation = + if a.kind == f.kind: result = isEqual else: let ab = skipTypes(a, {tyRange}) let k = ab.kind if k == f.kind: result = isSubrange - elif k == tyInt and f.kind in {tyRange, tyInt8..tyInt64, + elif k == tyInt and f.kind in {tyRange, tyInt8..tyInt64, tyUInt..tyUInt64} and isIntLit(ab) and ab.n.intVal >= firstOrd(f) and ab.n.intVal <= lastOrd(f): @@ -286,7 +286,7 @@ proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation = result = isIntConv elif k >= min and k <= max: result = isConvertible - elif a.kind == tyRange and a.sons[0].kind in {tyInt..tyInt64, + elif a.kind == tyRange and a.sons[0].kind in {tyInt..tyInt64, tyUInt8..tyUInt32} and a.n[0].intVal >= firstOrd(f) and a.n[1].intVal <= lastOrd(f): @@ -318,12 +318,12 @@ proc handleFloatRange(f, a: PType): TTypeRelation = if f.kind == tyFloat32: result = isConvertible else: result = isIntConv else: result = isNone - + proc isObjectSubtype(a, f: PType): int = var t = a assert t.kind == tyObject var depth = 0 - while t != nil and not sameObjectTypes(f, t): + while t != nil and not sameObjectTypes(f, t): assert t.kind == tyObject t = t.sons[0] if t == nil: break @@ -332,17 +332,17 @@ proc isObjectSubtype(a, f: PType): int = if t != nil: result = depth -proc minRel(a, b: TTypeRelation): TTypeRelation = +proc minRel(a, b: TTypeRelation): TTypeRelation = if a <= b: result = a else: result = b - + proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation = result = isNone if sameType(f, a): result = isEqual elif sonsLen(a) == sonsLen(f): result = isEqual let firstField = if f.kind == tyTuple: 0 - else: 1 + else: 1 for i in countup(firstField, sonsLen(f) - 1): var m = typeRel(c, f.sons[i], a.sons[i]) if m < isSubtype: return isNone @@ -373,7 +373,7 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = # We are matching a generic proc (as proc param) # to another generic type appearing in the proc # signature. There is a change that the target - # type is already fully-determined, so we are + # type is already fully-determined, so we are # going to try resolve it f = generateTypeInstance(c.c, c.bindings, c.call.info, f) if f == nil or f.isMetaType: @@ -388,17 +388,17 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = if result <= isSubtype or inconsistentVarTypes(f, a): result = isNone - + if result == isEqual: inc c.exactMatches - + proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = case a.kind of tyProc: if sonsLen(f) != sonsLen(a): return result = isEqual # start with maximum; also correct for no # params at all - + template checkParam(f, a) = result = minRel(result, procParamTypeRel(c, f, a)) if result == isNone: return @@ -407,7 +407,7 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = # return type! for i in 1 .. <f.sonsLen: checkParam(f.sons[i], a.sons[i]) - + if f.sons[0] != nil: if a.sons[0] != nil: checkParam(f.sons[0], a.sons[0]) @@ -487,14 +487,14 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, else: param = paramSym skType param.typ = makeTypeDesc(c, typ) - + addDecl(c, param) for param in body.n[0]: var dummyName: PNode dummyType: PType - + if param.kind == nkVarTy: dummyName = param[0] dummyType = if a.kind != tyVar: makeVarType(c, a) @@ -520,7 +520,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, of nkTypeSection: discard of nkConstDef: discard else: discard - + return isGeneric proc shouldSkipDistinct(rules: PNode, callIdent: PIdent): bool = @@ -554,7 +554,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # typeRel can be used to establish various relationships between types: # # 1) When used with concrete types, it will check for type equivalence - # or a subtype relationship. + # or a subtype relationship. # # 2) When used with a concrete type against a type class (such as generic # signature of a proc), it will check whether the concrete type is a member @@ -569,7 +569,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = isNone assert(f != nil) - + if f.kind == tyExpr: if aOrig != nil: put(c.bindings, f, aOrig) return isGeneric @@ -582,7 +582,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # start the param matching process. This could be done in `prepareOperand` # for example, but unfortunately `prepareOperand` is not called in certain # situation when nkDotExpr are rotated to nkDotCalls - + if a.kind == tyGenericInst and skipTypes(f, {tyVar}).kind notin { tyGenericBody, tyGenericInvocation, @@ -626,9 +626,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # seq[!int] vs seq[!number] # seq[float] matches the first, but not the second # we must turn the problem around: - # is number a subset of int? + # is number a subset of int? return typeRel(c, a.lastSon, f.lastSon) - + else: # negative type classes are essentially infinite, # so only the `any` type class is their superset @@ -727,20 +727,20 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyOpenArray, tyVarargs: result = typeRel(c, base(f), base(a)) if result < isGeneric: result = isNone - of tyArrayConstr: - if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty): + of tyArrayConstr: + if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty): result = isSubtype # [] is allowed here - elif typeRel(c, base(f), a.sons[1]) >= isGeneric: + elif typeRel(c, base(f), a.sons[1]) >= isGeneric: result = isSubtype - of tyArray: - if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty): + of tyArray: + if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty): result = isSubtype - elif typeRel(c, base(f), a.sons[1]) >= isGeneric: + elif typeRel(c, base(f), a.sons[1]) >= isGeneric: result = isConvertible - of tySequence: - if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): + of tySequence: + if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): result = isConvertible - elif typeRel(c, base(f), a.sons[0]) >= isGeneric: + elif typeRel(c, base(f), a.sons[0]) >= isGeneric: result = isConvertible else: discard of tySequence: @@ -768,7 +768,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyForward: internalError("forward type in typeRel()") of tyNil: if a.kind == f.kind: result = isEqual - of tyTuple: + of tyTuple: if a.kind == tyTuple: result = recordRel(c, f, a) of tyObject: if a.kind == tyObject: @@ -783,13 +783,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyDistinct: if (a.kind == tyDistinct) and sameDistinctTypes(f, a): result = isEqual elif c.coerceDistincts: result = typeRel(c, f.base, a) - of tySet: - if a.kind == tySet: - if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): + of tySet: + if a.kind == tySet: + if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): result = isSubtype - else: + else: result = typeRel(c, f.sons[0], a.sons[0]) - if result <= isConvertible: + if result <= isConvertible: result = isNone # BUGFIX! of tyPtr, tyRef: if a.kind == f.kind: @@ -823,9 +823,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = if a.len == 1: result = isConvertible of tyCString: result = isConvertible else: discard - of tyString: + of tyString: case a.kind - of tyString: + of tyString: if tfNotNil in f.flags and tfNotNil notin a.flags: result = isNilConversion else: @@ -848,7 +848,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyArray: if (firstOrd(a.sons[0]) == 0) and (skipTypes(a.sons[0], {tyRange}).kind in {tyInt..tyInt64}) and - (a.sons[1].kind == tyChar): + (a.sons[1].kind == tyChar): result = isConvertible else: discard @@ -863,7 +863,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = let ff = rootf.sons[i] let aa = roota.sons[i] result = typeRel(c, ff, aa) - if result == isNone: return + if result == isNone: return if ff.kind == tyRange and result != isEqual: return isNone result = isGeneric # XXX See bug #2220. A[int] should match A[int] better than some generic X @@ -883,13 +883,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = #InternalError("typeRel: tyGenericInvocation -> tyGenericInvocation") # simply no match for now: discard - elif x.kind == tyGenericInst and + elif x.kind == tyGenericInst and (f.sons[0] == x.sons[0]) and (sonsLen(x) - 1 == sonsLen(f)): for i in countup(1, sonsLen(f) - 1): if x.sons[i].kind == tyGenericParam: internalError("wrong instantiated type!") - elif typeRel(c, f.sons[i], x.sons[i]) <= isSubtype: return + elif typeRel(c, f.sons[i], x.sons[i]) <= isSubtype: return result = isGeneric else: result = typeRel(c, f.sons[0], x) @@ -900,7 +900,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = if x == nil or x.kind in {tyGenericInvocation, tyGenericParam}: internalError("wrong instantiated type!") put(c.bindings, f.sons[i], x) - + of tyAnd: considerPreviousT: for branch in f.sons: @@ -914,7 +914,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = for branch in f.sons: if typeRel(c, branch, aOrig) >= isSubtype: bindingRet isGeneric - + return isNone of tyNot: @@ -922,7 +922,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = for branch in f.sons: if typeRel(c, branch, aOrig) != isNone: return isNone - + bindingRet isGeneric of tyAnything: @@ -961,7 +961,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = if x == nil: if c.callee.kind == tyGenericBody and f.kind == tyGenericParam and not c.typedescMatched: - # XXX: The fact that generic types currently use tyGenericParam for + # XXX: The fact that generic types currently use tyGenericParam for # their parameters is really a misnomer. tyGenericParam means "match # any value" and what we need is "match any type", which can be encoded # by a tyTypeDesc params. Unfortunately, this requires more substantial @@ -1004,7 +1004,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = isGeneric else: result = typeRel(c, x, a) # check if it fits - + of tyStatic: let prev = PType(idTableGet(c.bindings, f)) if prev == nil: @@ -1034,12 +1034,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # when `f` is an unresolved typedesc, `a` could be any # type, so we should not perform this check earlier if a.kind != tyTypeDesc: return isNone - + if f.base.kind == tyNone: result = isGeneric else: result = typeRel(c, f.base, a.base) - + if result != isNone: put(c.bindings, f, a) else: @@ -1049,9 +1049,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = typeRel(c, prev.base, a.base) else: result = isNone - + of tyIter: - if a.kind == tyIter or + if a.kind == tyIter or (a.kind == tyProc and tfIterator in a.flags): result = typeRel(c, f.base, a.base) else: @@ -1059,7 +1059,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyStmt: result = isGeneric - + of tyProxy: result = isEqual @@ -1078,11 +1078,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = else: localError(f.n.info, errTypeExpected) result = isNone - + else: internalAssert false - -proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation = + +proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation = var m: TCandidate initCandidate(c, m, f) result = typeRel(m, f, a) @@ -1095,9 +1095,9 @@ proc getInstantiatedType(c: PContext, arg: PNode, m: TCandidate, if result == nil: internalError(arg.info, "getInstantiatedType") result = errorType(c) - -proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate, - c: PContext): PNode = + +proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate, + c: PContext): PNode = result = newNodeI(kind, arg.info) if containsGenericType(f): if not m.hasFauxMatch: @@ -1110,10 +1110,10 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate, addSon(result, ast.emptyNode) addSon(result, arg) -proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, - arg: PNode): PNode = +proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, + arg: PNode): PNode = result = nil - for i in countup(0, len(c.converters) - 1): + for i in countup(0, len(c.converters) - 1): var src = c.converters[i].typ.sons[1] var dest = c.converters[i].typ.sons[0] # for generic type converters we need to check 'src <- a' before @@ -1121,12 +1121,12 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, # see tests/tgenericconverter: let srca = typeRel(m, src, a) if srca notin {isEqual, isGeneric}: continue - + let destIsGeneric = containsGenericType(dest) if destIsGeneric: dest = generateTypeInstance(c, m.bindings, arg, dest) let fdest = typeRel(m, f, dest) - if fdest in {isEqual, isGeneric}: + if fdest in {isEqual, isGeneric}: markUsed(arg.info, c.converters[i]) var s = newSymNode(c.converters[i]) s.typ = c.converters[i].typ @@ -1138,8 +1138,8 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, m.genericConverter = srca == isGeneric or destIsGeneric return result -proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType, - arg: PNode): PNode = +proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType, + arg: PNode): PNode = # arg.typ can be nil in 'suggest': if isNil(arg.typ): return nil @@ -1181,12 +1181,12 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, arg = argSemantized argType = argType c = m.c - + if tfHasStatic in fMaybeStatic.flags: # XXX: When implicit statics are the default # this will be done earlier - we just have to # make sure that static types enter here - + # XXX: weaken tyGenericParam and call it tyGenericPlaceholder # and finally start using tyTypedesc for generic types properly. if argType.kind == tyGenericParam and tfWildcard in argType.flags: @@ -1205,11 +1205,11 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, arg.typ.sons = @[evaluated.typ] arg.typ.n = evaluated argType = arg.typ - + var a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc, tyFieldAccessor}) else: argType - + r = typeRel(m, f, a) if r != isNone and m.calleeSym != nil and @@ -1232,7 +1232,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, elif f.kind == tyStatic: return arg.typ.n else: - return argOrig + return argSemantized # argOrig if r != isNone and f.isInlineIterator: var inlined = newTypeS(tyStatic, c) @@ -1244,19 +1244,18 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, case r of isConvertible: inc(m.convMatches) - result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) + result = implicitConv(nkHiddenStdConv, f, arg, m, c) of isIntConv: # I'm too lazy to introduce another ``*matches`` field, so we conflate # ``isIntConv`` and ``isIntLit`` here: inc(m.intConvMatches) - result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) - of isSubtype: + result = implicitConv(nkHiddenStdConv, f, arg, m, c) + of isSubtype: inc(m.subtypeMatches) - result = implicitConv(nkHiddenSubConv, f, copyTree(arg), m, c) + result = implicitConv(nkHiddenSubConv, f, arg, m, c) of isSubrange: inc(m.subtypeMatches) - #result = copyTree(arg) - result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) + result = implicitConv(nkHiddenStdConv, f, arg, m, c) of isInferred, isInferredConvertible: inc(m.genericMatches) if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds: @@ -1269,42 +1268,32 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, result = implicitConv(nkHiddenStdConv, f, result, m, c) of isGeneric: inc(m.genericMatches) - when true: - if arg.typ == nil: - result = arg - elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple: - result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) - elif arg.typ.isEmptyContainer: - result = arg.copyTree - result.typ = getInstantiatedType(c, arg, m, f) - else: - result = arg - else: - # XXX Why is this ever necessary? arg's type should not be retrofitted - # to match formal's type in this way! - result = copyTree(arg) + if arg.typ == nil: + result = arg + elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple: + result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) + elif arg.typ.isEmptyContainer: + result = arg.copyTree result.typ = getInstantiatedType(c, arg, m, f) - # BUG: f may not be the right key! - if skipTypes(result.typ, abstractVar-{tyTypeDesc}).kind in {tyTuple}: - result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) - # BUGFIX: use ``result.typ`` and not `f` here + else: + result = arg of isFromIntLit: # too lazy to introduce another ``*matches`` field, so we conflate # ``isIntConv`` and ``isIntLit`` here: inc(m.intConvMatches, 256) - result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) - of isEqual: + result = implicitConv(nkHiddenStdConv, f, arg, m, c) + of isEqual: inc(m.exactMatches) - result = copyTree(arg) + result = arg if skipTypes(f, abstractVar-{tyTypeDesc}).kind in {tyTuple}: - result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) + result = implicitConv(nkHiddenStdConv, f, arg, m, c) of isNone: # do not do this in ``typeRel`` as it then can't infere T in ``ref T``: if a.kind in {tyProxy, tyUnknown}: inc(m.genericMatches) m.fauxMatch = a.kind - return copyTree(arg) - result = userConvMatch(c, m, f, a, arg) + return arg + result = userConvMatch(c, m, f, a, arg) # check for a base type match, which supports varargs[T] without [] # constructor in a call: if result == nil and f.kind == tyVarargs: @@ -1325,7 +1314,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, arg, argOrig: PNode): PNode = if arg == nil or arg.kind notin nkSymChoices: result = paramTypesMatchAux(m, f, a, arg, argOrig) - else: + else: # CAUTION: The order depends on the used hashing scheme. Thus it is # incorrect to simply use the first fitting match. However, to implement # this correctly is inefficient. We have to copy `m` here to be able to @@ -1339,28 +1328,28 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, y.calleeSym = m.calleeSym z.calleeSym = m.calleeSym var best = -1 - for i in countup(0, sonsLen(arg) - 1): + for i in countup(0, sonsLen(arg) - 1): if arg.sons[i].sym.kind in {skProc, skMethod, skConverter}+skIterators: copyCandidate(z, m) var r = typeRel(z, f, arg.sons[i].typ) - if r != isNone: + if r != isNone: case x.state - of csEmpty, csNoMatch: + of csEmpty, csNoMatch: x = z best = i x.state = csMatch - of csMatch: + of csMatch: var cmp = cmpCandidates(x, z) if cmp < 0: best = i x = z elif cmp == 0: y = z # z is as good as x - if x.state == csEmpty: + if x.state == csEmpty: result = nil - elif y.state == csMatch and cmpCandidates(x, y) == 0: - if x.state != csMatch: - internalError(arg.info, "x.state is not csMatch") + elif y.state == csMatch and cmpCandidates(x, y) == 0: + if x.state != csMatch: + internalError(arg.info, "x.state is not csMatch") # ambiguous: more than one symbol fits! # See tsymchoice_for_expr as an example. 'f.kind == tyExpr' should match # anyway: @@ -1373,7 +1362,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, result = paramTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best], argOrig) -proc setSon(father: PNode, at: int, son: PNode) = +proc setSon(father: PNode, at: int, son: PNode) = if sonsLen(father) <= at: setLen(father.sons, at + 1) father.sons[at] = son @@ -1417,7 +1406,7 @@ proc incrIndexType(t: PType) = inc t.sons[0].n.sons[1].intVal proc matchesAux(c: PContext, n, nOrig: PNode, - m: var TCandidate, marker: var IntSet) = + m: var TCandidate, marker: var IntSet) = template checkConstraint(n: expr) {.immediate, dirty.} = if not formal.constraint.isNil: if matchNodeKinds(formal.constraint, n): @@ -1447,20 +1436,20 @@ proc matchesAux(c: PContext, n, nOrig: PNode, # named param # check if m.callee has such a param: prepareNamedParam(n.sons[a]) - if n.sons[a].sons[0].kind != nkIdent: + if n.sons[a].sons[0].kind != nkIdent: localError(n.sons[a].info, errNamedParamHasToBeIdent) m.state = csNoMatch - return + return formal = getSymFromList(m.callee.n, n.sons[a].sons[0].ident, 1) - if formal == nil: + if formal == nil: # no error message! m.state = csNoMatch - return - if containsOrIncl(marker, formal.position): + return + if containsOrIncl(marker, formal.position): # already in namedParams: localError(n.sons[a].info, errCannotBindXTwice, formal.name.s) m.state = csNoMatch - return + return m.baseTypeMatch = false n.sons[a].sons[1] = prepareOperand(c, formal.typ, n.sons[a].sons[1]) n.sons[a].typ = n.sons[a].sons[1].typ @@ -1470,7 +1459,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m.state = csNoMatch return checkConstraint(n.sons[a].sons[1]) - if m.baseTypeMatch: + if m.baseTypeMatch: #assert(container == nil) container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg)) addSon(container, arg) @@ -1507,7 +1496,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m.state = csNoMatch return else: - if m.callee.n.sons[f].kind != nkSym: + if m.callee.n.sons[f].kind != nkSym: internalError(n.sons[a].info, "matches") return formal = m.callee.n.sons[f].sym @@ -1515,7 +1504,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, # already in namedParams: localError(n.sons[a].info, errCannotBindXTwice, formal.name.s) m.state = csNoMatch - return + return m.baseTypeMatch = false n.sons[a] = prepareOperand(c, formal.typ, n.sons[a]) var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, @@ -1528,7 +1517,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, if container.isNil: container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg)) addSon(container, arg) - setSon(m.call, formal.position + 1, + setSon(m.call, formal.position + 1, implicitConv(nkHiddenStdConv, formal.typ, container, m, c)) #if f != formalLen - 1: container = nil @@ -1603,7 +1592,7 @@ when not declared(tests): tests: var dummyOwner = newSym(skModule, getIdent("test_module"), nil, UnknownLineInfo()) - + proc `|` (t1, t2: PType): PType = result = newType(tyOr, dummyOwner) result.rawAddSon(t1) @@ -1624,12 +1613,12 @@ tests: proc array(x: int, t: PType): PType = result = newType(tyArray, dummyOwner) - + var n = newNodeI(nkRange, UnknownLineInfo()) addSon(n, newIntNode(nkIntLit, 0)) addSon(n, newIntNode(nkIntLit, x)) let range = newType(tyRange, dummyOwner) - + result.rawAddSon(range) result.rawAddSon(t) @@ -1664,7 +1653,7 @@ tests: template no(x, y) = test astToStr(x) & " is not " & astToStr(y): check typeRel(c, y, x) == isNone - + yes seq(any), array(10, int) | seq(any) # Sure, seq[any] is directly included @@ -1672,16 +1661,16 @@ tests: yes seq(int), seq(number) # Sure, the int sequence is certainly # part of the number sequences (and all sequences) - + no seq(any), seq(float) # Nope, seq[any] includes types that are not seq[float] (e.g. seq[int]) yes seq(int|string), seq(any) # Sure - + yes seq(int&string), seq(any) # Again - + yes seq(int&string), seq(int) # A bit more complicated # seq[int&string] is not a real type, but it's analogous to @@ -1690,23 +1679,23 @@ tests: no seq(int|string), seq(int|float) # Nope, seq[string] is not included in not included in # the seq[int|float] set - + no seq(!(int|string)), seq(string) # A sequence that is neither seq[int] or seq[string] # is obviously not seq[string] - + no seq(!int), seq(number) # Now your head should start to hurt a bit # A sequence that is not seq[int] is not necessarily a number sequence # it could well be seq[string] for example - + yes seq(!(int|string)), seq(!string) # all sequnece types besides seq[int] and seq[string] # are subset of all sequence types that are not seq[string] no seq(!(int|string)), seq(!(string|TFoo)) # Nope, seq[TFoo] is included in the first set, but not in the second - + no seq(!string), seq(!number) # Nope, seq[int] in included in the first set, but not in the second @@ -1714,7 +1703,7 @@ tests: yes seq(!int), seq(any) no seq(any), seq(!any) no seq(!int), seq(!any) - + yes int, ordinal no string, ordinal diff --git a/compiler/transf.nim b/compiler/transf.nim index 2f520aa20..e5e3bbe63 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -17,27 +17,27 @@ # * introduces method dispatchers # * performs lambda lifting for closure support -import - intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os, +import + intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os, idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread, lambdalifting, sempass2, lowerings # implementation -type +type PTransNode* = distinct PNode - + PTransCon = ref TTransCon TTransCon{.final.} = object # part of TContext; stackable mapping: TIdNodeTable # mapping from symbols to nodes owner: PSym # current owner forStmt: PNode # current for stmt - forLoopBody: PTransNode # transformed for loop body - yieldStmts: int # we count the number of yield statements, + forLoopBody: PTransNode # transformed for loop body + yieldStmts: int # we count the number of yield statements, # because we need to introduce new variables # if we encounter the 2nd yield statement next: PTransCon # for stacking - + TTransfContext = object of passes.TPassContext module: PSym transCon: PTransCon # top of a TransCon stack @@ -46,52 +46,52 @@ type contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' PTransf = ref TTransfContext -proc newTransNode(a: PNode): PTransNode {.inline.} = +proc newTransNode(a: PNode): PTransNode {.inline.} = result = PTransNode(shallowCopy(a)) -proc newTransNode(kind: TNodeKind, info: TLineInfo, - sons: int): PTransNode {.inline.} = +proc newTransNode(kind: TNodeKind, info: TLineInfo, + sons: int): PTransNode {.inline.} = var x = newNodeI(kind, info) newSeq(x.sons, sons) result = x.PTransNode -proc newTransNode(kind: TNodeKind, n: PNode, - sons: int): PTransNode {.inline.} = +proc newTransNode(kind: TNodeKind, n: PNode, + sons: int): PTransNode {.inline.} = var x = newNodeIT(kind, n.info, n.typ) newSeq(x.sons, sons) x.typ = n.typ result = x.PTransNode -proc `[]=`(a: PTransNode, i: int, x: PTransNode) {.inline.} = +proc `[]=`(a: PTransNode, i: int, x: PTransNode) {.inline.} = var n = PNode(a) n.sons[i] = PNode(x) -proc `[]`(a: PTransNode, i: int): PTransNode {.inline.} = +proc `[]`(a: PTransNode, i: int): PTransNode {.inline.} = var n = PNode(a) result = n.sons[i].PTransNode - + proc add(a, b: PTransNode) {.inline.} = addSon(PNode(a), PNode(b)) proc len(a: PTransNode): int {.inline.} = result = sonsLen(a.PNode) -proc newTransCon(owner: PSym): PTransCon = +proc newTransCon(owner: PSym): PTransCon = assert owner != nil new(result) initIdNodeTable(result.mapping) result.owner = owner -proc pushTransCon(c: PTransf, t: PTransCon) = +proc pushTransCon(c: PTransf, t: PTransCon) = t.next = c.transCon c.transCon = t -proc popTransCon(c: PTransf) = +proc popTransCon(c: PTransf) = if (c.transCon == nil): internalError("popTransCon") c.transCon = c.transCon.next -proc getCurrOwner(c: PTransf): PSym = +proc getCurrOwner(c: PTransf): PSym = if c.transCon != nil: result = c.transCon.owner else: result = c.module - -proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PSym = + +proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PSym = result = newSym(skTemp, getIdent(genPrefix), getCurrOwner(c), info) result.typ = skipTypes(typ, {tyGenericInst}) incl(result.flags, sfFromGeneric) @@ -100,10 +100,10 @@ proc transform(c: PTransf, n: PNode): PTransNode proc transformSons(c: PTransf, n: PNode): PTransNode = result = newTransNode(n) - for i in countup(0, sonsLen(n)-1): + for i in countup(0, sonsLen(n)-1): result[i] = transform(c, n.sons[i]) -proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode = +proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode = result = newTransNode(nkFastAsgn, PNode(ri).info, 2) result[0] = PTransNode(le) result[1] = ri @@ -113,30 +113,30 @@ proc transformSymAux(c: PTransf, n: PNode): PNode = # return liftIterSym(n) var b: PNode var tc = c.transCon - if sfBorrow in n.sym.flags: + if sfBorrow in n.sym.flags: # simply exchange the symbol: b = n.sym.getBody if b.kind != nkSym: internalError(n.info, "wrong AST for borrowed symbol") b = newSymNode(b.sym) b.info = n.info - else: + else: b = n - while tc != nil: + while tc != nil: result = idNodeTableGet(tc.mapping, b.sym) if result != nil: return tc = tc.next result = b -proc transformSym(c: PTransf, n: PNode): PTransNode = +proc transformSym(c: PTransf, n: PNode): PTransNode = result = PTransNode(transformSymAux(c, n)) proc transformVarSection(c: PTransf, v: PNode): PTransNode = result = newTransNode(v) for i in countup(0, sonsLen(v)-1): var it = v.sons[i] - if it.kind == nkCommentStmt: + if it.kind == nkCommentStmt: result[i] = PTransNode(it) - elif it.kind == nkIdentDefs: + elif it.kind == nkIdentDefs: if it.sons[0].kind != nkSym: internalError(it.info, "transformVarSection") internalAssert(it.len == 3) var newVar = copySym(it.sons[0].sym) @@ -153,12 +153,12 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode = defs[1] = it.sons[1].PTransNode defs[2] = transform(c, it.sons[2]) result[i] = defs - else: - if it.kind != nkVarTuple: + else: + if it.kind != nkVarTuple: internalError(it.info, "transformVarSection: not nkVarTuple") var L = sonsLen(it) var defs = newTransNode(it.kind, it.info, L) - for j in countup(0, L-3): + for j in countup(0, L-3): var newVar = copySym(it.sons[j].sym) incl(newVar.flags, sfFromGeneric) newVar.owner = getCurrOwner(c) @@ -188,12 +188,12 @@ proc transformConstSection(c: PTransf, v: PNode): PTransNode = else: result[i] = PTransNode(it) -proc hasContinue(n: PNode): bool = +proc hasContinue(n: PNode): bool = case n.kind of nkEmpty..nkNilLit, nkForStmt, nkParForStmt, nkWhileStmt: discard of nkContinueStmt: result = true - else: - for i in countup(0, sonsLen(n) - 1): + else: + for i in countup(0, sonsLen(n) - 1): if hasContinue(n.sons[i]): return true proc newLabel(c: PTransf, n: PNode): PSym = @@ -224,10 +224,10 @@ proc transformBlock(c: PTransf, n: PNode): PTransNode = discard c.breakSyms.pop result[0] = newSymNode(labl).PTransNode -proc transformLoopBody(c: PTransf, n: PNode): PTransNode = - # What if it contains "continue" and "break"? "break" needs +proc transformLoopBody(c: PTransf, n: PNode): PTransNode = + # What if it contains "continue" and "break"? "break" needs # an explicit label too, but not the same! - + # We fix this here by making every 'break' belong to its enclosing loop # and changing all breaks that belong to a 'block' by annotating it with # a label (if it hasn't one already). @@ -239,7 +239,7 @@ proc transformLoopBody(c: PTransf, n: PNode): PTransNode = result[0] = newSymNode(labl).PTransNode result[1] = transform(c, n) discard c.contSyms.pop() - else: + else: result = transform(c, n) proc transformWhile(c: PTransf; n: PNode): PTransNode = @@ -273,27 +273,27 @@ proc transformBreak(c: PTransf, n: PNode): PTransNode = result = transformSons(c, n) result[0] = newSymNode(labl).PTransNode -proc unpackTuple(c: PTransf, n: PNode, father: PTransNode) = +proc unpackTuple(c: PTransf, n: PNode, father: PTransNode) = # XXX: BUG: what if `n` is an expression with side-effects? - for i in countup(0, sonsLen(c.transCon.forStmt) - 3): - add(father, newAsgnStmt(c, c.transCon.forStmt.sons[i], + for i in countup(0, sonsLen(c.transCon.forStmt) - 3): + add(father, newAsgnStmt(c, c.transCon.forStmt.sons[i], transform(c, newTupleAccess(n, i)))) -proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode = +proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode = case n.kind - of nkSym: + of nkSym: result = transformSym(c, n) - of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: + of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: # nothing to be done for leaves: result = PTransNode(n) of nkVarSection, nkLetSection: result = transformVarSection(c, n) else: result = newTransNode(n) - for i in countup(0, sonsLen(n)-1): + for i in countup(0, sonsLen(n)-1): result[i] = introduceNewLocalVars(c, n.sons[i]) -proc transformYield(c: PTransf, n: PNode): PTransNode = +proc transformYield(c: PTransf, n: PNode): PTransNode = result = newTransNode(nkStmtList, n.info, 0) var e = n.sons[0] # c.transCon.forStmt.len == 3 means that there is one for loop variable @@ -301,21 +301,21 @@ proc transformYield(c: PTransf, n: PNode): PTransNode = if skipTypes(e.typ, {tyGenericInst}).kind == tyTuple and c.transCon.forStmt.len != 3: e = skipConv(e) - if e.kind == nkPar: - for i in countup(0, sonsLen(e) - 1): - add(result, newAsgnStmt(c, c.transCon.forStmt.sons[i], + if e.kind == nkPar: + for i in countup(0, sonsLen(e) - 1): + add(result, newAsgnStmt(c, c.transCon.forStmt.sons[i], transform(c, e.sons[i]))) - else: + else: unpackTuple(c, e, result) - else: + else: var x = transform(c, e) add(result, newAsgnStmt(c, c.transCon.forStmt.sons[0], x)) - + inc(c.transCon.yieldStmts) if c.transCon.yieldStmts <= 1: # common case add(result, c.transCon.forLoopBody) - else: + else: # we need to introduce new local variables: add(result, introduceNewLocalVars(c, c.transCon.forLoopBody.PNode)) @@ -340,25 +340,25 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode = if n.sons[0].kind == a or n.sons[0].kind == b: # addr ( deref ( x )) --> x result = PTransNode(n.sons[0].sons[0]) - -proc transformConv(c: PTransf, n: PNode): PTransNode = + +proc transformConv(c: PTransf, n: PNode): PTransNode = # numeric types need range checks: var dest = skipTypes(n.typ, abstractVarRange) var source = skipTypes(n.sons[1].typ, abstractVarRange) case dest.kind - of tyInt..tyInt64, tyEnum, tyChar, tyBool, tyUInt8..tyUInt32: + of tyInt..tyInt64, tyEnum, tyChar, tyBool, tyUInt8..tyUInt32: # we don't include uint and uint64 here as these are no ordinal types ;-) if not isOrdinalType(source): # float -> int conversions. ugh. result = transformSons(c, n) elif firstOrd(n.typ) <= firstOrd(n.sons[1].typ) and - lastOrd(n.sons[1].typ) <= lastOrd(n.typ): + lastOrd(n.sons[1].typ) <= lastOrd(n.typ): # BUGFIX: simply leave n as it is; we need a nkConv node, # but no range check: result = transformSons(c, n) - else: + else: # generate a range check: - if dest.kind == tyInt64 or source.kind == tyInt64: + if dest.kind == tyInt64 or source.kind == tyInt64: result = newTransNode(nkChckRange64, n, 3) else: result = newTransNode(nkChckRange, n, 3) @@ -368,7 +368,7 @@ proc transformConv(c: PTransf, n: PNode): PTransNode = result[2] = newIntTypeNode(nkIntLit, lastOrd(dest), source).PTransNode of tyFloat..tyFloat128: # XXX int64 -> float conversion? - if skipTypes(n.typ, abstractVar).kind == tyRange: + if skipTypes(n.typ, abstractVar).kind == tyRange: result = newTransNode(nkChckRangeF, n, 3) dest = skipTypes(n.typ, abstractVar) result[0] = transform(c, n.sons[1]) @@ -378,81 +378,81 @@ proc transformConv(c: PTransf, n: PNode): PTransNode = result = transformSons(c, n) of tyOpenArray, tyVarargs: result = transform(c, n.sons[1]) - of tyCString: - if source.kind == tyString: + of tyCString: + if source.kind == tyString: result = newTransNode(nkStringToCString, n, 1) result[0] = transform(c, n.sons[1]) else: result = transformSons(c, n) - of tyString: - if source.kind == tyCString: + of tyString: + if source.kind == tyCString: result = newTransNode(nkCStringToString, n, 1) result[0] = transform(c, n.sons[1]) else: result = transformSons(c, n) - of tyRef, tyPtr: + of tyRef, tyPtr: dest = skipTypes(dest, abstractPtrs) source = skipTypes(source, abstractPtrs) - if source.kind == tyObject: + if source.kind == tyObject: var diff = inheritanceDiff(dest, source) - if diff < 0: + if diff < 0: result = newTransNode(nkObjUpConv, n, 1) result[0] = transform(c, n.sons[1]) - elif diff > 0: + elif diff > 0: result = newTransNode(nkObjDownConv, n, 1) result[0] = transform(c, n.sons[1]) - else: + else: result = transform(c, n.sons[1]) else: result = transformSons(c, n) - of tyObject: + of tyObject: var diff = inheritanceDiff(dest, source) - if diff < 0: + if diff < 0: result = newTransNode(nkObjUpConv, n, 1) result[0] = transform(c, n.sons[1]) - elif diff > 0: + elif diff > 0: result = newTransNode(nkObjDownConv, n, 1) result[0] = transform(c, n.sons[1]) - else: + else: result = transform(c, n.sons[1]) of tyGenericParam, tyOrdinal: result = transform(c, n.sons[1]) # happens sometimes for generated assignments, etc. - else: + else: result = transformSons(c, n) - -type - TPutArgInto = enum + +type + TPutArgInto = enum paDirectMapping, paFastAsgn, paVarAsgn -proc putArgInto(arg: PNode, formal: PType): TPutArgInto = +proc putArgInto(arg: PNode, formal: PType): TPutArgInto = # This analyses how to treat the mapping "formal <-> arg" in an # inline context. if skipTypes(formal, abstractInst).kind in {tyOpenArray, tyVarargs}: return paDirectMapping # XXX really correct? # what if ``arg`` has side-effects? case arg.kind - of nkEmpty..nkNilLit: + of nkEmpty..nkNilLit: result = paDirectMapping - of nkPar, nkCurly, nkBracket: + of nkPar, nkCurly, nkBracket: result = paFastAsgn - for i in countup(0, sonsLen(arg) - 1): - if putArgInto(arg.sons[i], formal) != paDirectMapping: return + for i in countup(0, sonsLen(arg) - 1): + if putArgInto(arg.sons[i], formal) != paDirectMapping: return result = paDirectMapping - else: + else: if skipTypes(formal, abstractInst).kind == tyVar: result = paVarAsgn else: result = paFastAsgn - + proc findWrongOwners(c: PTransf, n: PNode) = if n.kind == nkVarSection: let x = n.sons[0].sons[0] if x.kind == nkSym and x.sym.owner != getCurrOwner(c): - internalError(x.info, "bah " & x.sym.name.s & " " & + internalError(x.info, "bah " & x.sym.name.s & " " & x.sym.owner.name.s & " " & getCurrOwner(c).name.s) else: for i in 0 .. <safeLen(n): findWrongOwners(c, n.sons[i]) -proc transformFor(c: PTransf, n: PNode): PTransNode = +proc transformFor(c: PTransf, n: PNode): PTransNode = # generate access statements for the parameters (unless they are constant) # put mapping from formal parameters to actual parameters if n.kind != nkForStmt: internalError(n.info, "transformFor") @@ -466,26 +466,26 @@ proc transformFor(c: PTransf, n: PNode): PTransNode = result[0] = newSymNode(labl).PTransNode if call.typ.kind != tyIter and - (call.kind notin nkCallKinds or call.sons[0].kind != nkSym or + (call.kind notin nkCallKinds or call.sons[0].kind != nkSym or call.sons[0].sym.kind != skIterator): n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode result[1] = lambdalifting.liftForLoop(n).PTransNode discard c.breakSyms.pop return result - + #echo "transforming: ", renderTree(n) var stmtList = newTransNode(nkStmtList, n.info, 0) - + var loopBody = transformLoopBody(c, n.sons[length-1]) result[1] = stmtList discard c.breakSyms.pop var v = newNodeI(nkVarSection, n.info) - for i in countup(0, length - 3): + for i in countup(0, length - 3): addVar(v, copyTree(n.sons[i])) # declare new vars add(stmtList, v.PTransNode) - + # Bugfix: inlined locals belong to the invoking routine, not to the invoked # iterator! let iter = call.sons[0].sym @@ -496,9 +496,9 @@ proc transformFor(c: PTransf, n: PNode): PTransNode = if iter.kind != skIterator: return result # generate access statements for the parameters (unless they are constant) pushTransCon(c, newC) - for i in countup(1, sonsLen(call) - 1): + for i in countup(1, sonsLen(call) - 1): var arg = transform(c, call.sons[i]).PNode - var formal = skipTypes(iter.typ, abstractInst).n.sons[i].sym + var formal = skipTypes(iter.typ, abstractInst).n.sons[i].sym if arg.typ.kind == tyIter: continue case putArgInto(arg, formal.typ) of paDirectMapping: @@ -527,20 +527,20 @@ proc transformFor(c: PTransf, n: PNode): PTransNode = popInfoContext() popTransCon(c) # echo "transformed: ", stmtList.PNode.renderTree - -proc getMagicOp(call: PNode): TMagic = + +proc getMagicOp(call: PNode): TMagic = if call.sons[0].kind == nkSym and - call.sons[0].sym.kind in {skProc, skMethod, skConverter}: + call.sons[0].sym.kind in {skProc, skMethod, skConverter}: result = call.sons[0].sym.magic else: result = mNone -proc transformCase(c: PTransf, n: PNode): PTransNode = +proc transformCase(c: PTransf, n: PNode): PTransNode = # removes `elif` branches of a case stmt # adds ``else: nil`` if needed for the code generator result = newTransNode(nkCaseStmt, n, 0) var ifs = PTransNode(nil) - for i in 0 .. sonsLen(n)-1: + for i in 0 .. sonsLen(n)-1: var it = n.sons[i] var e = transform(c, it) case it.kind @@ -564,8 +564,8 @@ proc transformCase(c: PTransf, n: PNode): PTransNode = var elseBranch = newTransNode(nkElse, n.info, 1) elseBranch[0] = newTransNode(nkNilLit, n.info, 0) add(result, elseBranch) - -proc transformArrayAccess(c: PTransf, n: PNode): PTransNode = + +proc transformArrayAccess(c: PTransf, n: PNode): PTransNode = # XXX this is really bad; transf should use a proper AST visitor if n.sons[0].kind == nkSym and n.sons[0].sym.kind == skType: result = n.PTransNode @@ -573,45 +573,45 @@ proc transformArrayAccess(c: PTransf, n: PNode): PTransNode = result = newTransNode(n) for i in 0 .. < n.len: result[i] = transform(c, skipConv(n.sons[i])) - -proc getMergeOp(n: PNode): PSym = + +proc getMergeOp(n: PNode): PSym = case n.kind - of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix, - nkCallStrLit: + of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix, + nkCallStrLit: if (n.sons[0].kind == nkSym) and (n.sons[0].sym.kind == skProc) and - (sfMerge in n.sons[0].sym.flags): + (sfMerge in n.sons[0].sym.flags): result = n.sons[0].sym else: discard -proc flattenTreeAux(d, a: PNode, op: PSym) = +proc flattenTreeAux(d, a: PNode, op: PSym) = let op2 = getMergeOp(a) if op2 != nil and - (op2.id == op.id or op.magic != mNone and op2.magic == op.magic): + (op2.id == op.id or op.magic != mNone and op2.magic == op.magic): for i in countup(1, sonsLen(a)-1): flattenTreeAux(d, a.sons[i], op) - else: + else: addSon(d, copyTree(a)) - -proc flattenTree(root: PNode): PNode = + +proc flattenTree(root: PNode): PNode = let op = getMergeOp(root) - if op != nil: + if op != nil: result = copyNode(root) addSon(result, copyTree(root.sons[0])) flattenTreeAux(result, root, op) - else: + else: result = root -proc transformCall(c: PTransf, n: PNode): PTransNode = +proc transformCall(c: PTransf, n: PNode): PTransNode = var n = flattenTree(n) let op = getMergeOp(n) let magic = getMagic(n) - if op != nil and op.magic != mNone and n.len >= 3: + if op != nil and op.magic != mNone and n.len >= 3: result = newTransNode(nkCall, n, 0) add(result, transform(c, n.sons[0])) var j = 1 - while j < sonsLen(n): + while j < sonsLen(n): var a = transform(c, n.sons[j]).PNode inc(j) - if isConstExpr(a): + if isConstExpr(a): while (j < sonsLen(n)): let b = transform(c, n.sons[j]).PNode if not isConstExpr(b): break @@ -640,7 +640,7 @@ proc transformCall(c: PTransf, n: PNode): PTransNode = proc dontInlineConstant(orig, cnst: PNode): bool {.inline.} = # symbols that expand to a complex constant (array, etc.) should not be # inlined, unless it's the empty array: - result = orig.kind == nkSym and cnst.kind in {nkCurly, nkPar, nkBracket} and + result = orig.kind == nkSym and cnst.kind in {nkCurly, nkPar, nkBracket} and cnst.len != 0 proc commonOptimizations*(c: PSym, n: PNode): PNode = @@ -673,11 +673,11 @@ proc commonOptimizations*(c: PSym, n: PNode): PNode = else: result = n -proc transform(c: PTransf, n: PNode): PTransNode = +proc transform(c: PTransf, n: PNode): PTransNode = case n.kind - of nkSym: + of nkSym: result = transformSym(c, n) - of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: + of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: # nothing to be done for leaves: result = PTransNode(n) of nkBracketExpr: result = transformArrayAccess(c, n) @@ -702,7 +702,7 @@ proc transform(c: PTransf, n: PNode): PTransNode = n.sons[bodyPos] = PNode(transform(c, s.getBody)) if n.kind == nkMethodDef: methodDef(s, false) result = PTransNode(n) - of nkForStmt: + of nkForStmt: result = transformFor(c, n) of nkParForStmt: result = transformSons(c, n) @@ -713,14 +713,14 @@ proc transform(c: PTransf, n: PNode): PTransNode = add(result, PTransNode(newSymNode(labl))) of nkBreakStmt: result = transformBreak(c, n) of nkWhileStmt: result = transformWhile(c, n) - of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix, - nkCallStrLit: + of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix, + nkCallStrLit: result = transformCall(c, n) - of nkAddr, nkHiddenAddr: + of nkAddr, nkHiddenAddr: result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref) - of nkDerefExpr, nkHiddenDeref: + of nkDerefExpr, nkHiddenDeref: result = transformAddrDeref(c, n, nkAddr, nkHiddenAddr) - of nkHiddenStdConv, nkHiddenSubConv, nkConv: + of nkHiddenStdConv, nkHiddenSubConv, nkConv: result = transformConv(c, n) of nkDiscardStmt: result = PTransNode(n) @@ -730,7 +730,7 @@ proc transform(c: PTransf, n: PNode): PTransNode = # ensure that e.g. discard "some comment" gets optimized away # completely: result = PTransNode(newNode(nkCommentStmt)) - of nkCommentStmt, nkTemplateDef: + of nkCommentStmt, nkTemplateDef: return n.PTransNode of nkConstSection: # do not replace ``const c = 3`` with ``const 3 = 3`` @@ -744,10 +744,10 @@ proc transform(c: PTransf, n: PNode): PTransNode = result = transformVarSection(c, n) else: result = transformSons(c, n) - of nkYieldStmt: + of nkYieldStmt: if c.inlining > 0: result = transformYield(c, n) - else: + else: result = transformSons(c, n) of nkBlockStmt, nkBlockExpr: result = transformBlock(c, n) @@ -764,7 +764,7 @@ proc transform(c: PTransf, n: PNode): PTransNode = if cnst != nil and not dontInlineConstant(n, cnst): result = PTransNode(cnst) # do not miss an optimization -proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode = +proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode = # Note: For interactive mode we cannot call 'passes.skipCodegen' and skip # this step! We have to rely that the semantic pass transforms too errornous # nodes into an empty node. @@ -774,7 +774,7 @@ proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode = popTransCon(c) incl(result.flags, nfTransf) -proc openTransf(module: PSym, filename: string): PTransf = +proc openTransf(module: PSym, filename: string): PTransf = new(result) result.contSyms = @[] result.breakSyms = @[] diff --git a/compiler/types.nim b/compiler/types.nim index 5c3be7553..5f506f10f 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -9,20 +9,20 @@ # this module contains routines for accessing and iterating over types -import +import intsets, ast, astalgo, trees, msgs, strutils, platform, renderer proc firstOrd*(t: PType): BiggestInt proc lastOrd*(t: PType): BiggestInt proc lengthOrd*(t: PType): BiggestInt -type +type TPreferedDesc* = enum preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string proc base*(t: PType): PType # ------------------- type iterator: ---------------------------------------- -type +type TTypeIter* = proc (t: PType, closure: RootRef): bool {.nimcall.} # true if iteration should stop TTypeMutator* = proc (t: PType, closure: RootRef): PType {.nimcall.} # copy t and mutate it TTypePredicate* = proc (t: PType): bool {.nimcall.} @@ -32,7 +32,7 @@ proc iterOverType*(t: PType, iter: TTypeIter, closure: RootRef): bool proc mutateType*(t: PType, iter: TTypeMutator, closure: RootRef): PType # Returns result of `iter`. -type +type TParamsEquality* = enum # they are equal, but their # identifiers or their return # type differ (i.e. they cannot be @@ -59,7 +59,7 @@ const abstractInst* = {tyGenericInst, tyDistinct, tyConst, tyMutable, tyOrdinal, tyTypeDesc} - skipPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyConst, tyMutable, + skipPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyConst, tyMutable, tyTypeDesc} typedescPtrs* = abstractPtrs + {tyTypeDesc} typedescInst* = abstractInst + {tyTypeDesc} @@ -75,9 +75,9 @@ proc getSize*(typ: PType): BiggestInt proc isPureObject*(typ: PType): bool proc invalidGenericInst*(f: PType): bool # for debugging -type - TTypeFieldResult* = enum - frNone, # type has no object type field +type + TTypeFieldResult* = enum + frNone, # type has no object type field frHeader, # type has an object type field only in the header frEmbedded # type has an object type field somewhere embedded @@ -86,15 +86,15 @@ proc analyseObjectWithTypeField*(t: PType): TTypeFieldResult # made or intializing of the type field suffices or if there is no type field # at all in this type. -proc invalidGenericInst(f: PType): bool = +proc invalidGenericInst(f: PType): bool = result = f.kind == tyGenericInst and lastSon(f) == nil -proc isPureObject(typ: PType): bool = +proc isPureObject(typ: PType): bool = var t = typ while t.kind == tyObject and t.sons[0] != nil: t = t.sons[0] result = t.sym != nil and sfPure in t.sym.flags -proc getOrdValue(n: PNode): BiggestInt = +proc getOrdValue(n: PNode): BiggestInt = case n.kind of nkCharLit..nkUInt64Lit: result = n.intVal of nkNilLit: result = 0 @@ -109,21 +109,21 @@ proc isIntLit*(t: PType): bool {.inline.} = proc isFloatLit*(t: PType): bool {.inline.} = result = t.kind == tyFloat and t.n != nil and t.n.kind == nkFloatLit -proc isCompatibleToCString(a: PType): bool = - if a.kind == tyArray: +proc isCompatibleToCString(a: PType): bool = + if a.kind == tyArray: if (firstOrd(a.sons[0]) == 0) and - (skipTypes(a.sons[0], {tyRange, tyConst, - tyMutable, tyGenericInst}).kind in + (skipTypes(a.sons[0], {tyRange, tyConst, + tyMutable, tyGenericInst}).kind in {tyInt..tyInt64, tyUInt..tyUInt64}) and - (a.sons[1].kind == tyChar): + (a.sons[1].kind == tyChar): result = true - -proc getProcHeader*(sym: PSym; prefer: TPreferedDesc = preferName): string = + +proc getProcHeader*(sym: PSym; prefer: TPreferedDesc = preferName): string = result = sym.owner.name.s & '.' & sym.name.s & '(' var n = sym.typ.n - for i in countup(1, sonsLen(n) - 1): + for i in countup(1, sonsLen(n) - 1): var p = n.sons[i] - if p.kind == nkSym: + if p.kind == nkSym: add(result, p.sym.name.s) add(result, ": ") add(result, typeToString(p.sym.typ, prefer)) @@ -134,7 +134,7 @@ proc getProcHeader*(sym: PSym; prefer: TPreferedDesc = preferName): string = if n.sons[0].typ != nil: result.add(": " & typeToString(n.sons[0].typ, prefer)) -proc elemType*(t: PType): PType = +proc elemType*(t: PType): PType = assert(t != nil) case t.kind of tyGenericInst, tyDistinct: result = elemType(lastSon(t)) @@ -142,10 +142,10 @@ proc elemType*(t: PType): PType = else: result = t.lastSon assert(result != nil) -proc skipGeneric(t: PType): PType = +proc skipGeneric(t: PType): PType = result = t while result.kind == tyGenericInst: result = lastSon(result) - + proc isOrdinalType(t: PType): bool = assert(t != nil) # caution: uint, uint64 are no ordinal types! @@ -153,134 +153,134 @@ proc isOrdinalType(t: PType): bool = (t.kind in {tyRange, tyOrdinal, tyConst, tyMutable, tyGenericInst}) and isOrdinalType(t.sons[0]) -proc enumHasHoles(t: PType): bool = +proc enumHasHoles(t: PType): bool = var b = t while b.kind in {tyConst, tyMutable, tyRange, tyGenericInst}: b = b.sons[0] result = b.kind == tyEnum and tfEnumHasHoles in b.flags -proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter, +proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter, closure: RootRef): bool -proc iterOverNode(marker: var IntSet, n: PNode, iter: TTypeIter, - closure: RootRef): bool = - if n != nil: +proc iterOverNode(marker: var IntSet, n: PNode, iter: TTypeIter, + closure: RootRef): bool = + if n != nil: case n.kind - of nkNone..nkNilLit: + of nkNone..nkNilLit: # a leaf result = iterOverTypeAux(marker, n.typ, iter, closure) - else: - for i in countup(0, sonsLen(n) - 1): + else: + for i in countup(0, sonsLen(n) - 1): result = iterOverNode(marker, n.sons[i], iter, closure) - if result: return - -proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter, - closure: RootRef): bool = + if result: return + +proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter, + closure: RootRef): bool = result = false - if t == nil: return + if t == nil: return result = iter(t, closure) - if result: return - if not containsOrIncl(marker, t.id): + if result: return + if not containsOrIncl(marker, t.id): case t.kind - of tyGenericInst, tyGenericBody: + of tyGenericInst, tyGenericBody: result = iterOverTypeAux(marker, lastSon(t), iter, closure) - else: - for i in countup(0, sonsLen(t) - 1): + else: + for i in countup(0, sonsLen(t) - 1): result = iterOverTypeAux(marker, t.sons[i], iter, closure) - if result: return + if result: return if t.n != nil: result = iterOverNode(marker, t.n, iter, closure) - -proc iterOverType(t: PType, iter: TTypeIter, closure: RootRef): bool = + +proc iterOverType(t: PType, iter: TTypeIter, closure: RootRef): bool = var marker = initIntSet() result = iterOverTypeAux(marker, t, iter, closure) -proc searchTypeForAux(t: PType, predicate: TTypePredicate, +proc searchTypeForAux(t: PType, predicate: TTypePredicate, marker: var IntSet): bool -proc searchTypeNodeForAux(n: PNode, p: TTypePredicate, - marker: var IntSet): bool = +proc searchTypeNodeForAux(n: PNode, p: TTypePredicate, + marker: var IntSet): bool = result = false case n.kind - of nkRecList: - for i in countup(0, sonsLen(n) - 1): + of nkRecList: + for i in countup(0, sonsLen(n) - 1): result = searchTypeNodeForAux(n.sons[i], p, marker) - if result: return - of nkRecCase: + if result: return + of nkRecCase: assert(n.sons[0].kind == nkSym) result = searchTypeNodeForAux(n.sons[0], p, marker) - if result: return - for i in countup(1, sonsLen(n) - 1): + if result: return + for i in countup(1, sonsLen(n) - 1): case n.sons[i].kind - of nkOfBranch, nkElse: + of nkOfBranch, nkElse: result = searchTypeNodeForAux(lastSon(n.sons[i]), p, marker) - if result: return + if result: return else: internalError("searchTypeNodeForAux(record case branch)") - of nkSym: + of nkSym: result = searchTypeForAux(n.sym.typ, p, marker) else: internalError(n.info, "searchTypeNodeForAux()") - -proc searchTypeForAux(t: PType, predicate: TTypePredicate, - marker: var IntSet): bool = + +proc searchTypeForAux(t: PType, predicate: TTypePredicate, + marker: var IntSet): bool = # iterates over VALUE types! result = false - if t == nil: return - if containsOrIncl(marker, t.id): return + if t == nil: return + if containsOrIncl(marker, t.id): return result = predicate(t) - if result: return + if result: return case t.kind - of tyObject: + of tyObject: result = searchTypeForAux(t.sons[0], predicate, marker) if not result: result = searchTypeNodeForAux(t.n, predicate, marker) - of tyGenericInst, tyDistinct: + of tyGenericInst, tyDistinct: result = searchTypeForAux(lastSon(t), predicate, marker) - of tyArray, tyArrayConstr, tySet, tyTuple: - for i in countup(0, sonsLen(t) - 1): + of tyArray, tyArrayConstr, tySet, tyTuple: + for i in countup(0, sonsLen(t) - 1): result = searchTypeForAux(t.sons[i], predicate, marker) - if result: return - else: + if result: return + else: discard -proc searchTypeFor(t: PType, predicate: TTypePredicate): bool = +proc searchTypeFor(t: PType, predicate: TTypePredicate): bool = var marker = initIntSet() result = searchTypeForAux(t, predicate, marker) -proc isObjectPredicate(t: PType): bool = +proc isObjectPredicate(t: PType): bool = result = t.kind == tyObject -proc containsObject(t: PType): bool = +proc containsObject(t: PType): bool = result = searchTypeFor(t, isObjectPredicate) -proc isObjectWithTypeFieldPredicate(t: PType): bool = +proc isObjectWithTypeFieldPredicate(t: PType): bool = result = t.kind == tyObject and t.sons[0] == nil and not (t.sym != nil and {sfPure, sfInfixCall} * t.sym.flags != {}) and tfFinal notin t.flags -proc analyseObjectWithTypeFieldAux(t: PType, - marker: var IntSet): TTypeFieldResult = +proc analyseObjectWithTypeFieldAux(t: PType, + marker: var IntSet): TTypeFieldResult = var res: TTypeFieldResult result = frNone - if t == nil: return + if t == nil: return case t.kind - of tyObject: - if (t.n != nil): - if searchTypeNodeForAux(t.n, isObjectWithTypeFieldPredicate, marker): + of tyObject: + if (t.n != nil): + if searchTypeNodeForAux(t.n, isObjectWithTypeFieldPredicate, marker): return frEmbedded - for i in countup(0, sonsLen(t) - 1): + for i in countup(0, sonsLen(t) - 1): res = analyseObjectWithTypeFieldAux(t.sons[i], marker) - if res == frEmbedded: + if res == frEmbedded: return frEmbedded if res == frHeader: result = frHeader - if result == frNone: + if result == frNone: if isObjectWithTypeFieldPredicate(t): result = frHeader - of tyGenericInst, tyDistinct, tyConst, tyMutable: + of tyGenericInst, tyDistinct, tyConst, tyMutable: result = analyseObjectWithTypeFieldAux(lastSon(t), marker) - of tyArray, tyArrayConstr, tyTuple: - for i in countup(0, sonsLen(t) - 1): + of tyArray, tyArrayConstr, tyTuple: + for i in countup(0, sonsLen(t) - 1): res = analyseObjectWithTypeFieldAux(t.sons[i], marker) - if res != frNone: + if res != frNone: return frEmbedded - else: + else: discard -proc analyseObjectWithTypeField(t: PType): TTypeFieldResult = +proc analyseObjectWithTypeField(t: PType): TTypeFieldResult = var marker = initIntSet() result = analyseObjectWithTypeFieldAux(t, marker) @@ -288,7 +288,7 @@ proc isGCRef(t: PType): bool = result = t.kind in GcTypeKinds or (t.kind == tyProc and t.callConv == ccClosure) -proc containsGarbageCollectedRef(typ: PType): bool = +proc containsGarbageCollectedRef(typ: PType): bool = # returns true if typ contains a reference, sequence or string (all the # things that are garbage-collected) result = searchTypeFor(typ, isGCRef) @@ -296,47 +296,47 @@ proc containsGarbageCollectedRef(typ: PType): bool = proc isTyRef(t: PType): bool = result = t.kind == tyRef or (t.kind == tyProc and t.callConv == ccClosure) -proc containsTyRef*(typ: PType): bool = +proc containsTyRef*(typ: PType): bool = # returns true if typ contains a 'ref' result = searchTypeFor(typ, isTyRef) -proc isHiddenPointer(t: PType): bool = +proc isHiddenPointer(t: PType): bool = result = t.kind in {tyString, tySequence} -proc containsHiddenPointer(typ: PType): bool = +proc containsHiddenPointer(typ: PType): bool = # returns true if typ contains a string, table or sequence (all the things # that need to be copied deeply) result = searchTypeFor(typ, isHiddenPointer) proc canFormAcycleAux(marker: var IntSet, typ: PType, startId: int): bool -proc canFormAcycleNode(marker: var IntSet, n: PNode, startId: int): bool = +proc canFormAcycleNode(marker: var IntSet, n: PNode, startId: int): bool = result = false - if n != nil: + if n != nil: result = canFormAcycleAux(marker, n.typ, startId) - if not result: + if not result: case n.kind - of nkNone..nkNilLit: + of nkNone..nkNilLit: discard - else: - for i in countup(0, sonsLen(n) - 1): + else: + for i in countup(0, sonsLen(n) - 1): result = canFormAcycleNode(marker, n.sons[i], startId) - if result: return - -proc canFormAcycleAux(marker: var IntSet, typ: PType, startId: int): bool = + if result: return + +proc canFormAcycleAux(marker: var IntSet, typ: PType, startId: int): bool = result = false - if typ == nil: return - if tfAcyclic in typ.flags: return + if typ == nil: return + if tfAcyclic in typ.flags: return var t = skipTypes(typ, abstractInst-{tyTypeDesc}) - if tfAcyclic in t.flags: return + if tfAcyclic in t.flags: return case t.kind of tyTuple, tyObject, tyRef, tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs: - if not containsOrIncl(marker, t.id): - for i in countup(0, sonsLen(t) - 1): + if not containsOrIncl(marker, t.id): + for i in countup(0, sonsLen(t) - 1): result = canFormAcycleAux(marker, t.sons[i], startId) - if result: return + if result: return if t.n != nil: result = canFormAcycleNode(marker, t.n, startId) - else: + else: result = t.id == startId # Inheritance can introduce cyclic types, however this is not relevant # as the type that is passed to 'new' is statically known! @@ -351,29 +351,29 @@ proc canFormAcycle(typ: PType): bool = var marker = initIntSet() result = canFormAcycleAux(marker, typ, typ.id) -proc mutateTypeAux(marker: var IntSet, t: PType, iter: TTypeMutator, +proc mutateTypeAux(marker: var IntSet, t: PType, iter: TTypeMutator, closure: RootRef): PType -proc mutateNode(marker: var IntSet, n: PNode, iter: TTypeMutator, - closure: RootRef): PNode = +proc mutateNode(marker: var IntSet, n: PNode, iter: TTypeMutator, + closure: RootRef): PNode = result = nil - if n != nil: + if n != nil: result = copyNode(n) result.typ = mutateTypeAux(marker, n.typ, iter, closure) case n.kind - of nkNone..nkNilLit: + of nkNone..nkNilLit: # a leaf discard - else: - for i in countup(0, sonsLen(n) - 1): + else: + for i in countup(0, sonsLen(n) - 1): addSon(result, mutateNode(marker, n.sons[i], iter, closure)) - -proc mutateTypeAux(marker: var IntSet, t: PType, iter: TTypeMutator, - closure: RootRef): PType = + +proc mutateTypeAux(marker: var IntSet, t: PType, iter: TTypeMutator, + closure: RootRef): PType = result = nil - if t == nil: return + if t == nil: return result = iter(t, closure) - if not containsOrIncl(marker, t.id): - for i in countup(0, sonsLen(t) - 1): + if not containsOrIncl(marker, t.id): + for i in countup(0, sonsLen(t) - 1): result.sons[i] = mutateTypeAux(marker, result.sons[i], iter, closure) if t.n != nil: result.n = mutateNode(marker, t.n, iter, closure) assert(result != nil) @@ -393,7 +393,7 @@ proc rangeToStr(n: PNode): string = assert(n.kind == nkRange) result = valueToString(n.sons[0]) & ".." & valueToString(n.sons[1]) -const +const typeToStr: array[TTypeKind, string] = ["None", "bool", "Char", "empty", "Array Constructor [$1]", "nil", "expr", "stmt", "typeDesc", "GenericInvocation", "GenericBody", "GenericInst", "GenericParam", @@ -414,7 +414,7 @@ const preferToResolveSymbols = {preferName, preferModuleInfo, preferGenericArg} proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = var t = typ result = "" - if t == nil: return + if t == nil: return if prefer in preferToResolveSymbols and t.sym != nil and sfAnon notin t.sym.flags: if t.kind == tyInt and isIntLit(t): @@ -484,47 +484,51 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = result = "expr" of tyFromExpr, tyFieldAccessor: result = renderTree(t.n) - of tyArray: - if t.sons[0].kind == tyRange: + of tyArray: + if t.sons[0].kind == tyRange: result = "array[" & rangeToStr(t.sons[0].n) & ", " & typeToString(t.sons[1]) & ']' - else: + else: result = "array[" & typeToString(t.sons[0]) & ", " & typeToString(t.sons[1]) & ']' - of tyArrayConstr: + of tyArrayConstr: result = "Array constructor[" & rangeToStr(t.sons[0].n) & ", " & typeToString(t.sons[1]) & ']' - of tySequence: + of tySequence: result = "seq[" & typeToString(t.sons[0]) & ']' - of tyOrdinal: + of tyOrdinal: result = "ordinal[" & typeToString(t.sons[0]) & ']' - of tySet: + of tySet: result = "set[" & typeToString(t.sons[0]) & ']' - of tyOpenArray: + of tyOpenArray: result = "openarray[" & typeToString(t.sons[0]) & ']' of tyDistinct: result = "distinct " & typeToString(t.sons[0], if prefer == preferModuleInfo: preferModuleInfo else: preferName) - of tyTuple: + of tyTuple: # we iterate over t.sons here, because t.n may be nil - result = "tuple[" - if t.n != nil: + if t.n != nil: + result = "tuple[" assert(sonsLen(t.n) == sonsLen(t)) - for i in countup(0, sonsLen(t.n) - 1): + for i in countup(0, sonsLen(t.n) - 1): assert(t.n.sons[i].kind == nkSym) add(result, t.n.sons[i].sym.name.s & ": " & typeToString(t.sons[i])) if i < sonsLen(t.n) - 1: add(result, ", ") - else: - for i in countup(0, sonsLen(t) - 1): + add(result, ']') + elif sonsLen(t) == 0: + result = "tuple[]" + else: + result = "(" + for i in countup(0, sonsLen(t) - 1): add(result, typeToString(t.sons[i])) if i < sonsLen(t) - 1: add(result, ", ") - add(result, ']') - of tyPtr, tyRef, tyVar, tyMutable, tyConst: + add(result, ')') + of tyPtr, tyRef, tyVar, tyMutable, tyConst: result = typeToStr[t.kind] if t.len >= 2: setLen(result, result.len-1) result.add '[' - for i in countup(0, sonsLen(t) - 1): + for i in countup(0, sonsLen(t) - 1): add(result, typeToString(t.sons[i])) if i < sonsLen(t) - 1: add(result, ", ") result.add ']' @@ -536,7 +540,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = result.add("(" & typeToString(t.sons[0]) & ")") of tyProc: result = if tfIterator in t.flags: "iterator (" else: "proc (" - for i in countup(1, sonsLen(t) - 1): + for i in countup(1, sonsLen(t) - 1): add(result, typeToString(t.sons[i])) if i < sonsLen(t) - 1: add(result, ", ") add(result, ')') @@ -554,29 +558,29 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = if len(prag) != 0: add(result, "{." & prag & ".}") of tyVarargs, tyIter: result = typeToStr[t.kind] % typeToString(t.sons[0]) - else: + else: result = typeToStr[t.kind] if tfShared in t.flags: result = "shared " & result if tfNotNil in t.flags: result.add(" not nil") -proc resultType(t: PType): PType = +proc resultType(t: PType): PType = assert(t.kind == tyProc) result = t.sons[0] # nil is allowed - -proc base(t: PType): PType = + +proc base(t: PType): PType = result = t.sons[0] -proc firstOrd(t: PType): BiggestInt = +proc firstOrd(t: PType): BiggestInt = case t.kind of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyProxy: result = 0 of tySet, tyVar: result = firstOrd(t.sons[0]) of tyArray, tyArrayConstr: result = firstOrd(t.sons[0]) - of tyRange: + of tyRange: assert(t.n != nil) # range directly given: assert(t.n.kind == nkRange) result = getOrdValue(t.n.sons[0]) - of tyInt: + of tyInt: if platform.intSize == 4: result = - (2147483646) - 2 else: result = 0x8000000000000000'i64 of tyInt8: result = - 128 @@ -584,11 +588,11 @@ proc firstOrd(t: PType): BiggestInt = of tyInt32: result = - 2147483646 - 2 of tyInt64: result = 0x8000000000000000'i64 of tyUInt..tyUInt64: result = 0 - of tyEnum: + 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: + else: assert(t.n.sons[0].kind == nkSym) result = t.n.sons[0].sym.position of tyGenericInst, tyDistinct, tyConst, tyMutable, tyTypeDesc, tyFieldAccessor: @@ -600,31 +604,31 @@ proc firstOrd(t: PType): BiggestInt = internalError("invalid kind for first(" & $t.kind & ')') result = 0 -proc lastOrd(t: PType): BiggestInt = +proc lastOrd(t: PType): BiggestInt = case t.kind of tyBool: result = 1 of tyChar: result = 255 of tySet, tyVar: result = lastOrd(t.sons[0]) of tyArray, tyArrayConstr: result = lastOrd(t.sons[0]) - of tyRange: + of tyRange: assert(t.n != nil) # range directly given: assert(t.n.kind == nkRange) result = getOrdValue(t.n.sons[1]) - of tyInt: + of tyInt: if platform.intSize == 4: result = 0x7FFFFFFF else: result = 0x7FFFFFFFFFFFFFFF'i64 of tyInt8: result = 0x0000007F of tyInt16: result = 0x00007FFF of tyInt32: result = 0x7FFFFFFF of tyInt64: result = 0x7FFFFFFFFFFFFFFF'i64 - of tyUInt: + of tyUInt: if platform.intSize == 4: result = 0xFFFFFFFF else: result = 0x7FFFFFFFFFFFFFFF'i64 of tyUInt8: result = 0xFF of tyUInt16: result = 0xFFFF of tyUInt32: result = 0xFFFFFFFF of tyUInt64: result = 0x7FFFFFFFFFFFFFFF'i64 - of tyEnum: + of tyEnum: assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym) result = t.n.sons[sonsLen(t.n) - 1].sym.position of tyGenericInst, tyDistinct, tyConst, tyMutable, @@ -638,7 +642,7 @@ proc lastOrd(t: PType): BiggestInt = internalError("invalid kind for last(" & $t.kind & ')') result = 0 -proc lengthOrd(t: PType): BiggestInt = +proc lengthOrd(t: PType): BiggestInt = case t.kind of tyInt64, tyInt32, tyInt: result = lastOrd(t) of tyDistinct, tyConst, tyMutable: result = lengthOrd(t.sons[0]) @@ -654,7 +658,7 @@ type dcEqOrDistinctOf ## a equals b or a is distinct of b TTypeCmpFlag* = enum - IgnoreTupleFields + IgnoreTupleFields ## NOTE: Only set this flag for backends! IgnoreCC ExactTypeDescValues ExactGenericParams @@ -673,7 +677,7 @@ type proc initSameTypeClosure: TSameTypeClosure = # we do the initialization lazily for performance (avoids memory allocations) discard - + proc containsOrIncl(c: var TSameTypeClosure, a, b: PType): bool = result = not isNil(c.s) and c.s.contains((a.id, b.id)) if not result: @@ -700,17 +704,17 @@ proc sameTypeOrNil*(a, b: PType, flags: TTypeCmpFlags = {}): bool = if a == nil or b == nil: result = false else: result = sameType(a, b, flags) -proc equalParam(a, b: PSym): TParamsEquality = +proc equalParam(a, b: PSym): TParamsEquality = if sameTypeOrNil(a.typ, b.typ, {ExactTypeDescValues}) and exprStructuralEquivalent(a.constraint, b.constraint): - if a.ast == b.ast: + if a.ast == b.ast: result = paramsEqual - elif a.ast != nil and b.ast != nil: + elif a.ast != nil and b.ast != nil: if exprStructuralEquivalent(a.ast, b.ast): result = paramsEqual else: result = paramsIncompatible - elif a.ast != nil: + elif a.ast != nil: result = paramsEqual - elif b.ast != nil: + elif b.ast != nil: result = paramsIncompatible else: result = paramsNotEqual @@ -723,70 +727,70 @@ proc sameConstraints(a, b: PNode): bool = return false return true -proc equalParams(a, b: PNode): TParamsEquality = +proc equalParams(a, b: PNode): TParamsEquality = result = paramsEqual var length = sonsLen(a) - if length != sonsLen(b): + if length != sonsLen(b): result = paramsNotEqual - else: - for i in countup(1, length - 1): + else: + for i in countup(1, length - 1): var m = a.sons[i].sym var n = b.sons[i].sym assert((m.kind == skParam) and (n.kind == skParam)) case equalParam(m, n) - of paramsNotEqual: + of paramsNotEqual: return paramsNotEqual - of paramsEqual: + of paramsEqual: discard - of paramsIncompatible: + of paramsIncompatible: result = paramsIncompatible - if (m.name.id != n.name.id): + if (m.name.id != n.name.id): # BUGFIX return paramsNotEqual # paramsIncompatible; # continue traversal! If not equal, we can return immediately; else # it stays incompatible if not sameTypeOrNil(a.sons[0].typ, b.sons[0].typ, {ExactTypeDescValues}): - if (a.sons[0].typ == nil) or (b.sons[0].typ == nil): + if (a.sons[0].typ == nil) or (b.sons[0].typ == nil): result = paramsNotEqual # one proc has a result, the other not is OK - else: + else: result = paramsIncompatible # overloading by different # result types does not work - -proc sameLiteral(x, y: PNode): bool = - if x.kind == y.kind: + +proc sameLiteral(x, y: PNode): bool = + if x.kind == y.kind: case x.kind of nkCharLit..nkInt64Lit: result = x.intVal == y.intVal of nkFloatLit..nkFloat64Lit: result = x.floatVal == y.floatVal of nkNilLit: result = true else: assert(false) - -proc sameRanges(a, b: PNode): bool = + +proc sameRanges(a, b: PNode): bool = result = sameLiteral(a.sons[0], b.sons[0]) and sameLiteral(a.sons[1], b.sons[1]) -proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool = +proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool = # two tuples are equivalent iff the names, types and positions are the same; # however, both types may not have any field names (t.n may be nil) which # complicates the matter a bit. if sonsLen(a) == sonsLen(b): result = true - for i in countup(0, sonsLen(a) - 1): + for i in countup(0, sonsLen(a) - 1): var x = a.sons[i] var y = b.sons[i] if IgnoreTupleFields in c.flags: - x = skipTypes(x, {tyRange}) - y = skipTypes(y, {tyRange}) - + x = skipTypes(x, {tyRange, tyGenericInst}) + y = skipTypes(y, {tyRange, tyGenericInst}) + result = sameTypeAux(x, y, c) - if not result: return + if not result: return if a.n != nil and b.n != nil and IgnoreTupleFields notin c.flags: - for i in countup(0, sonsLen(a.n) - 1): - # check field names: + for i in countup(0, sonsLen(a.n) - 1): + # check field names: if a.n.sons[i].kind == nkSym and b.n.sons[i].kind == nkSym: var x = a.n.sons[i].sym var y = b.n.sons[i].sym result = x.name.id == y.name.id - if not result: break + if not result: break else: internalError(a.n.info, "sameTuple") template ifFastObjectTypeCheckFailed(a, b: PType, body: stmt) {.immediate.} = @@ -797,7 +801,7 @@ template ifFastObjectTypeCheckFailed(a, b: PType, body: stmt) {.immediate.} = # expensive structural equality test; however due to the way generic and # objects work, if one of the types does **not** contain tfFromGeneric, # they cannot be equal. The check ``a.sym.id == b.sym.id`` checks - # for the same origin and is essential because we don't want "pure" + # for the same origin and is essential because we don't want "pure" # structural type equivalence: # # type @@ -823,8 +827,13 @@ proc sameEnumTypes*(a, b: PType): bool {.inline.} = proc sameObjectTree(a, b: PNode, c: var TSameTypeClosure): bool = if a == b: result = true - elif (a != nil) and (b != nil) and (a.kind == b.kind): - if sameTypeOrNilAux(a.typ, b.typ, c): + elif a != nil and b != nil and a.kind == b.kind: + var x = a.typ + var y = b.typ + if IgnoreTupleFields in c.flags: + if x != nil: x = skipTypes(x, {tyRange, tyGenericInst}) + if y != nil: y = skipTypes(y, {tyRange, tyGenericInst}) + if sameTypeOrNilAux(x, y, c): case a.kind of nkSym: # same symbol as string is enough: @@ -835,9 +844,9 @@ proc sameObjectTree(a, b: PNode, c: var TSameTypeClosure): bool = of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal of nkEmpty, nkNilLit, nkType: result = true else: - if sonsLen(a) == sonsLen(b): - for i in countup(0, sonsLen(a) - 1): - if not sameObjectTree(a.sons[i], b.sons[i], c): return + if sonsLen(a) == sonsLen(b): + for i in countup(0, sonsLen(a) - 1): + if not sameObjectTree(a.sons[i], b.sons[i], c): return result = true proc sameObjectStructures(a, b: PType, c: var TSameTypeClosure): bool = @@ -853,7 +862,7 @@ proc sameChildrenAux(a, b: PType, c: var TSameTypeClosure): bool = result = true for i in countup(0, sonsLen(a) - 1): result = sameTypeOrNilAux(a.sons[i], b.sons[i], c) - if not result: return + if not result: return proc isGenericAlias*(t: PType): bool = return t.kind == tyGenericInst and t.lastSon.kind == tyGenericInst @@ -866,7 +875,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = # believe it or not, the direct check for ``containsOrIncl(c, a, b)`` # increases bootstrapping time from 2.4s to 3.3s on my laptop! So we cheat # again: Since the recursion check is only to not get caught in an endless - # recursion, we use a counter and only if it's value is over some + # recursion, we use a counter and only if it's value is over some # threshold we perform the expensive exact cycle check: if c.recCheck < 3: inc c.recCheck @@ -874,11 +883,11 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = if containsOrIncl(c, a, b): return true proc sameFlags(a, b: PType): bool {.inline.} = - result = eqTypeFlags*a.flags == eqTypeFlags*b.flags + result = eqTypeFlags*a.flags == eqTypeFlags*b.flags if x == y: return true var a = skipTypes(x, {tyGenericInst}) - var b = skipTypes(y, {tyGenericInst}) + var b = skipTypes(y, {tyGenericInst}) assert(a != nil) assert(b != nil) if a.kind != b.kind: @@ -891,7 +900,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = of dcEqOrDistinctOf: while a.kind == tyDistinct: a = a.sons[0] if a.kind != b.kind: return false - + if x.kind == tyGenericInst: let lhs = x.skipGenericAlias @@ -916,11 +925,11 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = result = sameObjectStructures(a, b, c) and sameFlags(a, b) of tyDistinct: cycleCheck() - if c.cmp == dcEq: + if c.cmp == dcEq: if sameFlags(a, b): ifFastObjectTypeCheckFailed(a, b): - result = sameTypeAux(a.sons[0], b.sons[0], c) - else: + result = sameTypeAux(a.sons[0], b.sons[0], c) + else: result = sameTypeAux(a.sons[0], b.sons[0], c) and sameFlags(a, b) of tyEnum, tyForward: # XXX generic enums do not make much sense, but require structural checking @@ -957,13 +966,13 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = sameValue(a.n.sons[0], b.n.sons[0]) and sameValue(a.n.sons[1], b.n.sons[1]) of tyGenericInst: discard - of tyNone: result = false + of tyNone: result = false proc sameBackendType*(x, y: PType): bool = var c = initSameTypeClosure() c.flags.incl IgnoreTupleFields result = sameTypeAux(x, y, c) - + proc compareTypes*(x, y: PType, cmp: TDistinctCompare = dcEq, flags: TTypeCmpFlags = {}): bool = @@ -972,8 +981,8 @@ proc compareTypes*(x, y: PType, c.cmp = cmp c.flags = flags result = sameTypeAux(x, y, c) - -proc inheritanceDiff*(a, b: PType): int = + +proc inheritanceDiff*(a, b: PType): int = # | returns: 0 iff `a` == `b` # | returns: -x iff `a` is the x'th direct superclass of `b` # | returns: +x iff `a` is the x'th direct subclass of `b` @@ -985,14 +994,14 @@ proc inheritanceDiff*(a, b: PType): int = result = 0 while x != nil: x = skipTypes(x, skipPtrs) - if sameObjectTypes(x, b): return + if sameObjectTypes(x, b): return x = x.sons[0] dec(result) var y = b result = 0 while y != nil: y = skipTypes(y, skipPtrs) - if sameObjectTypes(y, a): return + if sameObjectTypes(y, a): return y = y.sons[0] inc(result) result = high(int) @@ -1066,7 +1075,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, if kind == skConst: return t var t2 = skipTypes(t.sons[0], abstractInst-{tyTypeDesc}) case t2.kind - of tyVar: + of tyVar: if taHeap notin flags: result = t2 # ``var var`` is illegal on the heap of tyOpenArray: if kind != skParam: result = t @@ -1075,9 +1084,9 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, if kind notin {skParam, skResult}: result = t else: result = typeAllowedAux(marker, t2, kind, flags) of tyProc: - for i in countup(1, sonsLen(t) - 1): + for i in countup(1, sonsLen(t) - 1): result = typeAllowedAux(marker, t.sons[i], skParam, flags) - if result != nil: break + if result != nil: break if result.isNil and t.sons[0] != nil: result = typeAllowedAux(marker, t.sons[0], skResult, flags) of tyExpr, tyStmt, tyTypeDesc, tyStatic: @@ -1092,7 +1101,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, result = t of tyNil: if kind != skConst: result = t - of tyString, tyBool, tyChar, tyEnum, tyInt..tyBigNum, tyCString, tyPointer: + of tyString, tyBool, tyChar, tyEnum, tyInt..tyBigNum, tyCString, tyPointer: result = nil of tyOrdinal: if kind != skParam: result = t @@ -1132,13 +1141,13 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, # prevent cascading errors: result = nil -proc typeAllowed*(t: PType, kind: TSymKind): PType = +proc typeAllowed*(t: PType, kind: TSymKind): PType = # returns 'nil' on success and otherwise the part of the type that is # wrong! var marker = initIntSet() result = typeAllowedAux(marker, t, kind, {}) -proc align(address, alignment: BiggestInt): BiggestInt = +proc align(address, alignment: BiggestInt): BiggestInt = result = (address + (alignment - 1)) and not (alignment - 1) const @@ -1147,17 +1156,17 @@ const szUnknownSize* = -1 proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt -proc computeRecSizeAux(n: PNode, a, currOffset: var BiggestInt): BiggestInt = +proc computeRecSizeAux(n: PNode, a, currOffset: var BiggestInt): BiggestInt = var maxAlign, maxSize, b, res: BiggestInt case n.kind - of nkRecCase: + of nkRecCase: assert(n.sons[0].kind == nkSym) result = computeRecSizeAux(n.sons[0], a, currOffset) maxSize = 0 maxAlign = 1 - for i in countup(1, sonsLen(n) - 1): + for i in countup(1, sonsLen(n) - 1): case n.sons[i].kind - of nkOfBranch, nkElse: + of nkOfBranch, nkElse: res = computeRecSizeAux(lastSon(n.sons[i]), b, currOffset) if res < 0: return res maxSize = max(maxSize, res) @@ -1166,17 +1175,17 @@ proc computeRecSizeAux(n: PNode, a, currOffset: var BiggestInt): BiggestInt = currOffset = align(currOffset, maxAlign) + maxSize result = align(result, maxAlign) + maxSize a = maxAlign - of nkRecList: + of nkRecList: result = 0 maxAlign = 1 - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): res = computeRecSizeAux(n.sons[i], b, currOffset) if res < 0: return res currOffset = align(currOffset, b) + res result = align(result, b) + res if b > maxAlign: maxAlign = b a = maxAlign - of nkSym: + of nkSym: result = computeSizeAux(n.sym.typ, a) n.sym.offset = int(currOffset) else: @@ -1193,31 +1202,31 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt = # size already computed result = typ.size a = typ.align - return + return typ.size = szIllegalRecursion # mark as being computed case typ.kind - of tyInt, tyUInt: + of tyInt, tyUInt: result = intSize a = result - of tyInt8, tyUInt8, tyBool, tyChar: + of tyInt8, tyUInt8, tyBool, tyChar: result = 1 a = result - of tyInt16, tyUInt16: + of tyInt16, tyUInt16: result = 2 a = result - of tyInt32, tyUInt32, tyFloat32: + of tyInt32, tyUInt32, tyFloat32: result = 4 a = result - of tyInt64, tyUInt64, tyFloat64: + of tyInt64, tyUInt64, tyFloat64: result = 8 a = result of tyFloat128: result = 16 a = result - of tyFloat: + of tyFloat: result = floatSize a = result - of tyProc: + of tyProc: if typ.callConv == ccClosure: result = 2 * ptrSize else: result = ptrSize a = ptrSize @@ -1232,17 +1241,17 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt = let elemSize = computeSizeAux(typ.sons[1], a) if elemSize < 0: return elemSize result = lengthOrd(typ.sons[0]) * elemSize - of tyEnum: - if firstOrd(typ) < 0: + of tyEnum: + if firstOrd(typ) < 0: result = 4 # use signed int32 - else: + else: length = lastOrd(typ) # BUGFIX: use lastOrd! if length + 1 < `shl`(1, 8): result = 1 elif length + 1 < `shl`(1, 16): result = 2 elif length + 1 < `shl`(BiggestInt(1), 32): result = 4 else: result = 8 a = result - of tySet: + of tySet: length = lengthOrd(typ.sons[0]) if length <= 8: result = 1 elif length <= 16: result = 2 @@ -1251,32 +1260,32 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt = elif align(length, 8) mod 8 == 0: result = align(length, 8) div 8 else: result = align(length, 8) div 8 + 1 a = result - of tyRange: + of tyRange: result = computeSizeAux(typ.sons[0], a) - of tyTuple: + of tyTuple: result = 0 maxAlign = 1 - for i in countup(0, sonsLen(typ) - 1): + for i in countup(0, sonsLen(typ) - 1): res = computeSizeAux(typ.sons[i], a) if res < 0: return res maxAlign = max(maxAlign, a) result = align(result, a) + res result = align(result, maxAlign) a = maxAlign - of tyObject: - if typ.sons[0] != nil: + of tyObject: + if typ.sons[0] != nil: result = computeSizeAux(typ.sons[0], a) - if result < 0: return + if result < 0: return maxAlign = a - elif isObjectWithTypeFieldPredicate(typ): + elif isObjectWithTypeFieldPredicate(typ): result = intSize maxAlign = result - else: + else: result = 0 maxAlign = 1 currOffset = result result = computeRecSizeAux(typ.n, a, currOffset) - if result < 0: return + if result < 0: return if a < maxAlign: a = maxAlign result = align(result, a) of tyGenericInst, tyDistinct, tyGenericBody, tyMutable, tyConst, tyIter: @@ -1290,7 +1299,7 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt = typ.size = result typ.align = int16(a) -proc computeSize(typ: PType): BiggestInt = +proc computeSize(typ: PType): BiggestInt = var a: BiggestInt = 1 result = computeSizeAux(typ, a) @@ -1299,7 +1308,7 @@ proc getReturnType*(s: PSym): PType = assert s.kind in skProcKinds result = s.typ.sons[0] -proc getSize(typ: PType): BiggestInt = +proc getSize(typ: PType): BiggestInt = result = computeSize(typ) if result < 0: internalError("getSize: " & $typ.kind) @@ -1317,7 +1326,7 @@ proc containsGenericTypeIter(t: PType, closure: RootRef): bool = return false -proc containsGenericType*(t: PType): bool = +proc containsGenericType*(t: PType): bool = result = iterOverType(t, containsGenericTypeIter, nil) proc baseOfDistinct*(t: PType): PType = @@ -1336,7 +1345,7 @@ proc baseOfDistinct*(t: PType): PType = proc safeInheritanceDiff*(a, b: PType): int = # same as inheritanceDiff but checks for tyError: - if a.kind == tyError or b.kind == tyError: + if a.kind == tyError or b.kind == tyError: result = -1 else: result = inheritanceDiff(a, b) @@ -1356,7 +1365,7 @@ proc compatibleEffects*(formal, actual: PType): bool = assert formal.kind == tyProc and actual.kind == tyProc internalAssert formal.n.sons[0].kind == nkEffectList internalAssert actual.n.sons[0].kind == nkEffectList - + var spec = formal.n.sons[0] if spec.len != 0: var real = actual.n.sons[0] diff --git a/compiler/typesrenderer.nim b/compiler/typesrenderer.nim index 995fe7f50..700356ab7 100644 --- a/compiler/typesrenderer.nim +++ b/compiler/typesrenderer.nim @@ -68,7 +68,6 @@ proc renderType(n: PNode): string = assert n[i].kind == nkIdent result.add(',' & typeStr) of nkTupleTy: - assert len(n) > 0 result = "tuple[" for i in 0 .. <len(n): result.add(renderType(n[i]) & ',') result[<len(result)] = ']' diff --git a/compiler/vm.nim b/compiler/vm.nim index a36de1c20..f0a0135e8 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -239,7 +239,7 @@ proc pushSafePoint(f: PStackFrame; pc: int) = proc popSafePoint(f: PStackFrame) = discard f.safePoints.pop() -proc cleanUpOnException(c: PCtx; tos: PStackFrame): +proc cleanUpOnException(c: PCtx; tos: PStackFrame): tuple[pc: int, f: PStackFrame] = let raisedType = c.currentExceptionA.typ.skipTypes(abstractPtrs) var f = tos @@ -257,7 +257,7 @@ proc cleanUpOnException(c: PCtx; tos: PStackFrame): let exceptType = c.types[c.code[pc2].regBx-wordExcess].skipTypes( abstractPtrs) if inheritanceDiff(exceptType, raisedType) <= 0: - # mark exception as handled but keep it in B for + # mark exception as handled but keep it in B for # the getCurrentException() builtin: c.currentExceptionB = c.currentExceptionA c.currentExceptionA = nil @@ -349,14 +349,14 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = if dest.kind != rkFloat: myreset(dest); dest.kind = rkFloat case skipTypes(srctyp, abstractRange).kind - of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyBool, tyChar: + of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyBool, tyChar: dest.floatVal = toFloat(src.intVal.int) else: dest.floatVal = src.floatVal else: asgnComplex(dest, src) -proc compile(c: PCtx, s: PSym): int = +proc compile(c: PCtx, s: PSym): int = result = vmgen.genProc(c, s) when debugEchoCode: c.echoCode result #c.echoCode @@ -396,10 +396,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = pc = tos.comesFrom tos = tos.next let retVal = regs[0] - if tos.isNil: + if tos.isNil: #echo "RET ", retVal.rendertree return retVal - + move(regs, tos.slots) assert c.code[pc].opcode in {opcIndCall, opcIndCallAsgn} if c.code[pc].opcode == opcIndCallAsgn: @@ -653,7 +653,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcSubu: decodeBC(rkInt) regs[ra].intVal = regs[rb].intVal -% regs[rc].intVal - of opcMulu: + of opcMulu: decodeBC(rkInt) regs[ra].intVal = regs[rb].intVal *% regs[rc].intVal of opcDivu: @@ -726,7 +726,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcLeSet: decodeBC(rkInt) regs[ra].intVal = ord(containsSets(regs[rb].node, regs[rc].node)) - of opcEqSet: + of opcEqSet: decodeBC(rkInt) regs[ra].intVal = ord(equalSets(regs[rb].node, regs[rc].node)) of opcLtSet: @@ -737,9 +737,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcMulSet: decodeBC(rkNode) createSet(regs[ra]) - move(regs[ra].node.sons, + move(regs[ra].node.sons, nimsets.intersectSets(regs[rb].node, regs[rc].node).sons) - of opcPlusSet: + of opcPlusSet: decodeBC(rkNode) createSet(regs[ra]) move(regs[ra].node.sons, @@ -753,7 +753,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeBC(rkNode) createSet(regs[ra]) move(regs[ra].node.sons, - nimsets.symdiffSets(regs[rb].node, regs[rc].node).sons) + nimsets.symdiffSets(regs[rb].node, regs[rc].node).sons) of opcConcatStr: decodeBC(rkNode) createStr regs[ra] @@ -793,7 +793,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = assert c.code[pc].opcode == opcSubStr let rd = c.code[pc].regA createStr regs[ra] - regs[ra].node.strVal = substr(regs[rb].node.strVal, + regs[ra].node.strVal = substr(regs[rb].node.strVal, regs[rc].intVal.int, regs[rd].intVal.int) of opcParseFloat: decodeBC(rkInt) @@ -896,12 +896,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = # we know the next instruction is a 'fjmp': let branch = c.constants[instr.regBx-wordExcess] var cond = false - for j in countup(0, sonsLen(branch) - 2): - if overlap(regs[ra].regToNode, branch.sons[j]): + for j in countup(0, sonsLen(branch) - 2): + if overlap(regs[ra].regToNode, branch.sons[j]): cond = true break assert c.code[pc+1].opcode == opcFJmp - inc pc + inc pc # we skip this instruction so that the final 'inc(pc)' skips # the following jump if not cond: @@ -1273,7 +1273,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNSetIntVal: decodeB(rkNode) var dest = regs[ra].node - if dest.kind in {nkCharLit..nkInt64Lit} and + if dest.kind in {nkCharLit..nkInt64Lit} and regs[rb].kind in {rkInt}: dest.intVal = regs[rb].intVal else: @@ -1281,24 +1281,24 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNSetFloatVal: decodeB(rkNode) var dest = regs[ra].node - if dest.kind in {nkFloatLit..nkFloat64Lit} and + if dest.kind in {nkFloatLit..nkFloat64Lit} and regs[rb].kind in {rkFloat}: dest.floatVal = regs[rb].floatVal - else: + else: stackTrace(c, tos, pc, errFieldXNotFound, "floatVal") of opcNSetSymbol: decodeB(rkNode) var dest = regs[ra].node if dest.kind == nkSym and regs[rb].node.kind == nkSym: dest.sym = regs[rb].node.sym - else: + else: stackTrace(c, tos, pc, errFieldXNotFound, "symbol") of opcNSetIdent: decodeB(rkNode) var dest = regs[ra].node if dest.kind == nkIdent and regs[rb].node.kind == nkIdent: dest.ident = regs[rb].node.ident - else: + else: stackTrace(c, tos, pc, errFieldXNotFound, "ident") of opcNSetType: decodeB(rkNode) @@ -1309,7 +1309,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNSetStrVal: decodeB(rkNode) var dest = regs[ra].node - if dest.kind in {nkStrLit..nkTripleStrLit} and + if dest.kind in {nkStrLit..nkTripleStrLit} and regs[rb].kind in {rkNode}: dest.strVal = regs[rb].node.strVal else: @@ -1435,8 +1435,9 @@ proc evalConstExprAux(module, prc: PSym, n: PNode, mode: TEvalMode): PNode = newSeq(tos.slots, c.prc.maxSlots) #for i in 0 .. <c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty) result = rawExecute(c, start, tos).regToNode + if result.info.line < 0: result.info = n.info -proc evalConstExpr*(module: PSym, e: PNode): PNode = +proc evalConstExpr*(module: PSym, e: PNode): PNode = result = evalConstExprAux(module, nil, e, emConst) proc evalStaticExpr*(module: PSym, e: PNode, prc: PSym): PNode = @@ -1496,6 +1497,7 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode = # temporary storage: #for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty) result = rawExecute(c, start, tos).regToNode + if result.info.line < 0: result.info = n.info if cyclicTree(result): globalError(n.info, errCyclicTree) dec(evalMacroCounter) c.callsite = nil diff --git a/doc/lib.txt b/doc/lib.txt index 76920c6a9..385e7a91a 100644 --- a/doc/lib.txt +++ b/doc/lib.txt @@ -175,6 +175,9 @@ Generic Operating System Services This module implements the ability to monitor a directory/file for changes using Posix's inotify API. +* `asyncfile <asyncfile.html>`_ + This module implements asynchronous file reading and writing using + ``asyncdispatch``. Math libraries -------------- diff --git a/koch.nim b/koch.nim index d365262c1..34cb1317d 100644 --- a/koch.nim +++ b/koch.nim @@ -347,7 +347,7 @@ proc temp(args: string) = if args.len > 0: exec(finalDest & " " & args) proc showHelp() = - quit(HelpText % [VersionAsString & repeatChar(44-len(VersionAsString)), + quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)), CompileDate, CompileTime], QuitSuccess) var op = initOptParser() diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 0888a8767..f73dbd241 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -60,7 +60,7 @@ type nnkStmtListType, nnkBlockType, nnkWith, nnkWithout, nnkTypeOfExpr, nnkObjectTy, - nnkTupleTy, nnkTypeClassTy, nnkStaticTy, + nnkTupleTy, nnkTupleClassTy, nnkTypeClassTy, nnkStaticTy, nnkRecList, nnkRecCase, nnkRecWhen, nnkRefTy, nnkPtrTy, nnkVarTy, nnkConstTy, nnkMutableTy, diff --git a/lib/impure/rdstdin.nim b/lib/impure/rdstdin.nim index aaf2ed1ca..f64f9ccb2 100644 --- a/lib/impure/rdstdin.nim +++ b/lib/impure/rdstdin.nim @@ -8,16 +8,16 @@ # ## This module contains code for reading from `stdin`:idx:. On UNIX the GNU -## readline library is wrapped and set up to provide default key bindings +## readline library is wrapped and set up to provide default key bindings ## (e.g. you can navigate with the arrow keys). On Windows ``system.readLine`` -## is used. This suffices because Windows' console already provides the +## is used. This suffices because Windows' console already provides the ## wanted functionality. {.deadCodeElim: on.} when defined(Windows): proc readLineFromStdin*(prompt: string): TaintedString {. - tags: [ReadIOEffect, WriteIOEffect].} = + tags: [ReadIOEffect, WriteIOEffect].} = ## Reads a line from stdin. stdout.write(prompt) result = readLine(stdin) @@ -33,27 +33,71 @@ when defined(Windows): stdout.write(prompt) result = readLine(stdin, line) + import winlean + + const + VK_SHIFT* = 16 + VK_CONTROL* = 17 + VK_MENU* = 18 + KEY_EVENT* = 1 + + type + KEY_EVENT_RECORD = object + bKeyDown: WinBool + wRepeatCount: uint16 + wVirtualKeyCode: uint16 + wVirtualScanCode: uint16 + unicodeChar: uint16 + dwControlKeyState: uint32 + INPUT_RECORD = object + eventType*: int16 + reserved*: int16 + event*: KEY_EVENT_RECORD + safetyBuffer: array[0..5, DWORD] + + proc readConsoleInputW*(hConsoleInput: THANDLE, lpBuffer: var INPUTRECORD, + nLength: uint32, + lpNumberOfEventsRead: var uint32): WINBOOL{. + stdcall, dynlib: "kernel32", importc: "ReadConsoleInputW".} + + proc getch(): uint16 = + let hStdin = getStdHandle(STD_INPUT_HANDLE) + var + irInputRecord: INPUT_RECORD + dwEventsRead: uint32 + + while readConsoleInputW(hStdin, irInputRecord, 1, dwEventsRead) != 0: + if irInputRecord.eventType == KEY_EVENT and + irInputRecord.event.wVirtualKeyCode notin {VK_SHIFT, VK_MENU, VK_CONTROL}: + result = irInputRecord.event.unicodeChar + discard readConsoleInputW(hStdin, irInputRecord, 1, dwEventsRead) + return result + + from unicode import toUTF8, Rune, runeLenAt + proc readPasswordFromStdin*(prompt: string, password: var TaintedString): bool {.tags: [ReadIOEffect, WriteIOEffect].} = ## Reads a `password` from stdin without printing it. `password` must not ## be ``nil``! Returns ``false`` if the end of the file has been reached, ## ``true`` otherwise. - proc getch(): cint {.header: "<conio.h>", importc: "_getch".} - password.setLen(0) - var c: char stdout.write(prompt) while true: - c = getch().char - case c + let c = getch() + case c.char of '\r', chr(0xA): break of '\b': - password.setLen(password.len - 1) + # ensure we delete the whole UTF-8 character: + var i = 0 + var x = 1 + while i < password.len: + x = runeLenAt(password, i) + inc i, x + password.setLen(password.len - x) else: - password.add(c) + password.add(toUTF8(c.Rune)) stdout.write "\n" - # TODO: How to detect EOF on Windows? else: import readline, history, termios, unsigned diff --git a/lib/impure/re.nim b/lib/impure/re.nim index 7d5ff8948..921a24fd1 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -66,7 +66,7 @@ proc rawCompile(pattern: string, flags: cint): PPcre = offset: cint result = pcre.compile(pattern, flags, addr(msg), addr(offset), nil) if result == nil: - raiseInvalidRegex($msg & "\n" & pattern & "\n" & repeatChar(offset) & "^\n") + raiseInvalidRegex($msg & "\n" & pattern & "\n" & spaces(offset) & "^\n") proc finalizeRegEx(x: Regex) = # XXX This is a hack, but PCRE does not export its "free" function properly. @@ -291,7 +291,7 @@ proc replace*(s: string, sub: Regex, by = ""): string = ## accessed in `by`. Examples: ## ## .. code-block:: nim - ## "var1=key; var2=key2".replace(re"(\w+)'='(\w+)") + ## "var1=key; var2=key2".replace(re"(\w+)=(\w+)") ## ## Results in: ## @@ -313,7 +313,7 @@ proc replacef*(s: string, sub: Regex, by: string): string = ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples: ## ## .. code-block:: nim - ## "var1=key; var2=key2".replacef(re"(\w+)'='(\w+)", "$1<-$2$2") + ## "var1=key; var2=key2".replacef(re"(\w+)=(\w+)", "$1<-$2$2") ## ## Results in: ## diff --git a/lib/nimbase.h b/lib/nimbase.h index 50c7968ac..e9dad0bb7 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -67,7 +67,7 @@ __clang__ #endif #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) -# define NIM_THREADVAR __declspec(thread) +# define NIM_THREADVAR __declspec(thread) #else # define NIM_THREADVAR __thread #endif @@ -103,7 +103,11 @@ __clang__ # define N_FASTCALL_PTR(rettype, name) rettype (__fastcall *name) # define N_SAFECALL_PTR(rettype, name) rettype (__safecall *name) -# define N_LIB_EXPORT extern __declspec(dllexport) +# ifdef __cplusplus +# define N_LIB_EXPORT extern "C" __declspec(dllexport) +# else +# define N_LIB_EXPORT extern __declspec(dllexport) +# endif # define N_LIB_IMPORT extern __declspec(dllimport) #else # define N_CDECL(rettype, name) rettype name @@ -118,7 +122,11 @@ __clang__ # define N_FASTCALL_PTR(rettype, name) rettype (*name) # define N_SAFECALL_PTR(rettype, name) rettype (*name) -# define N_LIB_EXPORT extern +# ifdef __cplusplus +# define N_LIB_EXPORT extern "C" +# else +# define N_LIB_EXPORT extern +# endif # define N_LIB_IMPORT extern #endif @@ -345,7 +353,7 @@ struct TFrame { #define nimln(n, file) \ F.line = n; F.filename = file; -#define NIM_POSIX_INIT __attribute__((constructor)) +#define NIM_POSIX_INIT __attribute__((constructor)) #if defined(_MSCVER) && defined(__i386__) __declspec(naked) int __fastcall NimXadd(volatile int* pNum, int val) { @@ -380,7 +388,7 @@ static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); } #endif /* Test to see if Nim and the C compiler agree on the size of a pointer. - On disagreement, your C compiler will say something like: + On disagreement, your C compiler will say something like: "error: 'assert_numbits' declared as an array with a negative size" */ typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1]; #endif @@ -398,4 +406,6 @@ typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof( # include <sys/types.h> # include <types/vxWind.h> # include <tool/gnu/toolMacros.h> +#elif defined(__FreeBSD__) +# include <sys/types.h> #endif diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 95d49bad7..97784898e 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -189,7 +189,7 @@ proc getIndent(L: var TLexer, tok: var TToken) = tok.line = L.line L.col = tok.ival tok.ival = max(tok.ival - L.baseIndent, 0) - tok.symbol = "\n" & repeatChar(tok.ival) + tok.symbol = "\n" & spaces(tok.ival) proc rawGetTok(L: var TLexer, tok: var TToken) = tok.symbol = "" @@ -963,7 +963,7 @@ proc parseLiteralBlock(p: var TRstParser): PRstNode = break else: add(n.text, "\n") - add(n.text, repeatChar(p.tok[p.idx].ival - indent)) + add(n.text, spaces(p.tok[p.idx].ival - indent)) inc(p.idx) else: add(n.text, p.tok[p.idx].symbol) diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index 52af672df..614001d76 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -110,7 +110,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) = const lvlToChar: array[0..8, char] = ['!', '=', '-', '~', '`', '<', '*', '|', '+'] if n == nil: return - var ind = repeatChar(d.indent) + var ind = spaces(d.indent) case n.kind of rnInner: renderRstSons(d, n, result) @@ -124,7 +124,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) = result.add("\n") result.add(ind) - result.add repeatChar(headlineLen, lvlToChar[n.level]) + result.add repeat(lvlToChar[n.level], headlineLen) of rnOverline: result.add("\n") result.add(ind) @@ -132,7 +132,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) = var headline = "" renderRstSons(d, n, headline) - let lvl = repeatChar(headline.len - d.indent, lvlToChar[n.level]) + let lvl = repeat(lvlToChar[n.level], headline.len - d.indent) result.add(lvl) result.add("\n") result.add(headline) @@ -143,7 +143,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) = of rnTransition: result.add("\n\n") result.add(ind) - result.add repeatChar(78-d.indent, '-') + result.add repeat('-', 78-d.indent) result.add("\n\n") of rnParagraph: result.add("\n\n") @@ -196,7 +196,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) = result.add ':' result.add tmp result.add ':' - result.add repeatChar(L - tmp.len - 2) + result.add spaces(L - tmp.len - 2) renderRstToRst(d, n.sons[1], result) dec(d.indent, L) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index e1e590877..a385336d6 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -461,9 +461,9 @@ proc indentToLevel(level: var int, newLevel: int): string = if level == newLevel: return if newLevel > level: - result = repeatStr(newLevel - level, "<ul>") + result = repeat("<ul>", newLevel - level) else: - result = repeatStr(level - newLevel, "</ul>") + result = repeat("</ul>", level - newLevel) level = newLevel proc generateDocumentationTOC(entries: seq[TIndexEntry]): string = @@ -701,7 +701,7 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) = # Generate index entry using spaces to indicate TOC level for the output HTML. assert n.level >= 0 setIndexTerm(d, refname, tmp.stripTOCHTML, - repeatChar(max(0, n.level), ' ') & tmp) + spaces(max(0, n.level)) & tmp) proc renderOverline(d: PDoc, n: PRstNode, result: var string) = if d.meta[metaTitle].len == 0: diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index 20bfc5c7c..20976e788 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -187,6 +187,34 @@ proc sort*[T](a: var openArray[T], dec(m, s*2) s = s*2 +proc sorted*[T](a: openArray[T], cmp: proc(x, y: T): int {.closure.}, order = SortOrder.Ascending): seq[T] = + ## returns `a` sorted by `cmp` in the specified `order`. + result = newSeq[T](a.len) + for i in 0 .. a.high: + result[i] = a[i] + sort(result, cmp, order) + +template sortByIt*(seq1, op: expr): expr = + ## Convenience template around the ``sorted`` proc to reduce typing. + ## + ## The template injects the ``it`` variable which you can use directly in an + ## expression. Example: + ## + ## .. code-block:: nim + ## + ## var users: seq[tuple[id: int, name: string]] = + ## @[(0, "Smith"), (1, "Pratt"), (2, "Sparrow")] + ## + ## echo users.sortByIt(it.name) + ## + var result {.gensym.} = sorted(seq1, proc(x, y: type(seq1[0])): int = + var it {.inject.} = x + let a = op + it = y + let b = op + result = cmp(a, b)) + result + proc product*[T](x: openArray[seq[T]]): seq[seq[T]] = ## produces the Cartesian product of the array. Warning: complexity ## may explode. diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index d6ed66030..bf905ed49 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -121,7 +121,7 @@ export Port, SocketFlag ## ## Limitations/Bugs ## ---------------- -## +## ## * ``except`` statement (without `try`) does not work inside async procedures. ## * The effect system (``raises: []``) does not work with async procedures. ## * Can't await in a ``except`` body @@ -379,7 +379,7 @@ when defined(windows) or defined(nimdoc): if p.handles.len == 0 and p.timers.len == 0: raise newException(ValueError, "No handles or timers registered in dispatcher.") - + let llTimeout = if timeout == -1: winlean.INFINITE else: timeout.int32 @@ -436,12 +436,12 @@ when defined(windows) or defined(nimdoc): if not initPointer(dummySock, getAcceptExSockAddrsPtr, WSAID_GETACCEPTEXSOCKADDRS): raiseOSError(osLastError()) - proc connectEx(s: SocketHandle, name: ptr SockAddr, namelen: cint, + proc connectEx(s: SocketHandle, name: ptr SockAddr, namelen: cint, lpSendBuffer: pointer, dwSendDataLength: Dword, lpdwBytesSent: PDword, lpOverlapped: POVERLAPPED): bool = if connectExPtr.isNil: raise newException(ValueError, "Need to initialise ConnectEx().") let fun = - cast[proc (s: SocketHandle, name: ptr SockAddr, namelen: cint, + cast[proc (s: SocketHandle, name: ptr SockAddr, namelen: cint, lpSendBuffer: pointer, dwSendDataLength: Dword, lpdwBytesSent: PDword, lpOverlapped: POVERLAPPED): bool {.stdcall,gcsafe.}](connectExPtr) @@ -475,7 +475,7 @@ when defined(windows) or defined(nimdoc): dwRemoteAddressLength: Dword, LocalSockaddr: ptr ptr SockAddr, LocalSockaddrLength: LPInt, RemoteSockaddr: ptr ptr SockAddr, RemoteSockaddrLength: LPInt) {.stdcall,gcsafe.}](getAcceptExSockAddrsPtr) - + fun(lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength, RemoteSockaddr, RemoteSockaddrLength) @@ -514,7 +514,7 @@ when defined(windows) or defined(nimdoc): else: retFuture.fail(newException(OSError, osErrorMsg(errcode))) ) - + var ret = connectEx(socket.SocketHandle, it.ai_addr, sizeof(Sockaddr_in).cint, nil, 0, nil, cast[POVERLAPPED](ol)) @@ -565,7 +565,7 @@ when defined(windows) or defined(nimdoc): var dataBuf: TWSABuf dataBuf.buf = cast[cstring](alloc0(size)) dataBuf.len = size - + var bytesReceived: Dword var flagsio = flags.toOSFlags().Dword var ol = PCustomOverlapped() @@ -612,9 +612,9 @@ when defined(windows) or defined(nimdoc): # the empty string (which signals a disconnection) when there is # nothing left to read. retFuture.complete("") - # TODO: "For message-oriented sockets, where a zero byte message is often - # allowable, a failure with an error code of WSAEDISCON is used to - # indicate graceful closure." + # TODO: "For message-oriented sockets, where a zero byte message is often + # allowable, a failure with an error code of WSAEDISCON is used to + # indicate graceful closure." # ~ http://msdn.microsoft.com/en-us/library/ms741688%28v=vs.85%29.aspx else: # Request to read completed immediately. @@ -748,7 +748,7 @@ when defined(windows) or defined(nimdoc): # http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx let ret = acceptEx(socket.SocketHandle, clientSock, addr lpOutputBuf[0], - dwReceiveDataLength, + dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength, addr dwBytesReceived, cast[POVERLAPPED](ol)) @@ -803,7 +803,7 @@ else: else: from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK, MSG_NOSIGNAL - + type TAsyncFD* = distinct cint TCallback = proc (fd: TAsyncFD): bool {.closure,gcsafe.} @@ -849,7 +849,7 @@ else: result = newRawSocket(domain, typ, protocol).TAsyncFD result.SocketHandle.setBlocking(false) register(result) - + proc closeSocket*(sock: TAsyncFD) = let disp = getGlobalDispatcher() sock.SocketHandle.close() @@ -864,14 +864,14 @@ else: raise newException(ValueError, "File descriptor not registered.") p.selector[fd.SocketHandle].data.PData.readCBs.add(cb) update(fd, p.selector[fd.SocketHandle].events + {EvRead}) - + proc addWrite*(fd: TAsyncFD, cb: TCallback) = let p = getGlobalDispatcher() if fd.SocketHandle notin p.selector: raise newException(ValueError, "File descriptor not registered.") p.selector[fd.SocketHandle].data.PData.writeCBs.add(cb) update(fd, p.selector[fd.SocketHandle].events + {EvWrite}) - + proc poll*(timeout = 500) = let p = getGlobalDispatcher() for info in p.selector.select(timeout): @@ -892,7 +892,7 @@ else: if not cb(data.fd): # Callback wants to be called again. data.readCBs.add(cb) - + if EvWrite in info.events: let currentCBs = data.writeCBs data.writeCBs = @[] @@ -900,7 +900,7 @@ else: if not cb(data.fd): # Callback wants to be called again. data.writeCBs.add(cb) - + if info.key in p.selector: var newEvents: set[Event] if data.readCBs.len != 0: newEvents = {EvRead} @@ -913,16 +913,16 @@ else: discard processTimers(p) - + proc connect*(socket: TAsyncFD, address: string, port: Port, af = AF_INET): Future[void] = var retFuture = newFuture[void]("connect") - + proc cb(fd: TAsyncFD): bool = # We have connected. retFuture.complete() return true - + var aiList = getAddrInfo(address, port, af) var success = false var lastError: OSErrorCode @@ -952,7 +952,7 @@ else: proc recv*(socket: TAsyncFD, size: int, flags = {SocketFlag.SafeDisconn}): Future[string] = var retFuture = newFuture[string]("recv") - + var readBuffer = newString(size) proc cb(sock: TAsyncFD): bool = @@ -983,9 +983,9 @@ else: proc send*(socket: TAsyncFD, data: string, flags = {SocketFlag.SafeDisconn}): Future[void] = var retFuture = newFuture[void]("send") - + var written = 0 - + proc cb(sock: TAsyncFD): bool = result = true let netSize = data.len-written @@ -1222,7 +1222,7 @@ proc processBody(node, retFutureSym: PNimrodNode, of nnkTryStmt: # try: await x; except: ... result = newNimNode(nnkStmtList, node) - template wrapInTry(n, tryBody: PNimrodNode) = + template wrapInTry(n, tryBody: expr) = var temp = n n[0] = tryBody tryBody = temp @@ -1315,14 +1315,14 @@ macro async*(prc: stmt): stmt {.immediate.} = if returnType.kind == nnkEmpty: newIdentNode("void") else: returnType[1] outerProcBody.add( - newVarStmt(retFutureSym, + newVarStmt(retFutureSym, newCall( newNimNode(nnkBracketExpr, prc[6]).add( newIdentNode(!"newFuture"), # TODO: Strange bug here? Remove the `!`. subRetType), newLit(prc[0].getName)))) # Get type from return type of this proc - - # -> iterator nameIter(): FutureBase {.closure.} = + + # -> iterator nameIter(): FutureBase {.closure.} = # -> var result: T # -> <proc_body> # -> complete(retFuture, result) @@ -1337,7 +1337,7 @@ macro async*(prc: stmt): stmt {.immediate.} = else: # -> complete(retFuture) procBody.add(newCall(newIdentNode("complete"), retFutureSym)) - + var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase")], procBody, nnkIteratorDef) closureIterator[4] = newNimNode(nnkPragma, prc[6]).add(newIdentNode("closure")) @@ -1351,7 +1351,7 @@ macro async*(prc: stmt): stmt {.immediate.} = # -> return retFuture outerProcBody.add newNimNode(nnkReturnStmt, prc[6][prc[6].len-1]).add(retFutureSym) - + result = prc # Remove the 'async' pragma. @@ -1377,7 +1377,7 @@ proc recvLine*(socket: TAsyncFD): Future[string] {.async.} = ## If a full line is read ``\r\L`` is not ## added to ``line``, however if solely ``\r\L`` is read then ``line`` ## will be set to it. - ## + ## ## If the socket is disconnected, ``line`` will be set to ``""``. ## ## If the socket is disconnected in the middle of a line (before ``\r\L`` @@ -1388,7 +1388,7 @@ proc recvLine*(socket: TAsyncFD): Future[string] {.async.} = ## ## **Note**: This procedure is mostly used for testing. You likely want to ## use ``asyncnet.recvLine`` instead. - + template addNLIfEmpty(): stmt = if result.len == 0: result.add("\c\L") diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim index 752c01eac..844c2ab55 100644 --- a/lib/pure/asyncfile.nim +++ b/lib/pure/asyncfile.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -## This module implements asynchronous file handling. +## This module implements asynchronous file reading and writing. ## ## .. code-block:: Nim ## import asyncfile, asyncdispatch, os @@ -30,7 +30,7 @@ else: import posix type - AsyncFile = ref object + AsyncFile* = ref object fd: TAsyncFd offset: int64 diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index e690e8eba..edb904cc6 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -47,6 +47,24 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] = result[i] = itm inc(i) +proc repeat*[T](s: seq[T], n: Natural): seq[T] = + ## Returns a new sequence with the items of `s` repeated `n` times. + ## + ## Example: + ## + ## .. code-block: + ## + ## let + ## s = @[1, 2, 3] + ## total = s.repeat(3) + ## assert total == @[1, 2, 3, 1, 2, 3, 1, 2, 3] + result = newSeq[T](n * s.len) + var o = 0 + for x in 1..n: + for e in s: + result[o] = e + inc o + proc deduplicate*[T](seq1: seq[T]): seq[T] = ## Returns a new sequence without duplicates. ## @@ -86,7 +104,7 @@ proc zip*[S, T](seq1: seq[S], seq2: seq[T]): seq[tuple[a: S, b: T]] = newSeq(result, m) for i in 0 .. m-1: result[i] = (seq1[i], seq2[i]) -proc distribute*[T](s: seq[T], num: int, spread = true): seq[seq[T]] = +proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] = ## Splits and distributes a sequence `s` into `num` sub sequences. ## ## Returns a sequence of `num` sequences. For some input values this is the @@ -113,11 +131,12 @@ proc distribute*[T](s: seq[T], num: int, spread = true): seq[seq[T]] = ## assert numbers.distribute(6)[0] == @[1, 2] ## assert numbers.distribute(6)[5] == @[7] assert(not s.isNil, "`s` can't be nil") - assert(num > 0, "`num` has to be greater than zero") if num < 2: result = @[s] return + let num = int(num) # XXX probably only needed because of .. bug + # Create the result and calculate the stride size and the remainder if any. result = newSeq[seq[T]](num) var @@ -587,4 +606,14 @@ when isMainModule: seq2D[0][1] = true doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]] + block: # repeat tests + let + a = @[1, 2, 3] + b: seq[int] = @[] + + doAssert a.repeat(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3] + doAssert a.repeat(0) == @[] + #doAssert a.repeat(-1) == @[] # will not compile! + doAssert b.repeat(3) == @[] + echo "Finished doc tests" diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 959688d6a..93a7591ac 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -196,7 +196,11 @@ proc mget*[A, B](t: var Table[A, B], key: A): var B = var hc: THash var index = rawGet(t, key, hc) if index >= 0: result = t.data[index].val - else: raise newException(KeyError, "key not found: " & $key) + else: + when compiles($key): + raise newException(KeyError, "key not found: " & $key) + else: + raise newException(KeyError, "key not found") iterator allValues*[A, B](t: Table[A, B]; key: A): B = ## iterates over any value in the table `t` that belongs to the given `key`. diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 30daaf2dc..a16342d44 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -33,8 +33,8 @@ ## proc hash(x: Something): THash = ## ## Computes a THash from `x`. ## var h: THash = 0 -## h = h &! hash(x.foo) -## h = h &! hash(x.bar) +## h = h !& hash(x.foo) +## h = h !& hash(x.bar) ## result = !$h import diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 37af14df3..f11101511 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -390,8 +390,11 @@ proc request*(url: string, httpMethod: string, extraHeaders = "", ## server takes longer than specified an ETimeout exception will be raised. var r = if proxy == nil: parseUri(url) else: proxy.url var headers = substr(httpMethod, len("http")) + # TODO: Use generateHeaders further down once it supports proxies. if proxy == nil: - headers.add(" " & r.path) + headers.add ' ' + if r.path[0] != '/': headers.add '/' + headers.add(r.path) if r.query.len > 0: headers.add("?" & r.query) else: @@ -567,9 +570,12 @@ proc downloadFile*(url: string, outputFilename: string, proc generateHeaders(r: Uri, httpMethod: string, headers: StringTableRef): string = + # TODO: Use this in the blocking HttpClient once it supports proxies. result = substr(httpMethod, len("http")) # TODO: Proxies - result.add(" " & r.path) + result.add ' ' + if r.path[0] != '/': result.add '/' + result.add(r.path) if r.query.len > 0: result.add("?" & r.query) result.add(" HTTP/1.1\c\L") diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 2038b246d..5041fe2f6 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -816,7 +816,7 @@ proc copy*(p: JsonNode): JsonNode = # ------------- pretty printing ---------------------------------------------- proc indent(s: var string, i: int) = - s.add(repeatChar(i)) + s.add(spaces(i)) proc newIndent(curr, indent: int, ml: bool): int = if ml: return curr + indent diff --git a/lib/pure/lexbase.nim b/lib/pure/lexbase.nim index a3a3d7b5c..23a87d9f8 100644 --- a/lib/pure/lexbase.nim +++ b/lib/pure/lexbase.nim @@ -165,5 +165,5 @@ proc getCurrentLine(L: BaseLexer, marker: bool = true): string = inc(i) add(result, "\n") if marker: - add(result, repeatChar(getColNumber(L, L.bufpos)) & "^\n") + add(result, spaces(getColNumber(L, L.bufpos)) & "^\n") diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim index b957c0cf7..b8f422c31 100644 --- a/lib/pure/parsexml.nim +++ b/lib/pure/parsexml.nim @@ -128,6 +128,7 @@ proc open*(my: var XmlParser, input: Stream, filename: string, my.kind = xmlError my.a = "" my.b = "" + my.c = "" my.options = options proc close*(my: var XmlParser) {.inline.} = @@ -138,43 +139,43 @@ proc kind*(my: XmlParser): XmlEventKind {.inline.} = ## returns the current event type for the XML parser return my.kind -proc charData*(my: XmlParser): string {.inline.} = +template charData*(my: XmlParser): string = ## returns the character data for the events: ``xmlCharData``, ## ``xmlWhitespace``, ``xmlComment``, ``xmlCData``, ``xmlSpecial`` assert(my.kind in {xmlCharData, xmlWhitespace, xmlComment, xmlCData, xmlSpecial}) - return my.a + my.a -proc elementName*(my: XmlParser): string {.inline.} = +template elementName*(my: XmlParser): string = ## returns the element name for the events: ``xmlElementStart``, ## ``xmlElementEnd``, ``xmlElementOpen`` assert(my.kind in {xmlElementStart, xmlElementEnd, xmlElementOpen}) - return my.a + my.a -proc entityName*(my: XmlParser): string {.inline.} = +template entityName*(my: XmlParser): string = ## returns the entity name for the event: ``xmlEntity`` assert(my.kind == xmlEntity) - return my.a + my.a -proc attrKey*(my: XmlParser): string {.inline.} = +template attrKey*(my: XmlParser): string = ## returns the attribute key for the event ``xmlAttribute`` assert(my.kind == xmlAttribute) - return my.a + my.a -proc attrValue*(my: XmlParser): string {.inline.} = +template attrValue*(my: XmlParser): string = ## returns the attribute value for the event ``xmlAttribute`` assert(my.kind == xmlAttribute) - return my.b + my.b -proc piName*(my: XmlParser): string {.inline.} = +template piName*(my: XmlParser): string = ## returns the processing instruction name for the event ``xmlPI`` assert(my.kind == xmlPI) - return my.a + my.a -proc piRest*(my: XmlParser): string {.inline.} = +template piRest*(my: XmlParser): string = ## returns the rest of the processing instruction for the event ``xmlPI`` assert(my.kind == xmlPI) - return my.b + my.b proc rawData*(my: XmlParser): string {.inline.} = ## returns the underlying 'data' string by reference. @@ -446,13 +447,15 @@ proc parseTag(my: var XmlParser) = # an attribute follows: my.kind = xmlElementOpen my.state = stateAttr - my.c = my.a # save for later + # save for later: + my.c.setLen(my.a.len) + my.c[0..my.c.high] = my.a[0..my.a.high] else: my.kind = xmlElementStart if my.buf[my.bufpos] == '/' and my.buf[my.bufpos+1] == '>': inc(my.bufpos, 2) my.state = stateEmptyElementTag - my.c = nil + my.c.setLen(0) elif my.buf[my.bufpos] == '>': inc(my.bufpos) else: @@ -621,8 +624,9 @@ proc next*(my: var XmlParser) = of stateEmptyElementTag: my.state = stateNormal my.kind = xmlElementEnd - if not isNil(my.c): - my.a = my.c + if my.c.len > 0: + my.a.setLen(my.c.len) + my.a[0..my.a.high] = my.c[0..my.c.high] of stateError: my.kind = xmlError my.state = stateNormal diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 419554de5..04c75ecae 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -29,7 +29,7 @@ when useUnicode: const InlineThreshold = 5 ## number of leaves; -1 to disable inlining MaxSubpatterns* = 20 ## defines the maximum number of subpatterns that - ## can be captured. More subpatterns cannot be captured! + ## can be captured. More subpatterns cannot be captured! type PegKind = enum @@ -85,14 +85,14 @@ type of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns] else: sons: seq[TNode] NonTerminal* = ref NonTerminalObj - + Peg* = TNode ## type that represents a PEG {.deprecated: [TPeg: Peg].} proc term*(t: string): Peg {.nosideEffect, rtl, extern: "npegs$1Str".} = ## constructs a PEG from a terminal string - if t.len != 1: + if t.len != 1: result.kind = pkTerminal result.term = t else: @@ -116,7 +116,7 @@ proc term*(t: char): Peg {.nosideEffect, rtl, extern: "npegs$1Char".} = assert t != '\0' result.kind = pkChar result.ch = t - + proc charSet*(s: set[char]): Peg {.nosideEffect, rtl, extern: "npegs$1".} = ## constructs a PEG from a character set `s` assert '\0' notin s @@ -129,12 +129,12 @@ proc add(d: var Peg, s: Peg) {.inline.} = add(d.sons, s) proc addChoice(dest: var Peg, elem: Peg) = var L = dest.len-1 - if L >= 0 and dest.sons[L].kind == pkCharChoice: + if L >= 0 and dest.sons[L].kind == pkCharChoice: # caution! Do not introduce false aliasing here! case elem.kind of pkCharChoice: dest.sons[L] = charSet(dest.sons[L].charChoice[] + elem.charChoice[]) - of pkChar: + of pkChar: dest.sons[L] = charSet(dest.sons[L].charChoice[] + {elem.ch}) else: add(dest, elem) else: add(dest, elem) @@ -158,12 +158,12 @@ proc `/`*(a: varargs[Peg]): Peg {. proc addSequence(dest: var Peg, elem: Peg) = var L = dest.len-1 - if L >= 0 and dest.sons[L].kind == pkTerminal: + if L >= 0 and dest.sons[L].kind == pkTerminal: # caution! Do not introduce false aliasing here! case elem.kind - of pkTerminal: + of pkTerminal: dest.sons[L] = term(dest.sons[L].term & elem.term) - of pkChar: + of pkChar: dest.sons[L] = term(dest.sons[L].term & elem.ch) else: add(dest, elem) else: add(dest, elem) @@ -172,7 +172,7 @@ proc sequence*(a: varargs[Peg]): Peg {. nosideEffect, rtl, extern: "npegs$1".} = ## constructs a sequence with all the PEGs from `a` multipleOp(pkSequence, addSequence) - + proc `?`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsOptional".} = ## constructs an optional for the PEG `a` if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar, @@ -207,7 +207,7 @@ proc `!*`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsSearch".} = result.kind = pkSearch result.sons = @[a] -proc `!*\`*(a: Peg): Peg {.noSideEffect, rtl, +proc `!*\`*(a: Peg): Peg {.noSideEffect, rtl, extern: "npgegsCapturedSearch".} = ## constructs a "captured search" for the PEG `a` result.kind = pkCapturedSearch @@ -216,7 +216,7 @@ proc `!*\`*(a: Peg): Peg {.noSideEffect, rtl, proc `+`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsGreedyPosRep".} = ## constructs a "greedy positive repetition" with the PEG `a` return sequence(a, *a) - + proc `&`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsAndPredicate".} = ## constructs an "and predicate" with the PEG `a` result.kind = pkAndPredicate @@ -239,33 +239,33 @@ proc newLine*: Peg {.inline.} = ## constructs the PEG `newline`:idx: (``\n``) result.kind = pkNewLine -proc unicodeLetter*: Peg {.inline.} = +proc unicodeLetter*: Peg {.inline.} = ## constructs the PEG ``\letter`` which matches any Unicode letter. result.kind = pkLetter - -proc unicodeLower*: Peg {.inline.} = + +proc unicodeLower*: Peg {.inline.} = ## constructs the PEG ``\lower`` which matches any Unicode lowercase letter. - result.kind = pkLower + result.kind = pkLower -proc unicodeUpper*: Peg {.inline.} = +proc unicodeUpper*: Peg {.inline.} = ## constructs the PEG ``\upper`` which matches any Unicode uppercase letter. result.kind = pkUpper - -proc unicodeTitle*: Peg {.inline.} = + +proc unicodeTitle*: Peg {.inline.} = ## constructs the PEG ``\title`` which matches any Unicode title letter. result.kind = pkTitle -proc unicodeWhitespace*: Peg {.inline.} = - ## constructs the PEG ``\white`` which matches any Unicode +proc unicodeWhitespace*: Peg {.inline.} = + ## constructs the PEG ``\white`` which matches any Unicode ## whitespace character. result.kind = pkWhitespace -proc startAnchor*: Peg {.inline.} = - ## constructs the PEG ``^`` which matches the start of the input. +proc startAnchor*: Peg {.inline.} = + ## constructs the PEG ``^`` which matches the start of the input. result.kind = pkStartAnchor -proc endAnchor*: Peg {.inline.} = - ## constructs the PEG ``$`` which matches the end of the input. +proc endAnchor*: Peg {.inline.} = + ## constructs the PEG ``$`` which matches the end of the input. result = !any() proc capture*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsCapture".} = @@ -274,21 +274,21 @@ proc capture*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsCapture".} = result.sons = @[a] proc backref*(index: range[1..MaxSubpatterns]): Peg {. - nosideEffect, rtl, extern: "npegs$1".} = + nosideEffect, rtl, extern: "npegs$1".} = ## constructs a back reference of the given `index`. `index` starts counting ## from 1. result.kind = pkBackRef result.index = index-1 proc backrefIgnoreCase*(index: range[1..MaxSubpatterns]): Peg {. - nosideEffect, rtl, extern: "npegs$1".} = + nosideEffect, rtl, extern: "npegs$1".} = ## constructs a back reference of the given `index`. `index` starts counting ## from 1. Ignores case for matching. result.kind = pkBackRefIgnoreCase result.index = index-1 proc backrefIgnoreStyle*(index: range[1..MaxSubpatterns]): Peg {. - nosideEffect, rtl, extern: "npegs$1".}= + nosideEffect, rtl, extern: "npegs$1".}= ## constructs a back reference of the given `index`. `index` starts counting ## from 1. Ignores style for matching. result.kind = pkBackRefIgnoreStyle @@ -298,7 +298,7 @@ proc spaceCost(n: Peg): int = case n.kind of pkEmpty: discard of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, - pkGreedyRepChar, pkCharChoice, pkGreedyRepSet, + pkGreedyRepChar, pkCharChoice, pkGreedyRepSet, pkAny..pkWhitespace, pkGreedyAny: result = 1 of pkNonTerminal: @@ -310,7 +310,7 @@ proc spaceCost(n: Peg): int = if result >= InlineThreshold: break proc nonterminal*(n: NonTerminal): Peg {. - nosideEffect, rtl, extern: "npegs$1".} = + nosideEffect, rtl, extern: "npegs$1".} = ## constructs a PEG that consists of the nonterminal symbol assert n != nil if ntDeclared in n.flags and spaceCost(n.rule) < InlineThreshold: @@ -331,7 +331,7 @@ proc newNonTerminal*(name: string, line, column: int): NonTerminal {. template letters*: expr = ## expands to ``charset({'A'..'Z', 'a'..'z'})`` charSet({'A'..'Z', 'a'..'z'}) - + template digits*: expr = ## expands to ``charset({'0'..'9'})`` charSet({'0'..'9'}) @@ -339,11 +339,11 @@ template digits*: expr = template whitespace*: expr = ## expands to ``charset({' ', '\9'..'\13'})`` charSet({' ', '\9'..'\13'}) - + template identChars*: expr = ## expands to ``charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})`` charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'}) - + template identStartChars*: expr = ## expands to ``charset({'A'..'Z', 'a'..'z', '_'})`` charSet({'a'..'z', 'A'..'Z', '_'}) @@ -352,14 +352,14 @@ template ident*: expr = ## same as ``[a-zA-Z_][a-zA-z_0-9]*``; standard identifier sequence(charSet({'a'..'z', 'A'..'Z', '_'}), *charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'})) - + template natural*: expr = ## same as ``\d+`` +digits # ------------------------- debugging ----------------------------------------- -proc esc(c: char, reserved = {'\0'..'\255'}): string = +proc esc(c: char, reserved = {'\0'..'\255'}): string = case c of '\b': result = "\\b" of '\t': result = "\\t" @@ -374,38 +374,38 @@ proc esc(c: char, reserved = {'\0'..'\255'}): string = elif c < ' ' or c >= '\128': result = '\\' & $ord(c) elif c in reserved: result = '\\' & c else: result = $c - + proc singleQuoteEsc(c: char): string = return "'" & esc(c, {'\''}) & "'" -proc singleQuoteEsc(str: string): string = +proc singleQuoteEsc(str: string): string = result = "'" for c in items(str): add result, esc(c, {'\''}) add result, '\'' - -proc charSetEscAux(cc: set[char]): string = + +proc charSetEscAux(cc: set[char]): string = const reserved = {'^', '-', ']'} result = "" var c1 = 0 - while c1 <= 0xff: - if chr(c1) in cc: + while c1 <= 0xff: + if chr(c1) in cc: var c2 = c1 while c2 < 0xff and chr(succ(c2)) in cc: inc(c2) - if c1 == c2: + if c1 == c2: add result, esc(chr(c1), reserved) - elif c2 == succ(c1): + elif c2 == succ(c1): add result, esc(chr(c1), reserved) & esc(chr(c2), reserved) - else: + else: add result, esc(chr(c1), reserved) & '-' & esc(chr(c2), reserved) c1 = c2 inc(c1) - + proc charSetEsc(cc: set[char]): string = - if card(cc) >= 128+64: + if card(cc) >= 128+64: result = "[^" & charSetEscAux({'\1'..'\xFF'} - cc) & ']' - else: + else: result = '[' & charSetEscAux(cc) & ']' - -proc toStrAux(r: Peg, res: var string) = + +proc toStrAux(r: Peg, res: var string) = case r.kind of pkEmpty: add(res, "()") of pkAny: add(res, '.') @@ -469,25 +469,25 @@ proc toStrAux(r: Peg, res: var string) = toStrAux(r.sons[0], res) of pkCapture: add(res, '{') - toStrAux(r.sons[0], res) + toStrAux(r.sons[0], res) add(res, '}') - of pkBackRef: + of pkBackRef: add(res, '$') add(res, $r.index) - of pkBackRefIgnoreCase: + of pkBackRefIgnoreCase: add(res, "i$") add(res, $r.index) - of pkBackRefIgnoreStyle: + of pkBackRefIgnoreStyle: add(res, "y$") add(res, $r.index) of pkRule: - toStrAux(r.sons[0], res) + toStrAux(r.sons[0], res) add(res, " <- ") toStrAux(r.sons[1], res) of pkList: for i in 0 .. high(r.sons): toStrAux(r.sons[i], res) - add(res, "\n") + add(res, "\n") of pkStartAnchor: add(res, '^') @@ -506,8 +506,8 @@ type {.deprecated: [TCaptures: Captures].} -proc bounds*(c: Captures, - i: range[0..MaxSubpatterns-1]): tuple[first, last: int] = +proc bounds*(c: Captures, + i: range[0..MaxSubpatterns-1]): tuple[first, last: int] = ## returns the bounds ``[first..last]`` of the `i`'th capture. result = c.matches[i] @@ -527,7 +527,7 @@ when not useUnicode: proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {. nosideEffect, rtl, extern: "npegs$1".} = - ## low-level matching proc that implements the PEG interpreter. Use this + ## low-level matching proc that implements the PEG interpreter. Use this ## for maximum efficiency (every other PEG operation ends up calling this ## proc). ## Returns -1 if it does not match, else the length of the match @@ -541,7 +541,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {. result = runeLenAt(s, start) else: result = -1 - of pkLetter: + of pkLetter: if s[start] != '\0': var a: Rune result = start @@ -550,7 +550,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {. else: result = -1 else: result = -1 - of pkLower: + of pkLower: if s[start] != '\0': var a: Rune result = start @@ -559,7 +559,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {. else: result = -1 else: result = -1 - of pkUpper: + of pkUpper: if s[start] != '\0': var a: Rune result = start @@ -568,16 +568,16 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {. else: result = -1 else: result = -1 - of pkTitle: + of pkTitle: if s[start] != '\0': var a: Rune result = start fastRuneAt(s, result, a) - if isTitle(a): dec(result, start) + if isTitle(a): dec(result, start) else: result = -1 else: result = -1 - of pkWhitespace: + of pkWhitespace: if s[start] != '\0': var a: Rune result = start @@ -641,7 +641,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {. when false: echo "leave: ", p.nt.name if result < 0: c.ml = oldMl of pkSequence: - var oldMl = c.ml + var oldMl = c.ml result = 0 for i in 0..high(p.sons): var x = rawMatch(s, p.sons[i], start+result, c) @@ -723,11 +723,11 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {. #else: silently ignore the capture else: c.ml = idx - of pkBackRef..pkBackRefIgnoreStyle: + of pkBackRef..pkBackRefIgnoreStyle: if p.index >= c.ml: return -1 var (a, b) = c.matches[p.index] var n: Peg - n.kind = succ(pkTerminal, ord(p.kind)-ord(pkBackRef)) + n.kind = succ(pkTerminal, ord(p.kind)-ord(pkBackRef)) n.term = s.substr(a, b) result = rawMatch(s, n, start, c) of pkStartAnchor: @@ -755,7 +755,7 @@ proc match*(s: string, pattern: Peg, matches: var openArray[string], result = rawMatch(s, pattern, start, c) == len(s) - start if result: fillMatches(s, matches, c) -proc match*(s: string, pattern: Peg, +proc match*(s: string, pattern: Peg, start = 0): bool {.nosideEffect, rtl, extern: "npegs$1".} = ## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``. var c: Captures @@ -773,7 +773,7 @@ proc matchLen*(s: string, pattern: Peg, matches: var openArray[string], result = rawMatch(s, pattern, start, c) if result >= 0: fillMatches(s, matches, c) -proc matchLen*(s: string, pattern: Peg, +proc matchLen*(s: string, pattern: Peg, start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} = ## the same as ``match``, but it returns the length of the match, ## if there is no match, -1 is returned. Note that a match length @@ -797,11 +797,11 @@ proc find*(s: string, pattern: Peg, matches: var openArray[string], return i return -1 # could also use the pattern here: (!P .)* P - + proc findBounds*(s: string, pattern: Peg, matches: var openArray[string], start = 0): tuple[first, last: int] {. nosideEffect, rtl, extern: "npegs$1Capture".} = - ## returns the starting position and end position of ``pattern`` in ``s`` + ## returns the starting position and end position of ``pattern`` in ``s`` ## and the captured ## substrings in the array ``matches``. If it does not match, nothing ## is written into ``matches`` and (-1,0) is returned. @@ -814,8 +814,8 @@ proc findBounds*(s: string, pattern: Peg, matches: var openArray[string], fillMatches(s, matches, c) return (i, i+L-1) return (-1, 0) - -proc find*(s: string, pattern: Peg, + +proc find*(s: string, pattern: Peg, start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} = ## returns the starting position of ``pattern`` in ``s``. If it does not ## match, -1 is returned. @@ -824,8 +824,8 @@ proc find*(s: string, pattern: Peg, for i in start .. s.len-1: if rawMatch(s, pattern, i, c) >= 0: return i return -1 - -iterator findAll*(s: string, pattern: Peg, start = 0): string = + +iterator findAll*(s: string, pattern: Peg, start = 0): string = ## yields all matching *substrings* of `s` that match `pattern`. var c: Captures c.origStart = start @@ -838,23 +838,23 @@ iterator findAll*(s: string, pattern: Peg, start = 0): string = else: yield substr(s, i, i+L-1) inc(i, L) - + proc findAll*(s: string, pattern: Peg, start = 0): seq[string] {. - nosideEffect, rtl, extern: "npegs$1".} = + nosideEffect, rtl, extern: "npegs$1".} = ## returns all matching *substrings* of `s` that match `pattern`. ## If it does not match, @[] is returned. accumulateResult(findAll(s, pattern, start)) when not defined(nimhygiene): {.pragma: inject.} - + template `=~`*(s: string, pattern: Peg): bool = - ## This calls ``match`` with an implicit declared ``matches`` array that - ## can be used in the scope of the ``=~`` call: - ## + ## This calls ``match`` with an implicit declared ``matches`` array that + ## can be used in the scope of the ``=~`` call: + ## ## .. code-block:: nim ## - ## if line =~ peg"\s* {\w+} \s* '=' \s* {\w+}": + ## if line =~ peg"\s* {\w+} \s* '=' \s* {\w+}": ## # matches a key=value pair: ## echo("Key: ", matches[0]) ## echo("Value: ", matches[1]) @@ -865,7 +865,7 @@ template `=~`*(s: string, pattern: Peg): bool = ## echo("comment: ", matches[0]) ## else: ## echo("syntax error") - ## + ## bind MaxSubpatterns when not declaredInScope(matches): var matches {.inject.}: array[0..MaxSubpatterns-1, string] @@ -902,7 +902,7 @@ proc replacef*(s: string, sub: Peg, by: string): string {. ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples: ## ## .. code-block:: nim - ## "var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2") + ## "var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2") ## ## Results in: ## @@ -941,10 +941,10 @@ proc replace*(s: string, sub: Peg, by = ""): string {. add(result, by) inc(i, x) add(result, substr(s, i)) - + proc parallelReplace*(s: string, subs: varargs[ tuple[pattern: Peg, repl: string]]): string {. - nosideEffect, rtl, extern: "npegs$1".} = + nosideEffect, rtl, extern: "npegs$1".} = ## Returns a modified copy of `s` with the substitutions in `subs` ## applied in parallel. result = "" @@ -964,8 +964,8 @@ proc parallelReplace*(s: string, subs: varargs[ add(result, s[i]) inc(i) # copy the rest: - add(result, substr(s, i)) - + add(result, substr(s, i)) + proc transformFile*(infile, outfile: string, subs: varargs[tuple[pattern: Peg, repl: string]]) {. rtl, extern: "npegs$1".} = @@ -974,7 +974,7 @@ proc transformFile*(infile, outfile: string, ## error occurs. This is supposed to be used for quick scripting. var x = readFile(infile).string writeFile(outfile, x.parallelReplace(subs)) - + iterator split*(s: string, sep: Peg): string = ## Splits the string `s` into substrings. ## @@ -1049,14 +1049,14 @@ type tkBackref, ## '$' tkDollar, ## '$' tkHat ## '^' - + TToken {.final.} = object ## a token kind: TTokKind ## the type of the token modifier: TModifier literal: string ## the parsed (string) literal charset: set[char] ## if kind == tkCharSet index: int ## if kind == tkBackref - + PegLexer {.inheritable.} = object ## the lexer object. bufpos: int ## the current position within the buffer buf: cstring ## the buffer itself @@ -1086,7 +1086,7 @@ proc handleLF(L: var PegLexer, pos: int): int = result = pos+1 L.lineStart = result -proc init(L: var PegLexer, input, filename: string, line = 1, col = 0) = +proc init(L: var PegLexer, input, filename: string, line = 1, col = 0) = L.buf = input L.bufpos = 0 L.lineNumber = line @@ -1094,69 +1094,69 @@ proc init(L: var PegLexer, input, filename: string, line = 1, col = 0) = L.lineStart = 0 L.filename = filename -proc getColumn(L: PegLexer): int {.inline.} = +proc getColumn(L: PegLexer): int {.inline.} = result = abs(L.bufpos - L.lineStart) + L.colOffset -proc getLine(L: PegLexer): int {.inline.} = +proc getLine(L: PegLexer): int {.inline.} = result = L.lineNumber - + proc errorStr(L: PegLexer, msg: string, line = -1, col = -1): string = var line = if line < 0: getLine(L) else: line var col = if col < 0: getColumn(L) else: col result = "$1($2, $3) Error: $4" % [L.filename, $line, $col, msg] -proc handleHexChar(c: var PegLexer, xi: var int) = +proc handleHexChar(c: var PegLexer, xi: var int) = case c.buf[c.bufpos] - of '0'..'9': + of '0'..'9': xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0')) inc(c.bufpos) - of 'a'..'f': + of 'a'..'f': xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10) inc(c.bufpos) - of 'A'..'F': + of 'A'..'F': xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10) inc(c.bufpos) else: discard -proc getEscapedChar(c: var PegLexer, tok: var TToken) = +proc getEscapedChar(c: var PegLexer, tok: var TToken) = inc(c.bufpos) case c.buf[c.bufpos] - of 'r', 'R', 'c', 'C': + of 'r', 'R', 'c', 'C': add(tok.literal, '\c') inc(c.bufpos) - of 'l', 'L': + of 'l', 'L': add(tok.literal, '\L') inc(c.bufpos) - of 'f', 'F': + of 'f', 'F': add(tok.literal, '\f') inc(c.bufpos) - of 'e', 'E': + of 'e', 'E': add(tok.literal, '\e') inc(c.bufpos) - of 'a', 'A': + of 'a', 'A': add(tok.literal, '\a') inc(c.bufpos) - of 'b', 'B': + of 'b', 'B': add(tok.literal, '\b') inc(c.bufpos) - of 'v', 'V': + of 'v', 'V': add(tok.literal, '\v') inc(c.bufpos) - of 't', 'T': + of 't', 'T': add(tok.literal, '\t') inc(c.bufpos) - of 'x', 'X': + of 'x', 'X': inc(c.bufpos) var xi = 0 handleHexChar(c, xi) handleHexChar(c, xi) if xi == 0: tok.kind = tkInvalid else: add(tok.literal, chr(xi)) - of '0'..'9': + of '0'..'9': var val = ord(c.buf[c.bufpos]) - ord('0') inc(c.bufpos) var i = 1 - while (i <= 3) and (c.buf[c.bufpos] in {'0'..'9'}): + while (i <= 3) and (c.buf[c.bufpos] in {'0'..'9'}): val = val * 10 + ord(c.buf[c.bufpos]) - ord('0') inc(c.bufpos) inc(i) @@ -1169,32 +1169,32 @@ proc getEscapedChar(c: var PegLexer, tok: var TToken) = else: add(tok.literal, c.buf[c.bufpos]) inc(c.bufpos) - -proc skip(c: var PegLexer) = + +proc skip(c: var PegLexer) = var pos = c.bufpos var buf = c.buf - while true: + while true: case buf[pos] - of ' ', '\t': + of ' ', '\t': inc(pos) of '#': while not (buf[pos] in {'\c', '\L', '\0'}): inc(pos) of '\c': pos = handleCR(c, pos) buf = c.buf - of '\L': + of '\L': pos = handleLF(c, pos) buf = c.buf - else: + else: break # EndOfFile also leaves the loop c.bufpos = pos - -proc getString(c: var PegLexer, tok: var TToken) = + +proc getString(c: var PegLexer, tok: var TToken) = tok.kind = tkStringLit var pos = c.bufpos + 1 var buf = c.buf var quote = buf[pos-1] - while true: + while true: case buf[pos] of '\\': c.bufpos = pos @@ -1205,13 +1205,13 @@ proc getString(c: var PegLexer, tok: var TToken) = break elif buf[pos] == quote: inc(pos) - break + break else: add(tok.literal, buf[pos]) inc(pos) c.bufpos = pos - -proc getDollar(c: var PegLexer, tok: var TToken) = + +proc getDollar(c: var PegLexer, tok: var TToken) = var pos = c.bufpos + 1 var buf = c.buf if buf[pos] in {'0'..'9'}: @@ -1223,8 +1223,8 @@ proc getDollar(c: var PegLexer, tok: var TToken) = else: tok.kind = tkDollar c.bufpos = pos - -proc getCharSet(c: var PegLexer, tok: var TToken) = + +proc getCharSet(c: var PegLexer, tok: var TToken) = tok.kind = tkCharSet tok.charset = {} var pos = c.bufpos + 1 @@ -1247,7 +1247,7 @@ proc getCharSet(c: var PegLexer, tok: var TToken) = of '\C', '\L', '\0': tok.kind = tkInvalid break - else: + else: ch = buf[pos] inc(pos) incl(tok.charset, ch) @@ -1267,18 +1267,18 @@ proc getCharSet(c: var PegLexer, tok: var TToken) = of '\C', '\L', '\0': tok.kind = tkInvalid break - else: + else: ch2 = buf[pos] inc(pos) for i in ord(ch)+1 .. ord(ch2): incl(tok.charset, chr(i)) c.bufpos = pos if caret: tok.charset = {'\1'..'\xFF'} - tok.charset - -proc getSymbol(c: var PegLexer, tok: var TToken) = + +proc getSymbol(c: var PegLexer, tok: var TToken) = var pos = c.bufpos var buf = c.buf - while true: + while true: add(tok.literal, buf[pos]) inc(pos) if buf[pos] notin strutils.IdentChars: break @@ -1294,7 +1294,7 @@ proc getBuiltin(c: var PegLexer, tok: var TToken) = tok.kind = tkEscaped getEscapedChar(c, tok) # may set tok.kind to tkInvalid -proc getTok(c: var PegLexer, tok: var TToken) = +proc getTok(c: var PegLexer, tok: var TToken) = tok.kind = tkInvalid tok.modifier = modNone setLen(tok.literal, 0) @@ -1309,11 +1309,11 @@ proc getTok(c: var PegLexer, tok: var TToken) = else: tok.kind = tkCurlyLe add(tok.literal, '{') - of '}': + of '}': tok.kind = tkCurlyRi inc(c.bufpos) add(tok.literal, '}') - of '[': + of '[': getCharSet(c, tok) of '(': tok.kind = tkParLe @@ -1323,7 +1323,7 @@ proc getTok(c: var PegLexer, tok: var TToken) = tok.kind = tkParRi inc(c.bufpos) add(tok.literal, ')') - of '.': + of '.': tok.kind = tkAny inc(c.bufpos) add(tok.literal, '.') @@ -1331,16 +1331,16 @@ proc getTok(c: var PegLexer, tok: var TToken) = tok.kind = tkAnyRune inc(c.bufpos) add(tok.literal, '_') - of '\\': + of '\\': getBuiltin(c, tok) of '\'', '"': getString(c, tok) of '$': getDollar(c, tok) - of '\0': + of '\0': tok.kind = tkEof tok.literal = "[EOF]" of 'a'..'z', 'A'..'Z', '\128'..'\255': getSymbol(c, tok) - if c.buf[c.bufpos] in {'\'', '"'} or + if c.buf[c.bufpos] in {'\'', '"'} or c.buf[c.bufpos] == '$' and c.buf[c.bufpos+1] in {'0'..'9'}: case tok.literal of "i": tok.modifier = modIgnoreCase @@ -1388,7 +1388,7 @@ proc getTok(c: var PegLexer, tok: var TToken) = tok.kind = tkAt inc(c.bufpos) add(tok.literal, '@') - if c.buf[c.bufpos] == '@': + if c.buf[c.bufpos] == '@': tok.kind = tkCurlyAt inc(c.bufpos) add(tok.literal, '@') @@ -1407,7 +1407,7 @@ proc arrowIsNextTok(c: PegLexer): bool = result = c.buf[pos] == '<' and c.buf[pos+1] == '-' # ----------------------------- parser ---------------------------------------- - + type EInvalidPeg* = object of ValueError ## raised if an invalid ## PEG has been detected @@ -1425,7 +1425,7 @@ proc pegError(p: PegParser, msg: string, line = -1, col = -1) = e.msg = errorStr(p, msg, line, col) raise e -proc getTok(p: var PegParser) = +proc getTok(p: var PegParser) = getTok(p, p.tok) if p.tok.kind == tkInvalid: pegError(p, "invalid token") @@ -1475,7 +1475,7 @@ proc builtin(p: var PegParser): Peg = of "white": result = unicodeWhitespace() else: pegError(p, "unknown built-in: " & p.tok.literal) -proc token(terminal: Peg, p: PegParser): Peg = +proc token(terminal: Peg, p: PegParser): Peg = if p.skip.kind == pkEmpty: result = terminal else: result = sequence(p.skip, terminal) @@ -1496,7 +1496,7 @@ proc primary(p: var PegParser): Peg = else: discard case p.tok.kind of tkIdentifier: - if p.identIsVerbatim: + if p.identIsVerbatim: var m = p.tok.modifier if m == modNone: m = p.modifier result = modifiedTerm(p.tok.literal, m).token(p) @@ -1539,17 +1539,17 @@ proc primary(p: var PegParser): Peg = of tkEscaped: result = term(p.tok.literal[0]).token(p) getTok(p) - of tkDollar: + of tkDollar: result = endAnchor() getTok(p) - of tkHat: + of tkHat: result = startAnchor() getTok(p) of tkBackref: var m = p.tok.modifier if m == modNone: m = p.modifier result = modifiedBackref(p.tok.index, m).token(p) - if p.tok.index < 0 or p.tok.index > p.captures: + if p.tok.index < 0 or p.tok.index > p.captures: pegError(p, "invalid back reference index: " & $p.tok.index) getTok(p) else: @@ -1573,7 +1573,7 @@ proc seqExpr(p: var PegParser): Peg = while true: case p.tok.kind of tkAmp, tkNot, tkAt, tkStringLit, tkCharSet, tkParLe, tkCurlyLe, - tkAny, tkAnyRune, tkBuiltin, tkEscaped, tkDollar, tkBackref, + tkAny, tkAnyRune, tkBuiltin, tkEscaped, tkDollar, tkBackref, tkHat, tkCurlyAt: result = sequence(result, primary(p)) of tkIdentifier: @@ -1587,7 +1587,7 @@ proc parseExpr(p: var PegParser): Peg = while p.tok.kind == tkBar: getTok(p) result = result / seqExpr(p) - + proc parseRule(p: var PegParser): NonTerminal = if p.tok.kind == tkIdentifier and arrowIsNextTok(p): result = getNonTerminal(p, p.tok.literal) @@ -1601,7 +1601,7 @@ proc parseRule(p: var PegParser): NonTerminal = incl(result.flags, ntDeclared) # NOW inlining may be attempted else: pegError(p, "rule expected, but found: " & p.tok.literal) - + proc rawParse(p: var PegParser): Peg = ## parses a rule or a PEG expression while p.tok.kind == tkBuiltin: @@ -1680,7 +1680,7 @@ when isMainModule: assert(not match("W_HI_L", peg"\y 'while'")) assert(not match("W_HI_Le", peg"\y v'while'")) assert match("W_HI_Le", peg"y'while'") - + assert($ +digits == $peg"\d+") assert "0158787".match(peg"\d+") assert "ABC 0232".match(peg"\w+\s+\d+") @@ -1693,14 +1693,14 @@ when isMainModule: var pattern = sequence(ident, *whitespace, term('='), *whitespace, ident) assert matchLen("key1= cal9", pattern) == 11 - + var ws = newNonTerminal("ws", 1, 1) ws.rule = *whitespace - + var expr = newNonTerminal("expr", 1, 1) expr.rule = sequence(capture(ident), *sequence( nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr))) - + var c: Captures var s = "a+b + c +d+e+f" assert rawMatch(s, expr.rule, 0, c) == len(s) @@ -1722,7 +1722,7 @@ when isMainModule: assert matches[0] == "abc" else: assert false - + var g2 = peg"""S <- A B / C D A <- 'a'+ B <- 'b'+ @@ -1753,13 +1753,13 @@ when isMainModule: for x in findAll("abcdef", peg"^{.}", 3): assert x == "d" - + if "f(a, b)" =~ peg"{[0-9]+} / ({\ident} '(' {@} ')')": assert matches[0] == "f" assert matches[1] == "a, b" else: assert false - + assert match("eine übersicht und außerdem", peg"(\letter \white*)+") # ß is not a lower cased letter?! assert match("eine übersicht und auerdem", peg"(\lower \white*)+") diff --git a/lib/pure/redis.nim b/lib/pure/redis.nim index 52d81b3a4..64d3e1470 100644 --- a/lib/pure/redis.nim +++ b/lib/pure/redis.nim @@ -798,6 +798,22 @@ proc zunionstore*(r: Redis, destination: string, numkeys: string, return r.readInteger() +# HyperLogLog + +proc pfadd*(r: Redis, key: string, elements: varargs[string]): RedisInteger = + ## Add variable number of elements into special 'HyperLogLog' set type + r.sendCommand("PFADD", key, elements) + return r.readInteger() + +proc pfcount*(r: Redis, key: string): RedisInteger = + ## Count approximate number of elements in 'HyperLogLog' + r.sendCommand("PFCOUNT", key) + return r.readInteger() + +proc pfmerge*(r: Redis, destination: string, sources: varargs[string]) = + ## Merge several source HyperLogLog's into one specified by destKey + r.sendCommand("PFMERGE", destination, sources) + raiseNoOK(r.readStatus(), r.pipeline.enabled) # Pub/Sub diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index 67c80e592..e706f2016 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -224,10 +224,12 @@ proc ssReadData(s: Stream, buffer: pointer, bufLen: int): int = proc ssWriteData(s: Stream, buffer: pointer, bufLen: int) = var s = StringStream(s) - if bufLen > 0: - setLen(s.data, s.data.len + bufLen) - copyMem(addr(s.data[s.pos]), buffer, bufLen) - inc(s.pos, bufLen) + if bufLen <= 0: + return + if s.pos + bufLen > s.data.len: + setLen(s.data, s.pos + bufLen) + copyMem(addr(s.data[s.pos]), buffer, bufLen) + inc(s.pos, bufLen) proc ssClose(s: Stream) = var s = StringStream(s) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index e0e2aa247..df637dcb6 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -45,7 +45,6 @@ when defined(windows): var oldAttr = getAttributes() - proc winGetch(): cint {.header: "<conio.h>", importc: "_getch".} else: import termios, unsigned @@ -344,7 +343,7 @@ proc isatty*(f: File): bool = else: proc isatty(fildes: FileHandle): cint {. importc: "_isatty", header: "<io.h>".} - + result = isatty(getFileHandle(f)) != 0'i32 proc styledEchoProcessArg(s: string) = write stdout, s @@ -364,12 +363,11 @@ macro styledEcho*(m: varargs[expr]): stmt = result.add(newCall(bindSym"write", bindSym"stdout", newStrLitNode("\n"))) result.add(newCall(bindSym"resetAttributes")) -proc getch*(): char = - ## Read a single character from the terminal, blocking until it is entered. - ## The character is not printed to the terminal. - when defined(windows): - result = winGetch().char - else: +when not defined(windows): + proc getch*(): char = + ## Read a single character from the terminal, blocking until it is entered. + ## The character is not printed to the terminal. This is not available for + ## Windows. let fd = getFileHandle(stdin) var oldMode: Termios discard fd.tcgetattr(addr oldMode) @@ -387,5 +385,5 @@ when isMainModule: setForeGroundColor(fgBlue) writeln(stdout, "ordinary text") - styledEcho("styled text ", {styleBright, styleBlink, styleUnderscore}) - + styledEcho("styled text ", {styleBright, styleBlink, styleUnderscore}) + diff --git a/lib/pure/times.nim b/lib/pure/times.nim index e32ea786a..c39667611 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -628,25 +628,25 @@ proc formatToken(info: TimeInfo, token: string, buf: var string) = var fr = ($info.year).len()-2 if fr < 0: fr = 0 var fyear = ($info.year)[fr .. ($info.year).len()-1] - if fyear.len != 2: fyear = repeatChar(2-fyear.len(), '0') & fyear + if fyear.len != 2: fyear = repeat('0', 2-fyear.len()) & fyear buf.add(fyear) of "yyy": var fr = ($info.year).len()-3 if fr < 0: fr = 0 var fyear = ($info.year)[fr .. ($info.year).len()-1] - if fyear.len != 3: fyear = repeatChar(3-fyear.len(), '0') & fyear + if fyear.len != 3: fyear = repeat('0', 3-fyear.len()) & fyear buf.add(fyear) of "yyyy": var fr = ($info.year).len()-4 if fr < 0: fr = 0 var fyear = ($info.year)[fr .. ($info.year).len()-1] - if fyear.len != 4: fyear = repeatChar(4-fyear.len(), '0') & fyear + if fyear.len != 4: fyear = repeat('0', 4-fyear.len()) & fyear buf.add(fyear) of "yyyyy": var fr = ($info.year).len()-5 if fr < 0: fr = 0 var fyear = ($info.year)[fr .. ($info.year).len()-1] - if fyear.len != 5: fyear = repeatChar(5-fyear.len(), '0') & fyear + if fyear.len != 5: fyear = repeat('0', 5-fyear.len()) & fyear buf.add(fyear) of "z": let hrs = (info.timezone div 60) div 60 diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index 42e6a3195..a6f8f916b 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -118,21 +118,35 @@ proc toUTF8*(c: Rune): string {.rtl, extern: "nuc$1".} = elif i <=% 0x07FF: result = newString(2) result[0] = chr((i shr 6) or 0b110_00000) - result[1] = chr((i and ones(6)) or 0b10_000000) + result[1] = chr((i and ones(6)) or 0b10_0000_00) elif i <=% 0xFFFF: result = newString(3) result[0] = chr(i shr 12 or 0b1110_0000) result[1] = chr(i shr 6 and ones(6) or 0b10_0000_00) result[2] = chr(i and ones(6) or 0b10_0000_00) - elif i <=% 0x0010FFFF: + elif i <=% 0x001FFFFF: result = newString(4) result[0] = chr(i shr 18 or 0b1111_0000) result[1] = chr(i shr 12 and ones(6) or 0b10_0000_00) result[2] = chr(i shr 6 and ones(6) or 0b10_0000_00) result[3] = chr(i and ones(6) or 0b10_0000_00) + elif i <=% 0x03FFFFFF: + result = newString(5) + result[0] = chr(i shr 24 or 0b111110_00) + result[1] = chr(i shr 18 and ones(6) or 0b10_0000_00) + result[2] = chr(i shr 12 and ones(6) or 0b10_0000_00) + result[3] = chr(i shr 6 and ones(6) or 0b10_0000_00) + result[4] = chr(i and ones(6) or 0b10_0000_00) + elif i <=% 0x7FFFFFFF: + result = newString(6) + result[0] = chr(i shr 30 or 0b1111110_0) + result[1] = chr(i shr 24 and ones(6) or 0b10_0000_00) + result[2] = chr(i shr 18 and ones(6) or 0b10_0000_00) + result[3] = chr(i shr 12 and ones(6) or 0b10_0000_00) + result[4] = chr(i shr 6 and ones(6) or 0b10_0000_00) + result[5] = chr(i and ones(6) or 0b10_0000_00) else: - result = newString(1) - result[0] = chr(i) + discard # error, exception? proc `$`*(rune: Rune): string = ## converts a rune to a string diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index 9a6e273a8..b0afb75f9 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -286,6 +286,16 @@ proc `$`*(u: Uri): string = when isMainModule: block: + let str = "http://localhost" + let test = parseUri(str) + doAssert test.path == "" + + block: + let str = "http://localhost/" + let test = parseUri(str) + doAssert test.path == "/" + + block: let str = "http://localhost:8080/test" let test = parseUri(str) doAssert test.scheme == "http" diff --git a/lib/pure/xmldom.nim b/lib/pure/xmldom.nim index 2e55ff182..6242e589e 100644 --- a/lib/pure/xmldom.nim +++ b/lib/pure/xmldom.nim @@ -1083,7 +1083,7 @@ proc addEscaped(s: string): string = else: result.add(c) proc nodeToXml(n: PNode, indent: int = 0): string = - result = repeatChar(indent, ' ') & "<" & n.nodeName + result = spaces(indent) & "<" & n.nodeName if not isNil(n.attributes): for i in items(n.attributes): result.add(" " & i.name & "=\"" & addEscaped(i.value) & "\"") @@ -1098,23 +1098,23 @@ proc nodeToXml(n: PNode, indent: int = 0): string = of ElementNode: result.add(nodeToXml(i, indent + 2)) of TextNode: - result.add(repeatChar(indent * 2, ' ')) + result.add(spaces(indent * 2)) result.add(addEscaped(i.nodeValue)) of CDataSectionNode: - result.add(repeatChar(indent * 2, ' ')) + result.add(spaces(indent * 2)) result.add("<![CDATA[" & i.nodeValue & "]]>") of ProcessingInstructionNode: - result.add(repeatChar(indent * 2, ' ')) + result.add(spaces(indent * 2)) result.add("<?" & PProcessingInstruction(i).target & " " & PProcessingInstruction(i).data & " ?>") of CommentNode: - result.add(repeatChar(indent * 2, ' ')) + result.add(spaces(indent * 2)) result.add("<!-- " & i.nodeValue & " -->") else: continue result.add("\n") # Add the ending tag - </tag> - result.add(repeatChar(indent, ' ') & "</" & n.nodeName & ">") + result.add(spaces(indent) & "</" & n.nodeName & ">") proc `$`*(doc: PDocument): string = ## Converts a PDocument object into a string representation of it's XML diff --git a/lib/system.nim b/lib/system.nim index abf31c821..4180f24f9 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -89,7 +89,7 @@ type SomeOrdinal* = int|int8|int16|int32|int64|bool|enum|uint8|uint16|uint32 ## type class matching all ordinal types; however this includes enums with ## holes. - + SomeReal* = float|float32|float64 ## type class matching all floating point number types @@ -181,7 +181,7 @@ proc new*[T](a: var ref T, finalizer: proc (x: ref T) {.nimcall.}) {. ## freeing the object. Note: The `finalizer` refers to the type `T`, not to ## the object! This means that for each object of type `T` the finalizer ## will be called! - + proc reset*[T](obj: var T) {.magic: "Reset", noSideEffect.} ## resets an object `obj` to its initial (binary zero) value. This needs to ## be called before any possible `object branch transition`:idx:. @@ -348,7 +348,7 @@ type ## This field is filled automatically in the ## ``raise`` statement. msg* {.exportc: "message".}: string ## the exception's message. Not - ## providing an exception message + ## providing an exception message ## is bad style. trace: string @@ -483,7 +483,7 @@ type E_Base: Exception, ESystem: SystemError, EIO: IOError, EOS: OSError, EInvalidLibrary: LibraryError, - EResourceExhausted: ResourceExhaustedError, + EResourceExhausted: ResourceExhaustedError, EArithmetic: ArithmeticError, EDivByZero: DivByZeroError, EOverflow: OverflowError, EAccessViolation: AccessViolationError, EAssertionFailed: AssertionError, EInvalidValue: ValueError, @@ -494,7 +494,7 @@ type EInvalidObjectAssignment: ObjectAssignmentError, EInvalidObjectConversion: ObjectConversionError, EDeadThread: DeadThreadError, - EFloatInexact: FloatInexactError, + EFloatInexact: FloatInexactError, EFloatUnderflow: FloatUnderflowError, EFloatingPoint: FloatingPointError, EFloatInvalidOp: FloatInvalidOpError, @@ -511,11 +511,11 @@ proc sizeof*[T](x: T): Natural {.magic: "SizeOf", noSideEffect.} proc `<`*[T](x: Ordinal[T]): T {.magic: "UnaryLt", noSideEffect.} ## unary ``<`` that can be used for nice looking excluding ranges: - ## + ## ## .. code-block:: nim ## for i in 0 .. <10: echo i ## - ## Semantically this is the same as ``pred``. + ## Semantically this is the same as ``pred``. proc succ*[T](x: Ordinal[T], y = 1): T {.magic: "Succ", noSideEffect.} ## returns the ``y``-th successor of the value ``x``. ``T`` has to be @@ -536,7 +536,7 @@ proc dec*[T: Ordinal|uint|uint64](x: var T, y = 1) {.magic: "Dec", noSideEffect. ## decrements the ordinal ``x`` by ``y``. If such a value does not ## exist, ``EOutOfRange`` is raised or a compile time error occurs. This is a ## short notation for: ``x = pred(x, y)``. - + proc newSeq*[T](s: var seq[T], len: int) {.magic: "NewSeq", noSideEffect.} ## creates a new sequence of type ``seq[T]`` with length ``len``. ## This is equivalent to ``s = @[]; setlen(s, len)``, but more @@ -636,7 +636,7 @@ when not defined(JS): proc toU8*(x: int): int8 {.magic: "ToU8", noSideEffect.} ## treats `x` as unsigned and converts it to a byte by taking the last 8 bits - ## from `x`. + ## from `x`. proc toU16*(x: int): int16 {.magic: "ToU16", noSideEffect.} ## treats `x` as unsigned and converts it to an ``int16`` by taking the last ## 16 bits from `x`. @@ -800,7 +800,7 @@ proc `%%` *(x, y: int64): int64 {.magic: "ModU", noSideEffect.} ## The result is truncated to fit into the result. ## This implements modulo arithmetic. ## No overflow errors are possible. - + proc `<=%` *(x, y: IntMax32): bool {.magic: "LeU", noSideEffect.} proc `<=%` *(x, y: int64): bool {.magic: "LeU64", noSideEffect.} ## treats `x` and `y` as unsigned and compares them. @@ -889,7 +889,7 @@ template `notin` * (x, y: expr): expr {.immediate, dirty.} = not contains(y, x) proc `is` *[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.} ## Checks if T is of the same type as S - ## + ## ## .. code-block:: Nim ## proc test[T](a: T): int = ## when (T is int): @@ -924,7 +924,7 @@ proc cmp*(x, y: string): int {.noSideEffect, procvar.} proc `@` * [IDX, T](a: array[IDX, T]): seq[T] {. magic: "ArrToSeq", nosideeffect.} ## turns an array into a sequence. This most often useful for constructing - ## sequences with the array constructor: ``@[1, 2, 3]`` has the type + ## sequences with the array constructor: ``@[1, 2, 3]`` has the type ## ``seq[int]``, while ``[1, 2, 3]`` has the type ``array[0..2, int]``. proc setLen*[T](s: var seq[T], newlen: int) {. @@ -933,14 +933,14 @@ proc setLen*[T](s: var seq[T], newlen: int) {. ## ``T`` may be any sequence type. ## If the current length is greater than the new length, ## ``s`` will be truncated. `s` cannot be nil! To initialize a sequence with - ## a size, use ``newSeq`` instead. + ## a size, use ``newSeq`` instead. proc setLen*(s: var string, newlen: int) {. magic: "SetLengthStr", noSideEffect.} ## sets the length of `s` to `newlen`. ## If the current length is greater than the new length, ## ``s`` will be truncated. `s` cannot be nil! To initialize a string with - ## a size, use ``newString`` instead. + ## a size, use ``newString`` instead. proc newString*(len: int): string {. magic: "NewString", importc: "mnewString", noSideEffect.} @@ -953,7 +953,7 @@ proc newString*(len: int): string {. proc newStringOfCap*(cap: int): string {. magic: "NewStringOfCap", importc: "rawNewString", noSideEffect.} ## returns a new string of length ``0`` but with capacity `cap`.This - ## procedure exists only for optimization purposes; the same effect can + ## procedure exists only for optimization purposes; the same effect can ## be achieved with the ``&`` operator or with ``add``. proc `&` * (x: string, y: char): string {. @@ -982,7 +982,7 @@ proc `&` * (x: char, y: string): string {. ## assert('a' & "bc" == "abc") # implementation note: These must all have the same magic value "ConStrStr" so -# that the merge optimization works properly. +# that the merge optimization works properly. proc add*(x: var string, y: char) {.magic: "AppendStrCh", noSideEffect.} ## Appends `y` to `x` in place @@ -1039,15 +1039,15 @@ proc compileOption*(option: string): bool {. ## can be used to determine an on|off compile-time option. Example: ## ## .. code-block:: nim - ## when compileOption("floatchecks"): + ## when compileOption("floatchecks"): ## echo "compiled with floating point NaN and Inf checks" - + proc compileOption*(option, arg: string): bool {. magic: "CompileOptionArg", noSideEffect.} ## can be used to determine an enum compile-time option. Example: ## ## .. code-block:: nim - ## when compileOption("opt", "size") and compileOption("gc", "boehm"): + ## when compileOption("opt", "size") and compileOption("gc", "boehm"): ## echo "compiled with optimization for size and uses Boehm's GC" const @@ -1056,16 +1056,16 @@ const taintMode = compileOption("taintmode") when taintMode: - type TaintedString* = distinct string ## a distinct string type that + type TaintedString* = distinct string ## a distinct string type that ## is `tainted`:idx:. It is an alias for ## ``string`` if the taint mode is not ## turned on. Use the ``-d:taintMode`` ## command line switch to turn the taint ## mode on. - + proc len*(s: TaintedString): int {.borrow.} else: - type TaintedString* = string ## a distinct string type that + type TaintedString* = string ## a distinct string type that ## is `tainted`:idx:. It is an alias for ## ``string`` if the taint mode is not ## turned on. Use the ``-d:taintMode`` @@ -1136,25 +1136,25 @@ proc add *[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} = proc shallowCopy*[T](x: var T, y: T) {.noSideEffect, magic: "ShallowCopy".} ## use this instead of `=` for a `shallow copy`:idx:. The shallow copy ## only changes the semantics for sequences and strings (and types which - ## contain those). Be careful with the changed semantics though! There + ## contain those). Be careful with the changed semantics though! There ## is a reason why the default assignment does a deep copy of sequences ## and strings. -proc del*[T](x: var seq[T], i: int) {.noSideEffect.} = +proc del*[T](x: var seq[T], i: int) {.noSideEffect.} = ## deletes the item at index `i` by putting ``x[high(x)]`` into position `i`. ## This is an O(1) operation. let xl = x.len shallowCopy(x[i], x[xl-1]) setLen(x, xl-1) - -proc delete*[T](x: var seq[T], i: int) {.noSideEffect.} = + +proc delete*[T](x: var seq[T], i: int) {.noSideEffect.} = ## deletes the item at index `i` by moving ``x[i+1..]`` by one position. ## This is an O(n) operation. let xl = x.len - for j in i..xl-2: shallowCopy(x[j], x[j+1]) + for j in i..xl-2: shallowCopy(x[j], x[j+1]) setLen(x, xl-1) - -proc insert*[T](x: var seq[T], item: T, i = 0) {.noSideEffect.} = + +proc insert*[T](x: var seq[T], item: T, i = 0) {.noSideEffect.} = ## inserts `item` into `x` at position `i`. let xl = x.len setLen(x, xl+1) @@ -1233,7 +1233,7 @@ type # these work for most platforms: ## This is binary compatible to the type ``char**`` in *C*. The array's ## high value is large enough to disable bounds checking in practice. ## Use `cstringArrayToSeq` to convert it into a ``seq[string]``. - + PFloat32* = ptr float32 ## an alias for ``ptr float32`` PFloat64* = ptr float64 ## an alias for ``ptr float64`` PInt64* = ptr int64 ## an alias for ``ptr int64`` @@ -1280,7 +1280,7 @@ proc addQuitProc*(QuitProc: proc() {.noconv.}) {. proc copy*(s: string, first = 0): string {. magic: "CopyStr", importc: "copyStr", noSideEffect, deprecated.} proc copy*(s: string, first, last: int): string {. - magic: "CopyStrLast", importc: "copyStrLast", noSideEffect, + magic: "CopyStrLast", importc: "copyStrLast", noSideEffect, deprecated.} ## copies a slice of `s` into a new string and returns this new ## string. The bounds `first` and `last` denote the indices of @@ -1358,7 +1358,7 @@ when not defined(nimrodVM): ## The allocated memory belongs to its allocating thread! ## Use `createShared` to allocate from a shared heap. cast[ptr T](alloc0(T.sizeof * size)) - proc realloc*(p: pointer, newSize: int): pointer {.noconv, rtl, tags: [], + proc realloc*(p: pointer, newSize: int): pointer {.noconv, rtl, tags: [], benign.} ## grows or shrinks a given memory block. If p is **nil** then a new ## memory block is returned. In either way the block has at least @@ -1381,7 +1381,7 @@ when not defined(nimrodVM): ## ``realloc``. This procedure is dangerous! If one forgets to ## free the memory a leak occurs; if one tries to access freed ## memory (or just freeing it twice!) a core dump may happen - ## or other memory may be corrupted. + ## or other memory may be corrupted. ## The freed memory must belong to its allocating thread! ## Use `deallocShared` to deallocate from a shared heap. proc free*[T](p: ptr T) {.inline, benign.} = @@ -1390,30 +1390,30 @@ when not defined(nimrodVM): ## allocates a new memory block on the shared heap with at ## least ``size`` bytes. The block has to be freed with ## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block - ## is not initialized, so reading from it before writing to it is + ## is not initialized, so reading from it before writing to it is ## undefined behaviour! - proc createSharedU*(T: typedesc, size = 1.Positive): ptr T {.inline, + proc createSharedU*(T: typedesc, size = 1.Positive): ptr T {.inline, benign.} = ## allocates a new memory block on the shared heap with at ## least ``T.sizeof * size`` bytes. The block has to be freed with ## ``resizeShared(block, 0)`` or ``freeShared(block)``. The block - ## is not initialized, so reading from it before writing to it is + ## is not initialized, so reading from it before writing to it is ## undefined behaviour! cast[ptr T](allocShared(T.sizeof * size)) proc allocShared0*(size: int): pointer {.noconv, rtl, benign.} - ## allocates a new memory block on the shared heap with at + ## allocates a new memory block on the shared heap with at ## least ``size`` bytes. The block has to be freed with ## ``reallocShared(block, 0)`` or ``deallocShared(block)``. ## The block is initialized with all bytes ## containing zero, so it is somewhat safer than ``allocShared``. proc createShared*(T: typedesc, size = 1.Positive): ptr T {.inline.} = - ## allocates a new memory block on the shared heap with at + ## allocates a new memory block on the shared heap with at ## least ``T.sizeof * size`` bytes. The block has to be freed with ## ``resizeShared(block, 0)`` or ``freeShared(block)``. ## The block is initialized with all bytes ## containing zero, so it is somewhat safer than ``createSharedU``. cast[ptr T](allocShared0(T.sizeof * size)) - proc reallocShared*(p: pointer, newSize: int): pointer {.noconv, rtl, + proc reallocShared*(p: pointer, newSize: int): pointer {.noconv, rtl, benign.} ## grows or shrinks a given memory block on the heap. If p is **nil** ## then a new memory block is returned. In either way the block has at @@ -1525,7 +1525,7 @@ const NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch ## is the version of Nim as a string. -{.deprecated: [TEndian: Endianness, NimrodVersion: NimVersion, +{.deprecated: [TEndian: Endianness, NimrodVersion: NimVersion, NimrodMajor: NimMajor, NimrodMinor: NimMinor, NimrodPatch: NimPatch].} # GC interface: @@ -1805,7 +1805,7 @@ proc `==` *[I, T](x, y: array[I, T]): bool = return result = true -proc `@`*[T](a: openArray[T]): seq[T] = +proc `@`*[T](a: openArray[T]): seq[T] = ## turns an openarray into a sequence. This is not as efficient as turning ## a fixed length array into a sequence as it always copies every element ## of `a`. @@ -1853,7 +1853,7 @@ when not defined(NimrodVM): else: proc seqToPtr[T](x: seq[T]): pointer {.asmNoStackFrame, nosideeffect.} = asm """return `x`""" - + proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} = ## Generic equals operator for sequences: relies on a equals operator for ## the element type `T`. @@ -1879,7 +1879,7 @@ proc contains*[T](a: openArray[T], item: T): bool {.inline.}= ## for ``find(a, item) >= 0``. return find(a, item) >= 0 -proc pop*[T](s: var seq[T]): T {.inline, noSideEffect.} = +proc pop*[T](s: var seq[T]): T {.inline, noSideEffect.} = ## returns the last item of `s` and decreases ``s.len`` by one. This treats ## `s` as a stack and implements the common *pop* operation. var L = s.len-1 @@ -1941,7 +1941,7 @@ iterator fields*[T: tuple|object](x: T): RootObj {. iterator fields*[S:tuple|object, T:tuple|object](x: S, y: T): tuple[a,b: expr] {. magic: "Fields", noSideEffect.} ## iterates over every field of `x` and `y`. - ## Warning: This is really transforms the 'for' and unrolls the loop. + ## Warning: This is really transforms the 'for' and unrolls the loop. ## The current implementation also has a bug that affects symbol binding ## in the loop body. iterator fieldPairs*[T: tuple|object](x: T): RootObj {. @@ -1982,18 +1982,18 @@ iterator fieldPairs*[S: tuple|object, T: tuple|object](x: S, y: T): tuple[ a, b: expr] {. magic: "FieldPairs", noSideEffect.} ## iterates over every field of `x` and `y`. - ## Warning: This really transforms the 'for' and unrolls the loop. + ## Warning: This really transforms the 'for' and unrolls the loop. ## The current implementation also has a bug that affects symbol binding ## in the loop body. -proc `==`*[T: tuple|object](x, y: T): bool = +proc `==`*[T: tuple|object](x, y: T): bool = ## generic ``==`` operator for tuples that is lifted from the components ## of `x` and `y`. for a, b in fields(x, y): if a != b: return false return true -proc `<=`*[T: tuple](x, y: T): bool = +proc `<=`*[T: tuple](x, y: T): bool = ## generic ``<=`` operator for tuples that is lifted from the components ## of `x` and `y`. This implementation uses `cmp`. for a, b in fields(x, y): @@ -2002,7 +2002,7 @@ proc `<=`*[T: tuple](x, y: T): bool = if c > 0: return false return true -proc `<`*[T: tuple](x, y: T): bool = +proc `<`*[T: tuple](x, y: T): bool = ## generic ``<`` operator for tuples that is lifted from the components ## of `x` and `y`. This implementation uses `cmp`. for a, b in fields(x, y): @@ -2011,7 +2011,7 @@ proc `<`*[T: tuple](x, y: T): bool = if c > 0: return false return false -proc `$`*[T: tuple|object](x: T): string = +proc `$`*[T: tuple|object](x: T): string = ## generic ``$`` operator for tuples that is lifted from the components ## of `x`. Example: ## @@ -2021,13 +2021,13 @@ proc `$`*[T: tuple|object](x: T): string = result = "(" var firstElement = true for name, value in fieldPairs(x): - if not(firstElement): result.add(", ") + if not firstElement: result.add(", ") result.add(name) result.add(": ") result.add($value) firstElement = false result.add(")") - + proc collectionToString[T](x: T, b, e: string): string = result = b var firstElement = true @@ -2037,7 +2037,7 @@ proc collectionToString[T](x: T, b, e: string): string = firstElement = false result.add(e) -proc `$`*[T](x: set[T]): string = +proc `$`*[T](x: set[T]): string = ## generic ``$`` operator for sets that is lifted from the components ## of `x`. Example: ## @@ -2045,7 +2045,7 @@ proc `$`*[T](x: set[T]): string = ## ${23, 45} == "{23, 45}" collectionToString(x, "{", "}") -proc `$`*[T](x: seq[T]): string = +proc `$`*[T](x: seq[T]): string = ## generic ``$`` operator for seqs that is lifted from the components ## of `x`. Example: ## @@ -2056,7 +2056,7 @@ proc `$`*[T](x: seq[T]): string = when false: # causes bootstrapping to fail as we use array of chars and cstring should # match better ... - proc `$`*[T, IDX](x: array[IDX, T]): string = + proc `$`*[T, IDX](x: array[IDX, T]): string = collectionToString(x, "[", "]") # ----------------- GC interface --------------------------------------------- @@ -2098,14 +2098,14 @@ when not defined(nimrodVM) and hostOS != "standalone": proc GC_getStatistics*(): string {.rtl, benign.} ## returns an informative string about the GC's activity. This may be useful ## for tweaking. - + proc GC_ref*[T](x: ref T) {.magic: "GCref", benign.} proc GC_ref*[T](x: seq[T]) {.magic: "GCref", benign.} proc GC_ref*(x: string) {.magic: "GCref", benign.} ## marks the object `x` as referenced, so that it will not be freed until ## it is unmarked via `GC_unref`. If called n-times for the same object `x`, - ## n calls to `GC_unref` are needed to unmark `x`. - + ## n calls to `GC_unref` are needed to unmark `x`. + proc GC_unref*[T](x: ref T) {.magic: "GCunref", benign.} proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", benign.} proc GC_unref*(x: string) {.magic: "GCunref", benign.} @@ -2141,19 +2141,19 @@ var ## application code should never set this hook! You better know what you ## do when setting this. If ``localRaiseHook`` returns false, the exception ## is caught and does not propagate further through the call stack. - + outOfMemHook*: proc () {.nimcall, tags: [], benign.} - ## set this variable to provide a procedure that should be called + ## set this variable to provide a procedure that should be called ## in case of an `out of memory`:idx: event. The standard handler ## writes an error message and terminates the program. `outOfMemHook` can ## be used to raise an exception in case of OOM like so: - ## + ## ## .. code-block:: nim ## ## var gOutOfMem: ref EOutOfMemory ## new(gOutOfMem) # need to be allocated *before* OOM really happened! ## gOutOfMem.msg = "out of memory" - ## + ## ## proc handleOOM() = ## raise gOutOfMem ## @@ -2210,7 +2210,7 @@ proc echo*(x: varargs[expr, `$`]) {.magic: "Echo", tags: [WriteIOEffect], ## <manual.html#nosideeffect-pragma>`_ you can use `debugEcho <#debugEcho>`_ ## instead. -proc debugEcho*(x: varargs[expr, `$`]) {.magic: "Echo", noSideEffect, +proc debugEcho*(x: varargs[expr, `$`]) {.magic: "Echo", noSideEffect, tags: [], raises: [].} ## Same as `echo <#echo>`_, but as a special semantic rule, ``debugEcho`` ## pretends to be free of side effects, so that it can be used for debugging @@ -2262,7 +2262,7 @@ proc abs*(x: int16): int16 {.magic: "AbsI", noSideEffect.} = proc abs*(x: int32): int32 {.magic: "AbsI", noSideEffect.} = if x < 0: -x else: x proc abs*(x: int64): int64 {.magic: "AbsI64", noSideEffect.} = - ## returns the absolute value of `x`. If `x` is ``low(x)`` (that + ## returns the absolute value of `x`. If `x` is ``low(x)`` (that ## is -MININT for its type), an overflow exception is thrown (if overflow ## checking is turned on). if x < 0: -x else: x @@ -2318,14 +2318,14 @@ when not defined(JS): #and not defined(NimrodVM): # we use binary mode in Windows: setmode(fileno(c_stdin), O_BINARY) setmode(fileno(c_stdout), O_BINARY) - + when defined(endb): proc endbStep() # ----------------- IO Part ------------------------------------------------ when hostOS != "standalone": type - CFile {.importc: "FILE", header: "<stdio.h>", + CFile {.importc: "FILE", header: "<stdio.h>", final, incompletestruct.} = object File* = ptr CFile ## The type representing a file handle. @@ -2375,9 +2375,9 @@ when not defined(JS): #and not defined(NimrodVM): ## Creates a ``TFile`` from a `filehandle` with given `mode`. ## ## Default mode is readonly. Returns true iff the file could be opened. - + proc open*(filename: string, - mode: FileMode = fmRead, bufSize: int = -1): File = + mode: FileMode = fmRead, bufSize: int = -1): File = ## Opens a file named `filename` with given `mode`. ## ## Default mode is readonly. Raises an ``IO`` exception if the file @@ -2387,7 +2387,7 @@ when not defined(JS): #and not defined(NimrodVM): proc reopen*(f: File, filename: string, mode: FileMode = fmRead): bool {. tags: [], benign.} - ## reopens the file `f` with given `filename` and `mode`. This + ## reopens the file `f` with given `filename` and `mode`. This ## is often used to redirect the `stdin`, `stdout` or `stderr` ## file variables. ## @@ -2398,7 +2398,7 @@ when not defined(JS): #and not defined(NimrodVM): proc endOfFile*(f: File): bool {.tags: [], benign.} ## Returns true iff `f` is at the end. - + proc readChar*(f: File): char {. importc: "fgetc", header: "<stdio.h>", tags: [ReadIOEffect].} ## Reads a single character from the stream `f`. @@ -2411,7 +2411,7 @@ when not defined(JS): #and not defined(NimrodVM): ## ## Raises an IO exception in case of an error. It is an error if the ## current file position is not at the beginning of the file. - + proc readFile*(filename: string): TaintedString {.tags: [ReadIOEffect], benign.} ## Opens a file named `filename` for reading. ## @@ -2440,8 +2440,8 @@ when not defined(JS): #and not defined(NimrodVM): ## reads a line of text from the file `f`. May throw an IO exception. ## A line of text may be delimited by ``CR``, ``LF`` or ## ``CRLF``. The newline character(s) are not part of the returned string. - - proc readLine*(f: File, line: var TaintedString): bool {.tags: [ReadIOEffect], + + proc readLine*(f: File, line: var TaintedString): bool {.tags: [ReadIOEffect], benign.} ## reads a line of text from the file `f` into `line`. `line` must not be ## ``nil``! May throw an IO exception. @@ -2450,7 +2450,7 @@ when not defined(JS): #and not defined(NimrodVM): ## Returns ``false`` if the end of the file has been reached, ``true`` ## otherwise. If ``false`` is returned `line` contains no new data. - proc writeln*[Ty](f: File, x: varargs[Ty, `$`]) {.inline, + proc writeln*[Ty](f: File, x: varargs[Ty, `$`]) {.inline, tags: [WriteIOEffect], benign.} ## writes the values `x` to `f` and then writes "\n". ## May throw an IO exception. @@ -2544,11 +2544,11 @@ when not defined(JS): #and not defined(NimrodVM): dealloc(a) when not defined(NimrodVM): - proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, + proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, discardable, benign.} ## atomic increment of `memLoc`. Returns the value after the operation. - - proc atomicDec*(memLoc: var int, x: int = 1): int {.inline, + + proc atomicDec*(memLoc: var int, x: int = 1): int {.inline, discardable, benign.} ## atomic decrement of `memLoc`. Returns the value after the operation. @@ -2562,7 +2562,7 @@ when not defined(JS): #and not defined(NimrodVM): context: C_JmpBuf hasRaiseAction: bool raiseAction: proc (e: ref Exception): bool {.closure.} - + when declared(initAllocator): initAllocator() when hasThreadSupport: @@ -2576,7 +2576,7 @@ when not defined(JS): #and not defined(NimrodVM): proc setControlCHook*(hook: proc () {.noconv.} not nil) ## allows you to override the behaviour of your application when CTRL+C ## is pressed. Only one such hook is supported. - + proc writeStackTrace*() {.tags: [WriteIOEffect].} ## writes the current stack trace to ``stderr``. This is only works ## for debug builds. @@ -2587,20 +2587,20 @@ when not defined(JS): #and not defined(NimrodVM): proc getStackTrace*(e: ref Exception): string ## gets the stack trace associated with `e`, which is the stack that ## lead to the ``raise`` statement. This only works for debug builds. - + {.push stack_trace: off, profiler:off.} when hostOS == "standalone": include "system/embedded" else: include "system/excpt" include "system/chcks" - + # we cannot compile this with stack tracing on # as it would recurse endlessly! include "system/arithm" {.pop.} # stack trace {.pop.} # stack trace - + when hostOS != "standalone" and not defined(NimrodVM): include "system/dyncalls" when not defined(NimrodVM): @@ -2608,7 +2608,7 @@ when not defined(JS): #and not defined(NimrodVM): const GenericSeqSize = (2 * sizeof(int)) - + proc getDiscriminant(aa: pointer, n: ptr TNimNode): int = sysAssert(n.kind == nkCase, "getDiscriminant: node != nkCase") var d: int @@ -2728,7 +2728,7 @@ when not defined(JS): #and not defined(NimrodVM): ## process(value) ## else: ## echo "Value too big!" - + proc unlikely*(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.} ## Hints the optimizer that `val` is likely going to be false. ## @@ -2742,7 +2742,7 @@ when not defined(JS): #and not defined(NimrodVM): ## echo "Value too big!" ## else: ## process(value) - + proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} = ## retrieves the raw proc pointer of the closure `x`. This is ## useful for interfacing closures with C. @@ -2774,7 +2774,7 @@ elif defined(JS): proc GC_enableMarkAndSweep() = discard proc GC_disableMarkAndSweep() = discard proc GC_getStatistics(): string = return "" - + proc getOccupiedMem(): int = return -1 proc getFreeMem(): int = return -1 proc getTotalMem(): int = return -1 @@ -2797,7 +2797,7 @@ elif defined(JS): if x == y: return 0 if x < y: return -1 return 1 - + when defined(nimffi): include "system/sysio" @@ -2831,14 +2831,14 @@ template spliceImpl(s, a, L, b: expr): stmt {.immediate.} = # cut down: setLen(s, newLen) # fill the hole: - for i in 0 .. <b.len: s[i+a] = b[i] + for i in 0 .. <b.len: s[i+a] = b[i] 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) - proc `[]=`*(s: var string, x: Slice[int], b: string) = + proc `[]=`*(s: var string, x: Slice[int], b: string) = ## slice assignment for strings. Negative indexes are supported. If ## ``b.len`` is not exactly the number of elements that are referred to ## by `x`, a `splice`:idx: is performed: @@ -2880,7 +2880,7 @@ proc `[]`*[Idx, T](a: array[Idx, T], x: Slice[Idx]): seq[T] = var L = ord(x.b) - ord(x.a) + 1 newSeq(result, L) var j = x.a - for i in 0.. <L: + for i in 0.. <L: result[i] = a[j] inc(j) @@ -2890,23 +2890,23 @@ proc `[]=`*[Idx, T](a: var array[Idx, T], x: Slice[Idx], b: openArray[T]) = var L = ord(x.b) - ord(x.a) + 1 if L == b.len: var j = x.a - for i in 0 .. <L: + for i in 0 .. <L: a[j] = b[i] inc(j) else: sysFatal(RangeError, "different lengths for slice assignment") -proc `[]`*[T](s: seq[T], x: Slice[int]): seq[T] = +proc `[]`*[T](s: seq[T], x: Slice[int]): seq[T] = ## slice operation for sequences. Negative indexes are supported. var a = x.a-|s var L = x.b-|s - 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]) = +proc `[]=`*[T](s: var seq[T], x: Slice[int], b: openArray[T]) = ## slice assignment for sequences. Negative indexes are supported. If ## ``b.len`` is not exactly the number of elements that are referred to - ## by `x`, a `splice`:idx: is performed. + ## by `x`, a `splice`:idx: is performed. var a = x.a-|s var L = x.b-|s - a + 1 if L == b.len: @@ -2937,7 +2937,7 @@ proc staticExec*(command: string, input = ""): string {. ## to the executed program. ## ## .. code-block:: nim - ## const buildInfo = "Revision " & staticExec("git rev-parse HEAD") & + ## const buildInfo = "Revision " & staticExec("git rev-parse HEAD") & ## "\nCompiled on " & staticExec("uname -v") ## ## `gorge <#gorge>`_ is an alias for ``staticExec``. Note that you can use @@ -2979,7 +2979,7 @@ proc `&=`* (x: var string, y: string) {.magic: "AppendStrStr", noSideEffect.} proc astToStr*[T](x: T): string {.magic: "AstToStr", noSideEffect.} ## converts the AST of `x` into a string representation. This is very useful ## for debugging. - + proc instantiationInfo*(index = -1, fullPaths = false): tuple[ filename: string, line: int] {. magic: "InstantiationInfo", noSideEffect.} ## provides access to the compiler's instantiation stack line information. @@ -3090,16 +3090,16 @@ template onFailedAssert*(msg: expr, code: stmt): stmt {.dirty, immediate.} = ## Sets an assertion failure handler that will intercept any assert ## statements following `onFailedAssert` in the current lexical scope. ## Can be defined multiple times in a single function. - ## + ## ## .. code-block:: nim ## ## proc example(x: int): TErrorCode = ## onFailedAssert(msg): ## log msg ## return E_FAIL - ## + ## ## assert(...) - ## + ## ## onFailedAssert(msg): ## raise newException(EMyException, msg) ## @@ -3111,7 +3111,7 @@ template onFailedAssert*(msg: expr, code: stmt): stmt {.dirty, immediate.} = proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} = ## marks a sequence `s` as `shallow`:idx:. Subsequent assignments will not - ## perform deep copies of `s`. This is only useful for optimization + ## perform deep copies of `s`. This is only useful for optimization ## purposes. when not defined(JS) and not defined(NimrodVM): var s = cast[PGenericSeq](s) @@ -3119,7 +3119,7 @@ proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} = proc shallow*(s: var string) {.noSideEffect, inline.} = ## marks a string `s` as `shallow`:idx:. Subsequent assignments will not - ## perform deep copies of `s`. This is only useful for optimization + ## perform deep copies of `s`. This is only useful for optimization ## purposes. when not defined(JS) and not defined(NimrodVM): var s = cast[PGenericSeq](s) @@ -3141,13 +3141,13 @@ else: when false: template eval*(blk: stmt): stmt = ## executes a block of code at compile time just as if it was a macro - ## optionally, the block can return an AST tree that will replace the + ## optionally, the block can return an AST tree that will replace the ## eval expression macro payload: stmt {.gensym.} = blk payload() when hostOS != "standalone": - proc insert*(x: var string, item: string, i = 0) {.noSideEffect.} = + proc insert*(x: var string, item: string, i = 0) {.noSideEffect.} = ## inserts `item` into `x` at position `i`. var xl = x.len setLen(x, xl+item.len) diff --git a/lib/system/deepcopy.nim b/lib/system/deepcopy.nim index 7a26a17c5..093c0f3a7 100644 --- a/lib/system/deepcopy.nim +++ b/lib/system/deepcopy.nim @@ -34,9 +34,9 @@ proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode) {.benign.} = proc copyDeepString(src: NimString): NimString {.inline.} = if src != nil: - result = rawNewString(src.space) + result = rawNewStringNoInit(src.len) result.len = src.len - c_memcpy(result.data, src.data, (src.len + 1) * sizeof(char)) + c_memcpy(result.data, src.data, src.len + 1) proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) = var diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 1f4279c8f..bbf8cbf66 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -1,7 +1,7 @@ # # # Nim's Runtime Library -# (c) Copyright 2013 Andreas Rumpf +# (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -48,7 +48,7 @@ type TWalkOp = enum waMarkGlobal, # part of the backup/debug mark&sweep waMarkPrecise, # part of the backup/debug mark&sweep - waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack, + waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack, waCollectWhite #, waDebug TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.} @@ -61,9 +61,9 @@ type maxThreshold: int # max threshold that has been set maxStackSize: int # max stack size maxStackCells: int # max stack cells in ``decStack`` - cycleTableSize: int # max entries in cycle table + cycleTableSize: int # max entries in cycle table maxPause: int64 # max measured GC pause in nanoseconds - + TGcHeap {.final, pure.} = object # this contains the zero count and # non-zero count table stackBottom: pointer @@ -88,11 +88,11 @@ var when not defined(useNimRtl): instantiateForRegion(gch.region) -template acquire(gch: TGcHeap) = +template acquire(gch: TGcHeap) = when hasThreadSupport and hasSharedHeap: acquireSys(HeapLock) -template release(gch: TGcHeap) = +template release(gch: TGcHeap) = when hasThreadSupport and hasSharedHeap: releaseSys(HeapLock) @@ -117,7 +117,7 @@ proc usrToCell(usr: pointer): PCell {.inline.} = # convert pointer to userdata to object (=pointer to refcount) result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(TCell))) -proc canbeCycleRoot(c: PCell): bool {.inline.} = +proc canBeCycleRoot(c: PCell): bool {.inline.} = result = ntfAcyclic notin c.typ.flags proc extGetCellType(c: pointer): PNimType {.compilerproc.} = @@ -163,7 +163,7 @@ when hasThreadSupport and hasSharedHeap: template `--`(x: expr): expr = atomicDec(x, rcIncrement) <% rcIncrement template `++`(x: expr): stmt = discard atomicInc(x, rcIncrement) else: - template `--`(x: expr): expr = + template `--`(x: expr): expr = dec(x, rcIncrement) x <% rcIncrement template `++`(x: expr): stmt = inc(x, rcIncrement) @@ -181,7 +181,7 @@ proc prepareDealloc(cell: PCell) = (cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell)) dec(gch.recGcLock) -proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} = +proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} = # we MUST access gch as a global here, because this crosses DLL boundaries! when hasThreadSupport and hasSharedHeap: acquireSys(HeapLock) @@ -211,7 +211,7 @@ proc decRef(c: PCell) {.inline.} = rtlAddCycleRoot(c) #writeCell("decRef", c) -proc incRef(c: PCell) {.inline.} = +proc incRef(c: PCell) {.inline.} = gcAssert(isAllocatedPtr(gch.region, c), "incRef: interiorPtr") c.refcount = c.refcount +% rcIncrement # and not colorMask @@ -246,12 +246,12 @@ proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} = dest[] = src proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerProc, inline.} = - # the code generator calls this proc if it is known at compile time that no + # the code generator calls this proc if it is known at compile time that no # cycle is possible. if src != nil: var c = usrToCell(src) ++c.refcount - if dest[] != nil: + if dest[] != nil: var c = usrToCell(dest[]) if --c.refcount: rtlAddZCT(c) @@ -269,7 +269,7 @@ proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerProc.} = if cast[int](dest[]) >=% PageSize: decRef(usrToCell(dest[])) else: # can't be an interior pointer if it's a stack location! - gcAssert(interiorAllocatedPtr(gch.region, dest) == nil, + gcAssert(interiorAllocatedPtr(gch.region, dest) == nil, "stack loc AND interior pointer") dest[] = src @@ -321,7 +321,7 @@ when useMarkForDebug or useBackupGc: echo "[GC] cannot register global variable; too many global variables" quit 1 -proc cellsetReset(s: var TCellSet) = +proc cellsetReset(s: var TCellSet) = deinit(s) init(s) @@ -336,7 +336,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) {.benign.} = if n.sons[i].typ.kind in {tyRef, tyString, tySequence}: doOperation(cast[PPointer](d +% n.sons[i].offset)[], op) else: - forAllChildrenAux(cast[pointer](d +% n.sons[i].offset), + forAllChildrenAux(cast[pointer](d +% n.sons[i].offset), n.sons[i].typ, op) else: forAllSlotsAux(dest, n.sons[i], op) @@ -384,7 +384,7 @@ proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} = # we check the last 8 entries (cache line) for a slot that could be reused. # In 63% of all cases we succeed here! But we have to optimize the heck # out of this small linear search so that ``newObj`` is not slowed down. - # + # # Slots to try cache hit # 1 32% # 4 59% @@ -481,7 +481,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} = gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1") collectCT(gch) sysAssert(allocInv(gch.region), "newObjRC1 after collectCT") - + var res = cast[PCell](rawAlloc(gch.region, size + sizeof(TCell))) sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc") sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") @@ -510,7 +510,7 @@ proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} = cast[PGenericSeq](result).len = len cast[PGenericSeq](result).reserved = len when defined(memProfiler): nimProfile(size) - + proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer = acquire(gch) collectCT(gch) @@ -522,7 +522,7 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer = var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(TCell))) var elemSize = 1 if ol.typ.kind != tyString: elemSize = ol.typ.base.size - + var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize copyMem(res, ol, oldsize + sizeof(TCell)) zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(TCell)), @@ -536,7 +536,7 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer = writeCell("growObj new cell", res) gcTrace(ol, csZctFreed) gcTrace(res, csAllocated) - when reallyDealloc: + when reallyDealloc: sysAssert(allocInv(gch.region), "growObj before dealloc") if ol.refcount shr rcShift <=% 1: # free immediately to save space: @@ -580,7 +580,7 @@ proc freeCyclicCell(gch: var TGcHeap, c: PCell) = prepareDealloc(c) gcTrace(c, csCycFreed) when logGC: writeCell("cycle collector dealloc cell", c) - when reallyDealloc: + when reallyDealloc: sysAssert(allocInv(gch.region), "free cyclic cell") rawDealloc(gch.region, c) else: @@ -767,7 +767,7 @@ proc collectCycles(gch: var TGcHeap) = gcAssert isAllocatedPtr(gch.region, c), "addBackStackRoots" gcAssert c.refcount >=% rcIncrement, "addBackStackRoots: dead cell" if canBeCycleRoot(c): - #if c notin gch.cycleRoots: + #if c notin gch.cycleRoots: inc cycleRootsLen incl(gch.cycleRoots, c) gcAssert c.typ != nil, "addBackStackRoots 2" @@ -794,12 +794,12 @@ proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} = add(gch.decStack, cell) sysAssert(allocInv(gch.region), "gcMark end") -proc markThreadStacks(gch: var TGcHeap) = +proc markThreadStacks(gch: var TGcHeap) = when hasThreadSupport and hasSharedHeap: {.error: "not fully implemented".} var it = threadList while it != nil: - # mark registers: + # mark registers: for i in 0 .. high(it.registers): gcMark(gch, it.registers[i]) var sp = cast[TAddress](it.stackBottom) var max = cast[TAddress](it.stackTop) @@ -933,7 +933,7 @@ else: while sp <=% max: gcMark(gch, cast[PPointer](sp)[]) sp = sp +% sizeof(pointer) - + proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = forEachStackSlot(gch, gcMark) @@ -946,13 +946,13 @@ when useMarkForDebug or useBackupGc: # ---------------------------------------------------------------------------- proc collectZCT(gch: var TGcHeap): bool = - # Note: Freeing may add child objects to the ZCT! So essentially we do - # deep freeing, which is bad for incremental operation. In order to + # Note: Freeing may add child objects to the ZCT! So essentially we do + # deep freeing, which is bad for incremental operation. In order to # avoid a deep stack, we move objects to keep the ZCT small. # This is performance critical! const workPackage = 100 var L = addr(gch.zct.len) - + when withRealTime: var steps = workPackage var t0: TTicks @@ -962,15 +962,15 @@ proc collectZCT(gch: var TGcHeap): bool = sysAssert(isAllocatedPtr(gch.region, c), "CollectZCT: isAllocatedPtr") # remove from ZCT: gcAssert((c.refcount and ZctFlag) == ZctFlag, "collectZCT") - + c.refcount = c.refcount and not ZctFlag gch.zct.d[0] = gch.zct.d[L[] - 1] dec(L[]) when withRealTime: dec steps - if c.refcount <% rcIncrement: + if c.refcount <% rcIncrement: # It may have a RC > 0, if it is in the hardware stack or # it has not been removed yet from the ZCT. This is because - # ``incref`` does not bother to remove the cell from the ZCT + # ``incref`` does not bother to remove the cell from the ZCT # as this might be too slow. # In any case, it should be removed from the ZCT. But not # freed. **KEEP THIS IN MIND WHEN MAKING THIS INCREMENTAL!** @@ -983,7 +983,7 @@ proc collectZCT(gch: var TGcHeap): bool = # access invalid memory. This is done by prepareDealloc(): prepareDealloc(c) forAllChildren(c, waZctDecRef) - when reallyDealloc: + when reallyDealloc: sysAssert(allocInv(gch.region), "collectZCT: rawDealloc") rawDealloc(gch.region, c) else: @@ -994,7 +994,7 @@ proc collectZCT(gch: var TGcHeap): bool = steps = workPackage if gch.maxPause > 0: let duration = getticks() - t0 - # the GC's measuring is not accurate and needs some cleanup actions + # the GC's measuring is not accurate and needs some cleanup actions # (stack unmarking), so subtract some short amount of time in # order to miss deadlines less often: if duration >= gch.maxPause - 50_000: @@ -1017,7 +1017,7 @@ proc collectCTBody(gch: var TGcHeap) = when withRealTime: let t0 = getticks() sysAssert(allocInv(gch.region), "collectCT: begin") - + gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize()) sysAssert(gch.decStack.len == 0, "collectCT") prepareForInteriorPointerChecking(gch.region) @@ -1036,7 +1036,7 @@ proc collectCTBody(gch: var TGcHeap) = gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold) unmarkStackAndRegisters(gch) sysAssert(allocInv(gch.region), "collectCT: end") - + when withRealTime: let duration = getticks() - t0 gch.stat.maxPause = max(gch.stat.maxPause, duration) @@ -1050,8 +1050,12 @@ when useMarkForDebug or useBackupGc: markGlobals(gch) proc collectCT(gch: var TGcHeap) = - if (gch.zct.len >= ZctThreshold or (cycleGC and - getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and + # stackMarkCosts prevents some pathological behaviour: Stack marking + # becomes more expensive with large stacks and large stacks mean that + # cells with RC=0 are more likely to be kept alive by the stack. + let stackMarkCosts = max(stackSize() div (16*sizeof(int)), ZctThreshold) + if (gch.zct.len >= stackMarkCosts or (cycleGC and + getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and gch.recGcLock == 0: when useMarkForDebug: prepareForInteriorPointerChecking(gch.region) @@ -1070,7 +1074,7 @@ when withRealTime: acquire(gch) gch.maxPause = us.toNano if (gch.zct.len >= ZctThreshold or (cycleGC and - getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or + getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or strongAdvice: collectCTBody(gch) release(gch) @@ -1078,13 +1082,13 @@ when withRealTime: proc GC_step*(us: int, strongAdvice = false) = GC_step(gch, us, strongAdvice) when not defined(useNimRtl): - proc GC_disable() = + proc GC_disable() = when hasThreadSupport and hasSharedHeap: discard atomicInc(gch.recGcLock, 1) else: inc(gch.recGcLock) proc GC_enable() = - if gch.recGcLock > 0: + if gch.recGcLock > 0: when hasThreadSupport and hasSharedHeap: discard atomicDec(gch.recGcLock, 1) else: diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim index ae2d2c85d..b8a61d627 100644 --- a/lib/system/gc2.nim +++ b/lib/system/gc2.nim @@ -593,7 +593,7 @@ proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} = return add(gch.zct, res) -proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap, rc1: bool): pointer = +proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap, rc1 = false): pointer = # generates a new object and sets its reference counter to 0 acquire(gch) sysAssert(allocInv(gch.region), "rawNewObj begin") diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index cfbc24f0c..11780a9aa 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -30,18 +30,30 @@ proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} = if a == b: return true if a == nil or b == nil: return false return a.len == b.len and - c_memcmp(a.data, b.data, a.len * sizeof(char)) == 0'i32 + c_memcmp(a.data, b.data, a.len) == 0'i32 when declared(allocAtomic): template allocStr(size: expr): expr = cast[NimString](allocAtomic(size)) + + template allocStrNoInit(size: expr): expr = + cast[NimString](boehmAllocAtomic(size)) else: template allocStr(size: expr): expr = cast[NimString](newObj(addr(strDesc), size)) + template allocStrNoInit(size: expr): expr = + cast[NimString](rawNewObj(addr(strDesc), size, gch)) + +proc rawNewStringNoInit(space: int): NimString {.compilerProc.} = + var s = space + if s < 7: s = 7 + result = allocStrNoInit(sizeof(TGenericSeq) + s + 1) + result.reserved = s + proc rawNewString(space: int): NimString {.compilerProc.} = var s = space - if s < 8: s = 7 + if s < 7: s = 7 result = allocStr(sizeof(TGenericSeq) + s + 1) result.reserved = s @@ -53,10 +65,10 @@ proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.} = var start = max(start, 0) var len = min(last, s.len-1) - start + 1 if len > 0: - result = rawNewString(len) + result = rawNewStringNoInit(len) result.len = len - c_memcpy(result.data, addr(s.data[start]), len * sizeof(char)) - #result.data[len] = '\0' + c_memcpy(result.data, addr(s.data[start]), len) + result.data[len] = '\0' else: result = rawNewString(len) @@ -64,10 +76,9 @@ proc copyStr(s: NimString, start: int): NimString {.compilerProc.} = result = copyStrLast(s, start, s.len-1) proc toNimStr(str: cstring, len: int): NimString {.compilerProc.} = - result = rawNewString(len) + result = rawNewStringNoInit(len) result.len = len - c_memcpy(result.data, str, (len+1) * sizeof(char)) - #result.data[len] = '\0' # readline relies on this! + c_memcpy(result.data, str, len + 1) proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} = result = toNimStr(str, c_strlen(str)) @@ -77,23 +88,24 @@ proc copyString(src: NimString): NimString {.compilerRtl.} = if (src.reserved and seqShallowFlag) != 0: result = src else: - result = rawNewString(src.space) + result = rawNewStringNoInit(src.len) result.len = src.len - c_memcpy(result.data, src.data, (src.len + 1) * sizeof(char)) + c_memcpy(result.data, src.data, src.len + 1) proc copyStringRC1(src: NimString): NimString {.compilerRtl.} = if src != nil: - var s = src.space - if s < 8: s = 7 when declared(newObjRC1): + var s = src.len + if s < 7: s = 7 result = cast[NimString](newObjRC1(addr(strDesc), sizeof(TGenericSeq) + s+1)) + result.reserved = s else: - result = allocStr(sizeof(TGenericSeq) + s + 1) - result.reserved = s + result = rawNewStringNoInit(src.len) result.len = src.len c_memcpy(result.data, src.data, src.len + 1) + proc hashString(s: string): int {.compilerproc.} = # the compiler needs exactly the same hash function! # this used to be used for efficient generation of string case statements @@ -113,7 +125,7 @@ proc addChar(s: NimString, c: char): NimString = if result.len >= result.space: result.reserved = resize(result.space) result = cast[NimString](growObj(result, - sizeof(TGenericSeq) + (result.reserved+1) * sizeof(char))) + sizeof(TGenericSeq) + result.reserved + 1)) result.data[result.len] = c result.data[result.len+1] = '\0' inc(result.len) @@ -157,7 +169,7 @@ proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} = result = cast[NimString](growObj(dest, sizeof(TGenericSeq) + sp + 1)) result.reserved = sp #result = rawNewString(sp) - #copyMem(result, dest, dest.len * sizeof(char) + sizeof(TGenericSeq)) + #copyMem(result, dest, dest.len + sizeof(TGenericSeq)) # DO NOT UPDATE LEN YET: dest.len = newLen proc appendString(dest, src: NimString) {.compilerproc, inline.} = diff --git a/tests/ccgbugs/tstringslice.nim b/tests/ccgbugs/tstringslice.nim new file mode 100644 index 000000000..66b3bbbe4 --- /dev/null +++ b/tests/ccgbugs/tstringslice.nim @@ -0,0 +1,10 @@ + +# bug #794 +type TRange = range[0..3] + +const str = "123456789" + +for i in TRange.low .. TRange.high: + echo str[i] #This works fine + echo str[int(i) .. int(TRange.high)] #So does this + echo str[i .. TRange.high] #The compiler complains about this diff --git a/tests/ccgbugs/tuple_canon.nim b/tests/ccgbugs/tuple_canon.nim new file mode 100644 index 000000000..960e2aae9 --- /dev/null +++ b/tests/ccgbugs/tuple_canon.nim @@ -0,0 +1,80 @@ +# bug #2250 + +import + math, strutils + +type + Meters = float + Point2[T] = tuple[x, y: T] + + HexState* = enum + hsOn, hsOff + + Index = uint16 + + HexGrid* = object + w, h: int ## Width and height of the hex grid. + radius: Meters ## Radius of circle that circumscribes a hexagon. + grid: seq[HexState] ## Information on what hexes are drawn. + + HexVtxIndex = enum + hiA, hiB, hiC, hiD, hiE, hiF + + HexCoord* = Point2[int] + +const + HexDY = sqrt(1.0 - (0.5 * 0.5)) # dy from center to midpoint of 1-2 + HexDX = sqrt(1.0 - (HexDY * HexDY)) # dx from center to midpoint of 1-5 (0.5) + + +let + hexOffsets : array[HexVtxIndex, Point2[float]] = [ + (-1.0, 0.0), + (-HexDX, -HexDY), + (HexDX, -HexDY), + (1.0, 0.0), + (HexDX, HexDY), + (-HexDX, HexDY)] + + evenSharingOffsets : array[HexVtxIndex, tuple[hc: HexCoord; idx: HexVtxIndex]] = [ + ((0,0), hiA), + ((0,0), hiB), + ((1,-1), hiA), + ((1,0), hiB), + ((1,0), hiA), + ((0,1), hiB)] + + oddSharingOffsets : array[HexVtxIndex, tuple[hc: HexCoord; idx: HexVtxIndex]] = [ + ((0,0), hiA), + ((0,0), hiB), + ((1,0), hiA), + ((1,1), hiB), + ((1,1), hiA), + ((0,1), hiB)] + +template odd*(i: int) : expr = + (i and 1) != 0 + +proc vidx(hg: HexGrid; col, row: int; i: HexVtxIndex) : Index = + #NOTE: this variation compiles + #var offset : type(evenSharingOffsets[i]) + # + #if odd(col): + # offset = oddSharingOffsets[i] + #else: + # offset = evenSharingOffsets[i] + + let + #NOTE: this line generates the bad code + offset = (if odd(col): oddSharingOffsets[i] else: evenSharingOffsets[i]) + x = col + 1 + offset.hc.x + y = row + 1 + offset.hc.y + + result = Index(x*2 + y * (hg.w + 2)*2 + int(offset.idx)) + +proc go() = + var hg : HexGrid + + echo "vidx ", $vidx(hg, 1, 2, hiC) + +go() diff --git a/tests/clearmsg/tconsttypemismatch.nim b/tests/clearmsg/tconsttypemismatch.nim new file mode 100644 index 000000000..edf480348 --- /dev/null +++ b/tests/clearmsg/tconsttypemismatch.nim @@ -0,0 +1,8 @@ +discard """ + file: "tconsttypemismatch.nim" + line: 7 + errormsg: "type mismatch" +""" +# bug #2252 +const foo: int = 1000 / 30 + diff --git a/tests/generics/tgeneric3.nim b/tests/generics/tgeneric3.nim index 6fb929efb..289bf1fd5 100644 --- a/tests/generics/tgeneric3.nim +++ b/tests/generics/tgeneric3.nim @@ -188,7 +188,7 @@ proc traceTree[T,D](root: PNode[T,D]) = write stdout, space proc doTrace(n: PNode[T,D], level: int) = - var space = repeatChar(2 * level) + var space = spaces(2 * level) traceln(space) write stdout, "node: " if n == nil: diff --git a/tests/manyloc/argument_parser/argument_parser.nim b/tests/manyloc/argument_parser/argument_parser.nim index a507a08e4..060610ae0 100644 --- a/tests/manyloc/argument_parser/argument_parser.nim +++ b/tests/manyloc/argument_parser/argument_parser.nim @@ -471,7 +471,7 @@ proc build_help*(expected: seq[Tparameter_specification] = @[], let width = prefixes.map(proc (x: string): int = 3 + len(x)).max for line in zip(prefixes, helps): - result.add(line.a & repeatChar(width - line.a.len) & line.b) + result.add(line.a & spaces(width - line.a.len) & line.b) proc echo_help*(expected: seq[Tparameter_specification] = @[], diff --git a/tests/manyloc/keineschweine/dependencies/genpacket/streams_enh.nim b/tests/manyloc/keineschweine/dependencies/genpacket/streams_enh.nim index a9759687f..3c5a7835c 100644 --- a/tests/manyloc/keineschweine/dependencies/genpacket/streams_enh.nim +++ b/tests/manyloc/keineschweine/dependencies/genpacket/streams_enh.nim @@ -1,5 +1,5 @@ import streams -from strutils import repeatChar +from strutils import repeat proc readPaddedStr*(s: PStream, length: int, padChar = '\0'): TaintedString = var lastChr = length @@ -10,7 +10,7 @@ proc readPaddedStr*(s: PStream, length: int, padChar = '\0'): TaintedString = proc writePaddedStr*(s: PStream, str: string, length: int, padChar = '\0') = if str.len < length: s.write(str) - s.write(repeatChar(length - str.len, padChar)) + s.write(repeat(padChar, length - str.len)) elif str.len > length: s.write(str.substr(0, length - 1)) else: @@ -37,7 +37,7 @@ when isMainModule: testStream.setPosition 0 testStream.writePaddedStr("Sup", 10) echo(repr(testStream), testStream.data.len) - doAssert testStream.data == "Sup"&repeatChar(7, '\0') + doAssert testStream.data == "Sup"&repeat('\0', 7) testStream.setPosition 0 res = testStream.readPaddedStr(10) diff --git a/tests/manyloc/keineschweine/enet_server/enet_server.nim b/tests/manyloc/keineschweine/enet_server/enet_server.nim index 6dd1a6a7f..c2e893273 100644 --- a/tests/manyloc/keineschweine/enet_server/enet_server.nim +++ b/tests/manyloc/keineschweine/enet_server/enet_server.nim @@ -144,7 +144,7 @@ when isMainModule: discard """block: var TestFile: FileChallengePair - contents = repeatStr(2, "abcdefghijklmnopqrstuvwxyz") + contents = repeat("abcdefghijklmnopqrstuvwxyz", 2) testFile.challenge = newScFileChallenge("foobar.test", FZoneCfg, contents.len.int32) testFile.file = checksumStr(contents) myAssets.add testFile""" diff --git a/tests/manyloc/keineschweine/keineschweine.nim b/tests/manyloc/keineschweine/keineschweine.nim index 0a5dc1efc..525d8a054 100644 --- a/tests/manyloc/keineschweine/keineschweine.nim +++ b/tests/manyloc/keineschweine/keineschweine.nim @@ -113,7 +113,7 @@ when defined(recordMode): isRecording = false proc zeroPad*(s: string; minLen: int): string = if s.len < minLen: - result = repeatChar(minLen - s.len, '0') + result = repeat(0, minLen - s.len) result.add s else: result = s diff --git a/tests/manyloc/keineschweine/lib/map_filter.nim b/tests/manyloc/keineschweine/lib/map_filter.nim index 966b84b6a..5776c9225 100644 --- a/tests/manyloc/keineschweine/lib/map_filter.nim +++ b/tests/manyloc/keineschweine/lib/map_filter.nim @@ -27,9 +27,9 @@ when isMainModule: var res = t.map(proc(z: int): int = result = z * 10) dumpSeq res - from strutils import toHex, repeatStr + from strutils import toHex var foocakes = t.map(proc(z: int): string = - result = toHex((z * 23).biggestInt, 4)) + result = toHex((z * 23).BiggestInt, 4)) dumpSeq foocakes t.mapInPlace(proc(z: int): int = result = z * 30) diff --git a/tests/manyloc/keineschweine/lib/zlib_helpers.nim b/tests/manyloc/keineschweine/lib/zlib_helpers.nim index 9a6542d75..fcd0e8d24 100644 --- a/tests/manyloc/keineschweine/lib/zlib_helpers.nim +++ b/tests/manyloc/keineschweine/lib/zlib_helpers.nim @@ -24,17 +24,17 @@ when isMainModule: import strutils var r = compress("Hello") echo repr(r) - var l = "Hello".len - var rr = uncompress(r, l) + var ln = "Hello".len + var rr = uncompress(r, ln) echo repr(rr) assert rr == "Hello" - proc `*`(a: string; b: int): string {.inline.} = result = repeatStr(b, a) + proc `*`(a: string; b: int): string {.inline.} = result = repeat(a, b) var s = "yo dude sup bruh homie" * 50 r = compress(s) echo s.len, " -> ", r.len - l = s.len - rr = uncompress(r, l) + ln = s.len + rr = uncompress(r, ln) echo r.len, " -> ", rr.len assert rr == s \ No newline at end of file diff --git a/tests/manyloc/keineschweine/server/old_sg_server.nim b/tests/manyloc/keineschweine/server/old_sg_server.nim index ac85cbf62..1e57c12a1 100644 --- a/tests/manyloc/keineschweine/server/old_sg_server.nim +++ b/tests/manyloc/keineschweine/server/old_sg_server.nim @@ -174,7 +174,7 @@ when isMainModule: block: var TestFile: FileChallengePair - contents = repeatStr(2, "abcdefghijklmnopqrstuvwxyz") + contents = repeat("abcdefghijklmnopqrstuvwxyz", 2) testFile.challenge = newScFileChallenge("foobar.test", FZoneCfg, contents.len.int32) testFile.file = checksumStr(contents) myAssets.add testFile diff --git a/tests/misc/parsecomb.nim b/tests/misc/parsecomb.nim new file mode 100644 index 000000000..68a61373f --- /dev/null +++ b/tests/misc/parsecomb.nim @@ -0,0 +1,105 @@ +type Input[T] = object + toks: seq[T] + index: int + +type + ResultKind* = enum rkSuccess, rkFailure + Result*[T, O] = object + case kind*: ResultKind + of rkSuccess: + output*: O + input: Input[T] + of rkFailure: + nil + +type + Parser*[T, O] = distinct proc (input: Input[T]): Result[T, O] + +proc unit*[T, O](v: O): Parser[T, O] = + Parser(proc (inp: Input[T]): Result[T, O] = + Result[T, O](kind: rkSuccess, output: v, input: inp)) + +proc fail*[T, O](): Parser[T, O] = + Parser(proc (inp: Input[T]): Result[T, O] = + Result(kind: rkFailure)) + +method runInput[T, O](self: Parser[T, O], inp: Input[T]): Result[T, O] = + # hmmm .. + type tmp = proc (input: Input[T]): Result[T, O] + # XXX: above needed for now, as without the `tmp` bit below, it compiles to invalid C. + tmp(self)(inp) + +method run*[T, O](self: Parser[T, O], toks: seq[T]): Result[T, O] = + self.runInput(Input[T](toks: toks, index: 0)) + +method chain*[T, O1, O2](self: Parser[T, O1], nextp: proc (v: O1): Parser[T, O2]): Parser[T, O2] = + Parser(proc (inp: Input[T]): Result[T, O2] = + let r = self.runInput(inp) + case r.kind: + of rkSuccess: + nextp(r.output).runInput(r.input) + of rkFailure: + Result[T, O2](kind: rkFailure)) + +method skip[T](self: Input[T], n: int): Input[T] = + Input[T](toks: self.toks, index: self.index + n) + +proc pskip*[T](n: int): Parser[T, tuple[]] = + Parser(proc (inp: Input[T]): Result[T, tuple[]] = + if inp.index + n <= inp.toks.len: + Result[T, tuple[]](kind: rkSuccess, output: (), input: inp.skip(n)) + else: + Result[T, tuple[]](kind: rkFailure)) + +proc tok*[T](t: T): Parser[T, T] = + Parser(proc (inp: Input[T]): Result[T, T] = + if inp.index < inp.toks.len and inp.toks[inp.index] == t: + pskip[T](1).then(unit[T, T](t)).runInput(inp) + else: + Result[T, T](kind: rkFailure)) + +proc `+`*[T, O](first: Parser[T, O], second: Parser[T, O]): Parser[T, O] = + Parser(proc (inp: Input[T]): Result[T, O] = + let r = first.runInput(inp) + case r.kind + of rkSuccess: + r + else: + second.runInput(inp)) + +# end of primitives (definitions involving Parser(..)) + +method map*[T, O1, O2](self: Parser[T, O1], p: proc (v: O1): O2): Parser[T, O2] = + self.chain(proc (v: O1): Parser[T, O2] = + unit[T, O2](p(v))) + +method then*[T, O1, O2](self: Parser[T, O1], next: Parser[T, O2]): Parser[T, O2] = + self.chain(proc (v: O1): Parser[T, O2] = + next) + +proc `*`*[T, O1, O2](first: Parser[T, O1], second: Parser[T, O2]): Parser[T, (O1, O2)] = + first.chain(proc (v1: O1): Parser[T, (O1, O2)] = + second.map(proc (v2: O2): (O1, O2) = + (v1, v2))) + +proc repeat0*[T, O](inner: Parser[T, O]): Parser[T, seq[O]] = + var nothing = unit[T, seq[O]](@[]) + inner.chain(proc(v: O): Parser[T, seq[O]] = + repeat0(inner).map(proc(vs: seq[O]): seq[O] = + @[v] & vs)) + nothing + +proc repeat1*[T, O](inner: Parser[T, O]): Parser[T, seq[O]] = + inner.chain(proc(v: O): Parser[T, seq[O]] = + repeat0(inner).map(proc(vs: seq[O]): seq[O] = + @[v] & vs)) + +proc leftRec*[T, O, A](inner: Parser[T, O], after: Parser[T, A], fold: proc(i: O, a: A): O): Parser[T, O] = + (inner*repeat0(after)).map(proc(ias: (O, seq[A])): O = + var (i, asx) = ias + for a in asx: + i = fold(i, a) + i) + +proc lazy*[T, O](inner: proc(): Parser[T, O]): Parser[T, O] = + unit[T, tuple[]](()).chain(proc(v: tuple[]): Parser[T, O] = + inner()) diff --git a/tests/misc/tparsecombnum.nim b/tests/misc/tparsecombnum.nim new file mode 100644 index 000000000..6fe539813 --- /dev/null +++ b/tests/misc/tparsecombnum.nim @@ -0,0 +1,55 @@ +import parsecomb + +discard """ + output: "-289096" +""" + +type Num = int + +# forward stuff +var exp3: Parser[string, Num] +var exp = lazy(proc(): Parser[string, Num] = exp3) + +var digit = (proc(): Parser[string, Num] = + result = tok("0").then(unit[string, Num](Num(0))) + for n in 1..9: + result = result + tok($n).then(unit[string, Num](Num(n))) +)() + +var num = repeat1(digit).map(proc(ds: seq[Num]): Num = + result = 0 + for d in ds: + result = result*10 + d) + +type Op = proc(a, b: Num): Num + +var plusOp = tok("+").then(unit[string, Op](proc(a, b: Num): Num = a + b)) +var minusOp = tok("-").then(unit[string, Op](proc(a, b: Num): Num = a - b)) +var timesOp = tok("*").then(unit[string, Op](proc(a, b: Num): Num = a*b)) +var divideOp = tok("/").then(unit[string, Op](proc(a, b: Num): Num = a div b)) + +var paren = (tok("(") * exp * tok(")")).map(proc(ler: ((string, Num), string)): Num = + var (le, r) = ler + var (l, e) = le + e) + +proc foldOp(a: Num, ob: (Op, Num)): Num = + var (o, b) = ob + o(a, b) + +var exp0 = paren + num +var exp1 = exp0.leftRec((timesOp + divideOp)*exp0, foldOp) +var exp2 = exp1.leftRec((plusOp + minusOp)*exp1, foldOp) +exp3 = exp2 + +proc strsplit(s: string): seq[string] = + result = @[] + for i in 0 .. s.len - 1: + result.add($s[i]) + +var r = exp.run("523-(1243+411/744*1642/1323)*233".strsplit) +case r.kind: +of rkSuccess: + echo r.output +of rkFailure: + echo "failed" diff --git a/tests/overload/tsymtabchange_during_or.nim b/tests/overload/tsymtabchange_during_or.nim new file mode 100644 index 000000000..b5551bcc7 --- /dev/null +++ b/tests/overload/tsymtabchange_during_or.nim @@ -0,0 +1,24 @@ + +# bug #2229 + +type Type1 = object + id: int + +type Type2 = object + id: int + +proc init(self: var Type1, a: int, b: ref Type2) = + echo "1" + +proc init(self: var Type2, a: int) = + echo """ + Works when this proc commented out + Otherwise error: + test.nim(14, 4) Error: ambiguous call; both test.init(self: var Type1, a: int, b: ref Type2) and test.init(self: var Type1, a: int, b: ref Type2) match for: (Type1, int literal(1), ref Type2) + """ + +var a: Type1 +init(a, 1, ( + var b = new(Type2); + b +)) diff --git a/tests/template/texponential_eval.nim b/tests/template/texponential_eval.nim new file mode 100644 index 000000000..32af9e8f7 --- /dev/null +++ b/tests/template/texponential_eval.nim @@ -0,0 +1,47 @@ +# bug #1940 + +discard """ + nimout: '''=== +merge (A) with (B) +merge (A B) with (C) +merge (A B C) with (D) +merge (A B C D) with (E) +merge (A B C D E) with (F) +===''' +""" + +type SqlStmt = tuple + sql: string + parts: int + +proc sql(q: string): SqlStmt = + result.sql = q + result.parts = 1 + +template `&%%`(x, y: SqlStmt): SqlStmt = + const a = x + const b = y + + static: + #echo "some merge" + echo "merge (", a.sql, ") with (", b.sql, ")" + + + const newSql = a.sql & " " & b.sql + const newParts = a.parts + b.parts + + SqlStmt((sql: newSql, parts: newParts)) + +static: + echo "===" + +let c =(sql("A") &%% + sql("B")) &%% + sql("C") &%% + sql("D") &%% + sql("E") &%% + sql("F") +echo c.sql + +static: + echo "===" diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index ab1e46d6f..323abd768 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -302,8 +302,7 @@ proc testNimblePackages(r: var TResults, cat: Category, filter: PackageFilter) = continue let - buildPath = getPackageDir(name)[0.. -3] - let + buildPath = getPackageDir(name).strip buildProcess = startProcess(nimbleExe, buildPath, ["build"]) buildStatus = waitForExitEx(buildProcess) buildProcess.close @@ -318,7 +317,7 @@ proc testNimblePackages(r: var TResults, cat: Category, filter: PackageFilter) = # ---------------------------------------------------------------------------- -const AdditionalCategories = ["debugger", "examples", "lib", "nimble-core"] +const AdditionalCategories = ["debugger", "examples", "lib"] proc `&.?`(a, b: string): string = # candidate for the stdlib? diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index 881a41ce6..0a0d08173 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -141,7 +141,7 @@ proc generatedFile(path, name: string, target: TTarget): string = (if target == targetJS: path.splitPath.tail & "_" else: "compiler_") & name.changeFileExt(ext) -proc codegenCheck(test: TTest, check: string, given: var TSpec) = +proc codegenCheck(test: TTest, check: string, given: var TSpec, r: var TResults) = if check.len > 0: try: let (path, name, ext2) = test.name.splitFile @@ -154,19 +154,29 @@ proc codegenCheck(test: TTest, check: string, given: var TSpec) = given.err = reInvalidPeg except IOError: given.err = reCodeNotFound + r.addResult(test, "", given.msg, given.err) -proc nimoutCheck(test: TTest; expectedNimout: string; given: var TSpec) = +proc nimoutCheck(test: TTest; expectedNimout: string; given: var TSpec, r: var TResults) = if expectedNimout.len > 0: let exp = expectedNimout.strip.replace("\C\L", "\L") let giv = given.nimout.strip.replace("\C\L", "\L") if exp notin giv: given.err = reMsgsDiffer + r.addResult(test, exp, giv, given.err) + proc makeDeterministic(s: string): string = var x = splitLines(s) sort(x, system.cmp) result = join(x, "\n") +proc compilerOutputTests(test: TTest, given: var TSpec, expected: TSpec, r: var TResults) = + if given.err == reSuccess: + codegenCheck(test, expected.ccodeCheck, given, r) + nimoutCheck(test, expected.nimout, given, r) + if given.err == reSuccess: inc(r.passed) + + proc testSpec(r: var TResults, test: TTest) = # major entry point for a single test let tname = test.name.addFileExt(".nim") @@ -179,13 +189,9 @@ proc testSpec(r: var TResults, test: TTest) = else: case expected.action of actionCompile: - var given = callCompiler(expected.cmd, test.name, test.options, - test.target) - if given.err == reSuccess: - codegenCheck(test, expected.ccodeCheck, given) - nimoutCheck(test, expected.nimout, given) - r.addResult(test, "", given.msg, given.err) - if given.err == reSuccess: inc(r.passed) + var given = callCompiler(expected.cmd, test.name, + test.options & " --hint[Path]:off --hint[Processing]:off", test.target) + compilerOutputTests(test, given, expected, r) of actionRun: var given = callCompiler(expected.cmd, test.name, test.options, test.target) @@ -215,11 +221,7 @@ proc testSpec(r: var TResults, test: TTest) = if bufB != strip(expected.outp): if not (expected.substr and expected.outp in bufB): given.err = reOutputsDiffer - if given.err == reSuccess: - codeGenCheck(test, expected.ccodeCheck, given) - nimoutCheck(test, expected.nimout, given) - if given.err == reSuccess: inc(r.passed) - r.addResult(test, expected.outp, buf.string, given.err) + compilerOutputTests(test, given, expected, r) else: r.addResult(test, expected.outp, "executable not found", reExeNotFound) of actionReject: diff --git a/tests/vm/tconsteval.nim b/tests/vm/tconsteval.nim index 96a1bafe8..4459931c5 100644 --- a/tests/vm/tconsteval.nim +++ b/tests/vm/tconsteval.nim @@ -24,7 +24,7 @@ Possible Commands: csource [options] builds the C sources for installation zip builds the installation ZIP package inno builds the Inno Setup installer -""" % [NimVersion & repeatChar(44-len(NimVersion)), +""" % [NimVersion & spaces(44-len(NimVersion)), CompileDate, CompileTime] echo HelpText diff --git a/todo.txt b/todo.txt index 1d180f737..69674f019 100644 --- a/todo.txt +++ b/todo.txt @@ -53,7 +53,6 @@ Bugs - VM: Pegs do not work at compile-time - VM: ptr/ref T cannot work in general - scopes are still broken for generic instantiation! -- compilation of niminst takes way too long. looks like a regression - blocks can "export" an identifier but the CCG generates {} for them ... diff --git a/tools/nimgrep.nim b/tools/nimgrep.nim index aeae86300..72e4adc07 100644 --- a/tools/nimgrep.nim +++ b/tools/nimgrep.nim @@ -109,7 +109,7 @@ proc highlight(s, match, repl: string, t: tuple[first, last: int], for i in t.last+1 .. y: stdout.write(s[i]) stdout.write("\n") if showRepl: - stdout.write(repeatChar(alignment-1), "-> ") + stdout.write(spaces(alignment-1), "-> ") for i in x .. t.first-1: stdout.write(s[i]) writeColored(repl) for i in t.last+1 .. y: stdout.write(s[i]) diff --git a/tools/niminst/buildsh.tmpl b/tools/niminst/buildsh.tmpl index 191c39282..980915be3 100644 --- a/tools/niminst/buildsh.tmpl +++ b/tools/niminst/buildsh.tmpl @@ -62,7 +62,7 @@ case $uos in myos="freebsd" CC="clang" LINKER="clang" - LINK_FLAGS="$LINK_FLAGS -ldl -lm" + LINK_FLAGS="$LINK_FLAGS -lm" ;; *openbsd* ) myos="openbsd" diff --git a/tools/nimweb.nim b/tools/nimweb.nim index 8213cf418..a7301195e 100644 --- a/tools/nimweb.nim +++ b/tools/nimweb.nim @@ -99,8 +99,8 @@ macro updated(e: expr): expr {.immediate.} = proc updatedDate(year, month, day: string): string = ## wrapper around the update macro with easy input. result = updated("$1-$2-$3T00:00:00Z" % [year, - repeatStr(2 - len(month), "0") & month, - repeatStr(2 - len(day), "0") & day]) + repeat("0", 2 - len(month)) & month, + repeat("0", 2 - len(day)) & day]) macro entry(e: expr): expr {.immediate.} = ## generates the rss xml ``entry`` element. diff --git a/web/website.ini b/web/website.ini index 696829764..6266f05bb 100644 --- a/web/website.ini +++ b/web/website.ini @@ -59,6 +59,7 @@ srcdoc2: "pure/nimprof;pure/unittest;packages/docutils/highlite" srcdoc2: "packages/docutils/rst;packages/docutils/rstast" srcdoc2: "packages/docutils/rstgen;pure/logging;pure/asyncdispatch;pure/asyncnet" srcdoc2: "pure/rawsockets;pure/asynchttpserver;pure/net;pure/selectors;pure/future" +srcdoc2: "pure/asyncfile" srcdoc2: "pure/md5" srcdoc2: "posix/posix" srcdoc2: "pure/fenv" |