diff options
105 files changed, 3745 insertions, 3621 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 1462d58d5..ab315877a 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,9 +457,9 @@ 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 + # other type so that it requires initalization tfVarIsPtr, # 'var' type is translated like 'ptr' even in C++ mode tfHasMeta, # type contains "wildcard" sub-types such as generic params # or other type classes @@ -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,45 +807,45 @@ type lockLevel*: TLockLevel # lock level as required for deadlock checking loc*: TLoc - TPair*{.final.} = object + TPair* = object key*, val*: RootRef TPairSeq* = seq[TPair] - TTable*{.final.} = object # the same as table[PObject] of PObject + TTable* = object # the same as table[PObject] of PObject counter*: int data*: TPairSeq - TIdPair*{.final.} = object + TIdPair* = object key*: PIdObj val*: RootRef TIdPairSeq* = seq[TIdPair] - TIdTable*{.final.} = object # the same as table[PIdent] of PObject + TIdTable* = object # the same as table[PIdent] of PObject counter*: int data*: TIdPairSeq - TIdNodePair*{.final.} = object + TIdNodePair* = object key*: PIdObj val*: PNode TIdNodePairSeq* = seq[TIdNodePair] - TIdNodeTable*{.final.} = object # the same as table[PIdObj] of PNode + TIdNodeTable* = object # the same as table[PIdObj] of PNode counter*: int data*: TIdNodePairSeq - TNodePair*{.final.} = object + TNodePair* = object h*: THash # because it is expensive to compute! key*: PNode val*: int TNodePairSeq* = seq[TNodePair] - TNodeTable*{.final.} = object # the same as table[PNode] of int; + TNodeTable* = object # the same as table[PNode] of int; # nodes are compared by structure! counter*: int data*: TNodePairSeq TObjectSeq* = seq[RootRef] - TObjectSet*{.final.} = object + TObjectSet* = 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) @@ -1320,18 +1318,18 @@ proc isGCedMem*(t: PType): bool {.inline.} = t.kind == tyProc and t.callConv == ccClosure proc propagateToOwner*(owner, elem: PType) = - const HaveTheirOwnEmpty = {tySequence, tySet} + const HaveTheirOwnEmpty = {tySequence, tySet, tyPtr, tyRef, tyProc} owner.flags = owner.flags + (elem.flags * {tfHasMeta}) if tfNotNil in elem.flags: if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvocation}: 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/ccgcalls.nim b/compiler/ccgcalls.nim index 200ff91e0..86f300aa0 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -135,14 +135,14 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): PRope = elif ccgIntroducedPtr(param): initLocExpr(p, n, a) result = addrLoc(a) - elif p.module.compileToCpp and param.typ.kind == tyVar and + elif p.module.compileToCpp and param.typ.kind == tyVar and n.kind == nkHiddenAddr: initLocExprSingleUse(p, n.sons[0], a) # if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still # means '*T'. See posix.nim for lots of examples that do that in the wild. let callee = call.sons[0] if callee.kind == nkSym and - {sfImportC, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportC} and + {sfImportC, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportC} and {lfHeader, lfNoDecl} * callee.sym.loc.flags != {}: result = addrLoc(a) else: @@ -192,7 +192,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = var op: TLoc initLocExpr(p, ri.sons[0], op) var pl: PRope - + var typ = skipTypes(ri.sons[0].typ, abstractInst) assert(typ.kind == tyProc) var length = sonsLen(ri) @@ -204,7 +204,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = else: app(pl, genArgNoParam(p, ri.sons[i])) if i < length - 1: app(pl, ~", ") - + template genCallPattern {.dirty.} = lineF(p, cpsStmts, callPattern & ";$n", op.r, pl, pl.addComma, rawProc) @@ -339,7 +339,8 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): PRope = let typ = skipTypes(ri.sons[0].typ, abstractInst) if pat[i+1] == '+': result.app genArgNoParam(p, ri.sons[0]) result.app(~"(") - result.app genOtherArg(p, ri, 1, typ) + if 1 < ri.len: + result.app genOtherArg(p, ri, 1, typ) for k in j+1 .. < ri.len: result.app(~", ") result.app genOtherArg(p, ri, k, typ) 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/guards.nim b/compiler/guards.nim index 3255364c2..b0420cb75 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -15,11 +15,11 @@ import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents, const someEq = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, mEqUntracedRef, mEqStr, mEqSet, mEqCString} - + # set excluded here as the semantics are vastly different: someLe = {mLeI, mLeI64, mLeF64, mLeU, mLeU64, mLeEnum, mLeCh, mLeB, mLePtr, mLeStr} - someLt = {mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum, + someLt = {mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum, mLtCh, mLtB, mLtPtr, mLtStr} someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq} @@ -44,7 +44,7 @@ proc isLet(n: PNode): bool = if n.kind == nkSym: if n.sym.kind in {skLet, skTemp, skForVar}: result = true - elif n.sym.kind == skParam and skipTypes(n.sym.typ, + elif n.sym.kind == skParam and skipTypes(n.sym.typ, abstractInst).kind != tyVar: result = true @@ -117,7 +117,7 @@ proc neg(n: PNode): PNode = result.sons[0] = n.sons[0] result.sons[2] = n.sons[2] if t.kind == tyEnum: - var s = newNodeIT(nkCurly, n.info, n.sons[1].typ) + var s = newNodeIT(nkCurly, n.info, n.sons[1].typ) for e in t.n: let eAsNode = newIntNode(nkIntLit, e.sym.position) if not inSet(n.sons[1], eAsNode): s.add eAsNode @@ -189,13 +189,17 @@ proc zero(): PNode = nkIntLit.newIntNode(0) proc one(): PNode = nkIntLit.newIntNode(1) proc minusOne(): PNode = nkIntLit.newIntNode(-1) -proc lowBound*(x: PNode): PNode = +proc lowBound*(x: PNode): PNode = result = nkIntLit.newIntNode(firstOrd(x.typ)) result.info = x.info proc highBound*(x: PNode): PNode = - result = if x.typ.skipTypes(abstractInst).kind == tyArray: - nkIntLit.newIntNode(lastOrd(x.typ)) + let typ = x.typ.skipTypes(abstractInst) + result = if typ.kind in {tyArrayConstr, tyArray}: + nkIntLit.newIntNode(lastOrd(typ)) + elif typ.kind == tySequence and x.kind == nkSym and + x.sym.kind == skConst: + nkIntLit.newIntNode(x.sym.ast.len-1) else: opAdd.buildCall(opLen.buildCall(x), minusOne()) result.info = x.info @@ -205,21 +209,32 @@ proc reassociation(n: PNode): PNode = # (foo+5)+5 --> foo+10; same for '*' case result.getMagic of someAdd: - if result[2].isValue and + if result[2].isValue and result[1].getMagic in someAdd and result[1][2].isValue: result = opAdd.buildCall(result[1][1], result[1][2] |+| result[2]) of someMul: - if result[2].isValue and + if result[2].isValue and result[1].getMagic in someMul and result[1][2].isValue: result = opAdd.buildCall(result[1][1], result[1][2] |*| result[2]) else: discard +proc pred(n: PNode): PNode = + if n.kind in {nkCharLit..nkUInt64Lit} and n.intVal != low(BiggestInt): + result = copyNode(n) + dec result.intVal + else: + result = n + proc canon*(n: PNode): PNode = # XXX for now only the new code in 'semparallel' uses this if n.safeLen >= 1: result = shallowCopy(n) for i in 0 .. < n.len: result.sons[i] = canon(n.sons[i]) + elif n.kind == nkSym and n.sym.kind == skLet and + n.sym.ast.getMagic in (someEq + someAdd + someMul + someMin + + someMax + someHigh + {mUnaryLt} + someSub + someLen): + result = n.sym.ast.copyTree else: result = n case result.getMagic @@ -231,34 +246,40 @@ proc canon*(n: PNode): PNode = of someHigh: # high == len+(-1) result = opAdd.buildCall(opLen.buildCall(result[1]), minusOne()) - of mUnaryMinusI, mUnaryMinusI64: + of mUnaryLt: result = buildCall(opAdd, result[1], newIntNode(nkIntLit, -1)) of someSub: # x - 4 --> x + (-4) result = negate(result[1], result[2], result) of someLen: result.sons[0] = opLen.newSymNode + of someLt: + # x < y same as x <= y-1: + let y = n[2].canon + let p = pred(y) + let minus = if p != y: p else: opAdd.buildCall(y, minusOne()).canon + result = opLe.buildCall(n[1].canon, minus) else: discard result = skipConv(result) result = reassociation(result) - # most important rule: (x-4) < a.len --> x < a.len+4 + # most important rule: (x-4) <= a.len --> x <= a.len+4 case result.getMagic - of someLe, someLt: + of someLe: let x = result[1] let y = result[2] - if x.kind in nkCallKinds and x.len == 3 and x[2].isValue and + if x.kind in nkCallKinds and x.len == 3 and x[2].isValue and isLetLocation(x[1], true): case x.getMagic of someSub: - result = buildCall(result[0].sym, x[1], + result = buildCall(result[0].sym, x[1], reassociation(opAdd.buildCall(y, x[2]))) of someAdd: # Rule A: let plus = negate(y, x[2], nil).reassociation if plus != nil: result = buildCall(result[0].sym, x[1], plus) else: discard - elif y.kind in nkCallKinds and y.len == 3 and y[2].isValue and + elif y.kind in nkCallKinds and y.len == 3 and y[2].isValue and isLetLocation(y[1], true): # a.len < x-3 case y.getMagic @@ -317,7 +338,7 @@ proc usefulFact(n: PNode): PNode = of mOr: # 'or' sucks! (p.isNil or q.isNil) --> hard to do anything # with that knowledge... - # DeMorgan helps a little though: + # DeMorgan helps a little though: # not a or not b --> not (a and b) # (x == 3) or (y == 2) ---> not ( not (x==3) and not (y == 2)) # not (x != 3 and y != 2) @@ -348,11 +369,11 @@ proc addFact*(m: var TModel, n: PNode) = let n = usefulFact(n) if n != nil: m.add n -proc addFactNeg*(m: var TModel, n: PNode) = +proc addFactNeg*(m: var TModel, n: PNode) = let n = n.neg if n != nil: addFact(m, n) -proc sameTree*(a, b: PNode): bool = +proc sameTree*(a, b: PNode): bool = result = false if a == b: result = true @@ -382,8 +403,8 @@ proc invalidateFacts*(m: var TModel, n: PNode) = # 'while p != nil: f(p); p = p.next' # This is actually quite easy to do: # Re-assignments (incl. pass to a 'var' param) trigger an invalidation - # of every fact that contains 'v'. - # + # of every fact that contains 'v'. + # # if x < 4: # if y < 5 # x = unknown() @@ -402,16 +423,9 @@ proc valuesUnequal(a, b: PNode): bool = if a.isValue and b.isValue: result = not sameValue(a, b) -proc pred(n: PNode): PNode = - if n.kind in {nkCharLit..nkUInt64Lit} and n.intVal != low(BiggestInt): - result = copyNode(n) - dec result.intVal - else: - result = n - proc impliesEq(fact, eq: PNode): TImplication = let (loc, val) = if isLocation(eq.sons[1]): (1, 2) else: (2, 1) - + case fact.sons[0].sym.magic of someEq: if sameTree(fact.sons[1], eq.sons[loc]): @@ -428,12 +442,12 @@ proc impliesEq(fact, eq: PNode): TImplication = else: result = impNo of mNot, mOr, mAnd: internalError(eq.info, "impliesEq") else: discard - + proc leImpliesIn(x, c, aSet: PNode): TImplication = if c.kind in {nkCharLit..nkUInt64Lit}: # fact: x <= 4; question x in {56}? # --> true if every value <= 4 is in the set {56} - # + # var value = newIntNode(c.kind, firstOrd(x.typ)) # don't iterate too often: if c.intVal - value.intVal < 1000: @@ -449,7 +463,7 @@ proc geImpliesIn(x, c, aSet: PNode): TImplication = if c.kind in {nkCharLit..nkUInt64Lit}: # fact: x >= 4; question x in {56}? # --> true iff every value >= 4 is in the set {56} - # + # var value = newIntNode(c.kind, c.intVal) let max = lastOrd(x.typ) # don't iterate too often: @@ -568,19 +582,19 @@ proc impliesLe(fact, x, c: PNode): TImplication = # fact: x < 4; question x <= 2? --> we don't know elif sameTree(fact.sons[2], x): # fact: 3 < x; question: x <= 1 ? --> false iff 1 <= 3 - if isValue(fact.sons[1]) and isValue(c): + if isValue(fact.sons[1]) and isValue(c): if leValue(c, fact.sons[1]): result = impNo - + of someLe: if sameTree(fact.sons[1], x): if isValue(fact.sons[2]) and isValue(c): # fact: x <= 4; question x <= 56? --> true iff 4 <= 56 if leValue(fact.sons[2], c): result = impYes # fact: x <= 4; question x <= 2? --> we don't know - + elif sameTree(fact.sons[2], x): # fact: 3 <= x; question: x <= 2 ? --> false iff 2 < 3 - if isValue(fact.sons[1]) and isValue(c): + if isValue(fact.sons[1]) and isValue(c): if leValue(c, fact.sons[1].pred): result = impNo of mNot, mOr, mAnd: internalError(x.info, "impliesLe") @@ -614,7 +628,7 @@ proc factImplies(fact, prop: PNode): TImplication = # it's provably wrong if every value > 4 is in the set {56} # That's because we compute the implication and 'a -> not b' cannot # be treated the same as 'not a -> b' - + # (not a) -> b compute as not (a -> b) ??? # == not a or not b == not (a and b) let arg = fact.sons[1] @@ -635,7 +649,7 @@ proc factImplies(fact, prop: PNode): TImplication = if result != impUnknown: return result return factImplies(fact.sons[2], prop) else: discard - + case prop.sons[0].sym.magic of mNot: result = ~fact.factImplies(prop.sons[1]) of mIsNil: result = impliesIsNil(fact, prop) @@ -671,6 +685,7 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication proc ple(m: TModel; a, b: PNode): TImplication = template `<=?`(a,b): expr = ple(m,a,b) == impYes + # 0 <= 3 if a.isValue and b.isValue: return if leValue(a, b): impYes else: impNo @@ -744,16 +759,21 @@ proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication = # mark as used: m[i] = nil if ple(m, a, x) == impYes: - if ple(m, y, b) == impYes: return impYes + if ple(m, y, b) == impYes: + return impYes #if pleViaModelRec(m, y, b): return impYes # fact: 16 <= i # x y # question: i <= 15? no! result = impliesLe(fact, a, b) - if result != impUnknown: return result - if sameTree(y, a): - result = ple(m, b, x) - if result != impUnknown: return result + if result != impUnknown: + return result + when false: + # given: x <= y; y==a; x <= a this means: a <= b if x <= b + if sameTree(y, a): + result = ple(m, b, x) + if result != impUnknown: + return result proc pleViaModel(model: TModel; aa, bb: PNode): TImplication = # compute replacements: 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 87847204f..75c4ddfa0 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -62,13 +62,13 @@ type res: PRope # result part; index if this is an # (address, index)-tuple address: PRope # address of an (address, index)-tuple - - TBlock = object + + TBlock = object id: int # the ID of the label; positive means that it # has been used (i.e. the label should be emitted) isLoop: bool # whether it's a 'block' or 'while' - - TGlobals = object + + TGlobals = object typeInfo, code: PRope forwarded: seq[PSym] generatedSyms: IntSet @@ -98,7 +98,7 @@ proc newGlobals(): PGlobals = result.generatedSyms = initIntSet() result.typeInfoGenerated = initIntSet() -proc initCompRes(r: var TCompRes) = +proc initCompRes(r: var TCompRes) = r.address = nil r.res = nil r.typ = etyNone @@ -112,7 +112,7 @@ proc rdLoc(a: TCompRes): PRope {.inline.} = else: result = ropef("$1[$2]", a.address, a.res) -proc newProc(globals: PGlobals, module: BModule, procDef: PNode, +proc newProc(globals: PGlobals, module: BModule, procDef: PNode, options: TOptions): PProc = result = PProc( blocks: @[], @@ -121,30 +121,30 @@ proc newProc(globals: PGlobals, module: BModule, procDef: PNode, procDef: procDef, g: globals) if procDef != nil: result.prc = procDef.sons[namePos].sym - -const - MappedToObject = {tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray, + +const + MappedToObject = {tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray, tySet, tyVar, tyRef, tyPtr, tyBigNum, tyVarargs} -proc mapType(typ: PType): TJSTypeKind = +proc mapType(typ: PType): TJSTypeKind = let t = skipTypes(typ, abstractInst) case t.kind - of tyVar, tyRef, tyPtr: - if skipTypes(t.lastSon, abstractInst).kind in MappedToObject: + of tyVar, tyRef, tyPtr: + if skipTypes(t.lastSon, abstractInst).kind in MappedToObject: result = etyObject - else: + else: result = etyBaseIndex of tyPointer: # treat a tyPointer like a typed pointer to an array of bytes result = etyInt - of tyRange, tyDistinct, tyOrdinal, tyConst, tyMutable, tyIter, tyProxy: + of tyRange, tyDistinct, tyOrdinal, tyConst, tyMutable, tyIter, tyProxy: result = mapType(t.sons[0]) of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = etyInt of tyBool: result = etyBool of tyFloat..tyFloat128: result = etyFloat of tySet: result = etyObject # map a set to a table of tyString, tySequence: result = etyInt # little hack to get right semantics - of tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray, tyBigNum, + of tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray, tyBigNum, tyVarargs: result = etyObject of tyNil: result = etyNull @@ -154,10 +154,10 @@ proc mapType(typ: PType): TJSTypeKind = result = etyNone of tyProc: result = etyProc of tyCString: result = etyString - -proc mangleName(s: PSym): PRope = + +proc mangleName(s: PSym): PRope = result = s.loc.r - if result == nil: + if result == nil: result = toRope(mangle(s.name.s)) app(result, "_") app(result, toRope(s.id)) @@ -166,7 +166,7 @@ proc mangleName(s: PSym): PRope = proc makeJSString(s: string): PRope = strutils.escape(s).toRope include jstypes - + proc gen(p: PProc, n: PNode, r: var TCompRes) proc genStmt(p: PProc, n: PNode) proc genProc(oldProc: PProc, prc: PSym): PRope @@ -341,14 +341,14 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["", "", "$1", "$1"], # ToBiggestFloat ["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt ["", "", "Math.floor($1)", "Math.floor($1)"], # ToBiggestInt - ["nimCharToStr", "nimCharToStr", "nimCharToStr($1)", "nimCharToStr($1)"], + ["nimCharToStr", "nimCharToStr", "nimCharToStr($1)", "nimCharToStr($1)"], ["nimBoolToStr", "nimBoolToStr", "nimBoolToStr($1)", "nimBoolToStr($1)"], [ - "cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", - "cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", "cstrToNimstr", - "cstrToNimstr(($1)+\"\")", - "cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", - "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"], - ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr($1)", "cstrToNimstr($1)"], + "cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", + "cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", "cstrToNimstr", + "cstrToNimstr(($1)+\"\")", + "cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", + "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"], + ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr($1)", "cstrToNimstr($1)"], ["", "", "$1", "$1"]] luaOps: TMagicOps = [ @@ -441,14 +441,14 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["", "", "$1", "$1"], # ToBiggestFloat ["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt ["", "", "Math.floor($1)", "Math.floor($1)"], # ToBiggestInt - ["nimCharToStr", "nimCharToStr", "nimCharToStr($1)", "nimCharToStr($1)"], + ["nimCharToStr", "nimCharToStr", "nimCharToStr($1)", "nimCharToStr($1)"], ["nimBoolToStr", "nimBoolToStr", "nimBoolToStr($1)", "nimBoolToStr($1)"], [ - "cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", - "cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", "cstrToNimstr", - "cstrToNimstr(($1)+\"\")", - "cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", - "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"], - ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr($1)", "cstrToNimstr($1)"], + "cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", + "cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", "cstrToNimstr", + "cstrToNimstr(($1)+\"\")", + "cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", + "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"], + ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr($1)", "cstrToNimstr($1)"], ["", "", "$1", "$1"]] proc binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = @@ -502,9 +502,9 @@ proc genLineDir(p: PProc, n: PNode) = appf(p.body, "endb($1);$n", [toRope(line)]) elif ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and - ((p.prc == nil) or not (sfPure in p.prc.flags)): + ((p.prc == nil) or not (sfPure in p.prc.flags)): appf(p.body, "F.line = $1;$n", [toRope(line)]) - + proc genWhileStmt(p: PProc, n: PNode) = var cond: TCompRes @@ -533,7 +533,7 @@ proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) = src.kind = resNone src.res = nil -proc genTry(p: PProc, n: PNode, r: var TCompRes) = +proc genTry(p: PProc, n: PNode, r: var TCompRes) = # code to generate: # # var sp = {prev: excHandler, exc: null}; @@ -560,8 +560,8 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = inc(p.unique) var safePoint = ropef("Tmp$1", [toRope(p.unique)]) appf(p.body, - "var $1 = {prev: excHandler, exc: null};$nexcHandler = $1;$n" | - "local $1 = pcall(", + "var $1 = {prev: excHandler, exc: null};$nexcHandler = $1;$n" | + "local $1 = pcall(", [safePoint]) if optStackTrace in p.options: app(p.body, "framePtr = F;" & tnl) appf(p.body, "try {$n" | "function()$n") @@ -574,9 +574,9 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = appf(p.body, "} catch (EXC) {$n lastJSError = EXC;$n") elif p.target == targetLua: appf(p.body, "end)$n") - while i < length and n.sons[i].kind == nkExceptBranch: + while i < length and n.sons[i].kind == nkExceptBranch: let blen = sonsLen(n.sons[i]) - if blen == 1: + if blen == 1: # general except section: if i > 1: appf(p.body, "else {$n" | "else$n") gen(p, n.sons[i].sons[0], a) @@ -585,11 +585,11 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = else: var orExpr: PRope = nil useMagic(p, "isObj") - for j in countup(0, blen - 2): - if n.sons[i].sons[j].kind != nkType: + for j in countup(0, blen - 2): + if n.sons[i].sons[j].kind != nkType: internalError(n.info, "genTryStmt") if orExpr != nil: app(orExpr, "||" | " or ") - appf(orExpr, "isObj($1.exc.m_type, $2)", + appf(orExpr, "isObj($1.exc.m_type, $2)", [safePoint, genTypeInfo(p, n.sons[i].sons[j].typ)]) if i > 1: app(p.body, "else ") appf(p.body, "if ($1.exc && ($2)) {$n" | "if $1.exc and ($2) then$n", @@ -622,13 +622,13 @@ proc genRaiseStmt(p: PProc, n: PNode) = useMagic(p, "reraiseException") app(p.body, "reraiseException();" & tnl) -proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = +proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = var cond, stmt: TCompRes genLineDir(p, n) gen(p, n.sons[0], cond) let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString - if stringSwitch: + if stringSwitch: useMagic(p, "toJSStr") appf(p.body, "switch (toJSStr($1)) {$n", [cond.rdLoc]) else: @@ -636,25 +636,25 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = if not isEmptyType(n.typ): r.kind = resVal r.res = getTemp(p) - for i in countup(1, sonsLen(n) - 1): + for i in countup(1, sonsLen(n) - 1): let it = n.sons[i] case it.kind - of nkOfBranch: - for j in countup(0, sonsLen(it) - 2): + of nkOfBranch: + for j in countup(0, sonsLen(it) - 2): let e = it.sons[j] - if e.kind == nkRange: + if e.kind == nkRange: var v = copyNode(e.sons[0]) - while v.intVal <= e.sons[1].intVal: + while v.intVal <= e.sons[1].intVal: gen(p, v, cond) appf(p.body, "case $1: ", [cond.rdLoc]) inc(v.intVal) else: - if stringSwitch: + if stringSwitch: case e.kind - of nkStrLit..nkTripleStrLit: appf(p.body, "case $1: ", + of nkStrLit..nkTripleStrLit: appf(p.body, "case $1: ", [makeJSString(e.strVal)]) else: internalError(e.info, "jsgen.genCaseStmt: 2") - else: + else: gen(p, e, cond) appf(p.body, "case $1: ", [cond.rdLoc]) gen(p, lastSon(it), stmt) @@ -668,7 +668,7 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = else: internalError(it.info, "jsgen.genCaseStmt") appf(p.body, "}$n") -proc genCaseLua(p: PProc, n: PNode, r: var TCompRes) = +proc genCaseLua(p: PProc, n: PNode, r: var TCompRes) = var cond, stmt: TCompRes genLineDir(p, n) @@ -681,13 +681,13 @@ proc genCaseLua(p: PProc, n: PNode, r: var TCompRes) = if not isEmptyType(n.typ): r.kind = resVal r.res = getTemp(p) - for i in countup(1, sonsLen(n) - 1): + for i in countup(1, sonsLen(n) - 1): let it = n.sons[i] case it.kind of nkOfBranch: if i != 1: appf(p.body, "$nelsif ") else: appf(p.body, "if ") - for j in countup(0, sonsLen(it) - 2): + for j in countup(0, sonsLen(it) - 2): if j != 0: app(p.body, " or ") let e = it.sons[j] if e.kind == nkRange: @@ -696,7 +696,7 @@ proc genCaseLua(p: PProc, n: PNode, r: var TCompRes) = gen(p, e.sons[1], ib) appf(p.body, "$1 >= $2 and $1 <= $3", [tmp, ia.rdLoc, ib.rdLoc]) else: - if stringSwitch: + if stringSwitch: case e.kind of nkStrLit..nkTripleStrLit: appf(p.body, "eqStr($1, $2)", [tmp, makeJSString(e.strVal)]) @@ -713,11 +713,11 @@ proc genCaseLua(p: PProc, n: PNode, r: var TCompRes) = moveInto(p, stmt, r) else: internalError(it.info, "jsgen.genCaseStmt") appf(p.body, "$nend$n") - + proc genBlock(p: PProc, n: PNode, r: var TCompRes) = inc(p.unique) let idx = len(p.blocks) - if n.sons[0].kind != nkEmpty: + if n.sons[0].kind != nkEmpty: # named block? if (n.sons[0].kind != nkSym): internalError(n.info, "genBlock") var sym = n.sons[0].sym @@ -731,10 +731,10 @@ proc genBlock(p: PProc, n: PNode, r: var TCompRes) = appf(p.body, "} while(false);$n" | "$n::L$#::$n", labl.toRope) setLen(p.blocks, idx) -proc genBreakStmt(p: PProc, n: PNode) = +proc genBreakStmt(p: PProc, n: PNode) = var idx: int genLineDir(p, n) - if n.sons[0].kind != nkEmpty: + if n.sons[0].kind != nkEmpty: # named break? assert(n.sons[0].kind == nkSym) let sym = n.sons[0].sym @@ -749,24 +749,24 @@ proc genBreakStmt(p: PProc, n: PNode) = p.blocks[idx].id = abs(p.blocks[idx].id) # label is used appf(p.body, "break L$1;$n" | "goto ::L$1::;$n", [toRope(p.blocks[idx].id)]) -proc genAsmStmt(p: PProc, n: PNode) = +proc genAsmStmt(p: PProc, n: PNode) = genLineDir(p, n) assert(n.kind == nkAsmStmt) - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): case n.sons[i].kind of nkStrLit..nkTripleStrLit: app(p.body, n.sons[i].strVal) of nkSym: app(p.body, mangleName(n.sons[i].sym)) else: internalError(n.sons[i].info, "jsgen: genAsmStmt()") - -proc genIf(p: PProc, n: PNode, r: var TCompRes) = + +proc genIf(p: PProc, n: PNode, r: var TCompRes) = var cond, stmt: TCompRes var toClose = 0 if not isEmptyType(n.typ): r.kind = resVal r.res = getTemp(p) - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): let it = n.sons[i] - if sonsLen(it) != 1: + if sonsLen(it) != 1: if i > 0: appf(p.body, "else {$n" | "else$n", []) inc(toClose) @@ -793,18 +793,18 @@ proc generateHeader(p: PProc, typ: PType): PRope = if isCompileTimeOnly(param.typ): continue var name = mangleName(param) app(result, name) - if mapType(param.typ) == etyBaseIndex: + if mapType(param.typ) == etyBaseIndex: app(result, ", ") app(result, name) app(result, "_Idx") -const - nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, - nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkObjConstr, nkStringToCString, - nkCStringToString, nkCall, nkPrefix, nkPostfix, nkInfix, +const + nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, + nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkObjConstr, nkStringToCString, + nkCStringToString, nkCall, nkPrefix, nkPostfix, nkInfix, nkCommand, nkHiddenCallConv, nkCallStrLit} -proc needsNoCopy(y: PNode): bool = +proc needsNoCopy(y: PNode): bool = result = (y.kind in nodeKindsNeedNoCopy) or (skipTypes(y.typ, abstractInst).kind in {tyRef, tyPtr, tyVar}) @@ -820,22 +820,22 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = useMagic(p, "nimCopy") appf(p.body, "$1 = nimCopy($2, $3);$n", [a.res, b.res, genTypeInfo(p, y.typ)]) - of etyBaseIndex: - if a.typ != etyBaseIndex or b.typ != etyBaseIndex: + of etyBaseIndex: + if a.typ != etyBaseIndex or b.typ != etyBaseIndex: internalError(x.info, "genAsgn") appf(p.body, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res]) else: appf(p.body, "$1 = $2;$n", [a.res, b.res]) -proc genAsgn(p: PProc, n: PNode) = +proc genAsgn(p: PProc, n: PNode) = genLineDir(p, n) genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=false) -proc genFastAsgn(p: PProc, n: PNode) = +proc genFastAsgn(p: PProc, n: PNode) = genLineDir(p, n) genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=true) -proc genSwap(p: PProc, n: PNode) = +proc genSwap(p: PProc, n: PNode) = var a, b: TCompRes gen(p, n.sons[1], a) gen(p, n.sons[2], b) @@ -844,7 +844,7 @@ proc genSwap(p: PProc, n: PNode) = if mapType(skipTypes(n.sons[1].typ, abstractVar)) == etyBaseIndex: inc(p.unique) let tmp2 = ropef("Tmp$1", [toRope(p.unique)]) - if a.typ != etyBaseIndex or b.typ != etyBaseIndex: + if a.typ != etyBaseIndex or b.typ != etyBaseIndex: internalError(n.info, "genSwap") appf(p.body, "var $1 = $2; $2 = $3; $3 = $1;$n" | "local $1 = $2; $2 = $3; $3 = $1;$n", [ @@ -859,7 +859,7 @@ proc getFieldPosition(f: PNode): int = of nkSym: result = f.sym.position else: internalError(f.info, "genFieldPosition") -proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) = +proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes r.typ = etyBaseIndex let b = if n.kind == nkHiddenAddr: n.sons[0] else: n @@ -875,7 +875,7 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) = r.address = a.res r.kind = resExpr -proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) = +proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) = r.typ = etyNone gen(p, n.sons[0], r) if skipTypes(n.sons[0].typ, abstractVarRange).kind == tyTuple: @@ -887,14 +887,16 @@ proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) = r.res = ropef("$1.$2", [r.res, f.loc.r]) r.kind = resExpr -proc genCheckedFieldAddr(p: PProc, n: PNode, r: var TCompRes) = - genFieldAddr(p, n.sons[0], r) # XXX - -proc genCheckedFieldAccess(p: PProc, n: PNode, r: var TCompRes) = +proc genCheckedFieldAddr(p: PProc, n: PNode, r: var TCompRes) = + let m = if n.kind == nkHiddenAddr: n.sons[0] else: n + internalAssert m.kind == nkCheckedFieldExpr + genFieldAddr(p, m.sons[0], r) # XXX + +proc genCheckedFieldAccess(p: PProc, n: PNode, r: var TCompRes) = genFieldAccess(p, n.sons[0], r) # XXX - -proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = - var + +proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = + var a, b: TCompRes first: BiggestInt r.typ = etyBaseIndex @@ -908,7 +910,7 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = else: first = 0 if optBoundsCheck in p.options and not isConstExpr(m.sons[1]): useMagic(p, "chckIndx") - r.res = ropef("chckIndx($1, $2, $3.length)-$2", + r.res = ropef("chckIndx($1, $2, $3.length)-$2", [b.res, toRope(first), a.res]) elif first != 0: r.res = ropef("($1)-$2", [b.res, toRope(first)]) @@ -916,14 +918,14 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = r.res = b.res r.kind = resExpr -proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) = +proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) = var ty = skipTypes(n.sons[0].typ, abstractVarRange) if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) case ty.kind of tyArray, tyArrayConstr, tyOpenArray, tySequence, tyString, tyCString, tyVarargs: genArrayAddr(p, n, r) - of tyTuple: + of tyTuple: genFieldAddr(p, n, r) else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')') r.typ = etyNone @@ -967,12 +969,12 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) = of tyArray, tyArrayConstr, tyOpenArray, tySequence, tyString, tyCString, tyVarargs: genArrayAddr(p, n, r) - of tyTuple: + of tyTuple: genFieldAddr(p, n, r) else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')') else: internalError(n.info, "genAddr") - -proc genSym(p: PProc, n: PNode, r: var TCompRes) = + +proc genSym(p: PProc, n: PNode, r: var TCompRes) = var s = n.sym case s.kind of skVar, skLet, skParam, skTemp, skResult: @@ -1019,9 +1021,9 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = internalError(n.info, "symbol has no generated name: " & s.name.s) r.res = s.loc.r r.kind = resVal - -proc genDeref(p: PProc, n: PNode, r: var TCompRes) = - if mapType(n.sons[0].typ) == etyObject: + +proc genDeref(p: PProc, n: PNode, r: var TCompRes) = + if mapType(n.sons[0].typ) == etyObject: gen(p, n.sons[0], r) else: var a: TCompRes @@ -1041,15 +1043,15 @@ proc genArg(p: PProc, n: PNode, r: var TCompRes) = proc genArgs(p: PProc, n: PNode, r: var TCompRes) = app(r.res, "(") - for i in countup(1, sonsLen(n) - 1): + for i in countup(1, sonsLen(n) - 1): let it = n.sons[i] - if it.typ.isCompileTimeOnly: continue + if it.typ.isCompileTimeOnly: continue if i > 1: app(r.res, ", ") genArg(p, it, r) app(r.res, ")") r.kind = resExpr -proc genCall(p: PProc, n: PNode, r: var TCompRes) = +proc genCall(p: PProc, n: PNode, r: var TCompRes) = gen(p, n.sons[0], r) genArgs(p, n, r) @@ -1065,7 +1067,7 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) = var op: TCompRes gen(p, n.sons[0], op) app(r.res, op.res) - + app(r.res, "(") for i in countup(2, sonsLen(n) - 1): if i > 2: app(r.res, ", ") @@ -1080,88 +1082,88 @@ proc genEcho(p: PProc, n: PNode, r: var TCompRes) = internalAssert n.kind == nkBracket for i in countup(0, sonsLen(n) - 1): let it = n.sons[i] - if it.typ.isCompileTimeOnly: continue + if it.typ.isCompileTimeOnly: continue if i > 0: app(r.res, ", ") genArg(p, it, r) app(r.res, ")") r.kind = resExpr -proc putToSeq(s: string, indirect: bool): PRope = +proc putToSeq(s: string, indirect: bool): PRope = result = toRope(s) if indirect: result = ropef("[$1]", [result]) - + proc createVar(p: PProc, typ: PType, indirect: bool): PRope -proc createRecordVarAux(p: PProc, rec: PNode, c: var int): PRope = +proc createRecordVarAux(p: PProc, rec: PNode, c: var int): PRope = result = nil case rec.kind - of nkRecList: - for i in countup(0, sonsLen(rec) - 1): + of nkRecList: + for i in countup(0, sonsLen(rec) - 1): app(result, createRecordVarAux(p, rec.sons[i], c)) - of nkRecCase: + of nkRecCase: app(result, createRecordVarAux(p, rec.sons[0], c)) - for i in countup(1, sonsLen(rec) - 1): + for i in countup(1, sonsLen(rec) - 1): app(result, createRecordVarAux(p, lastSon(rec.sons[i]), c)) - of nkSym: + of nkSym: if c > 0: app(result, ", ") app(result, mangleName(rec.sym)) app(result, ": ") app(result, createVar(p, rec.sym.typ, false)) inc(c) else: internalError(rec.info, "createRecordVarAux") - -proc createVar(p: PProc, typ: PType, indirect: bool): PRope = + +proc createVar(p: PProc, typ: PType, indirect: bool): PRope = var t = skipTypes(typ, abstractInst) case t.kind - of tyInt..tyInt64, tyEnum, tyChar: + of tyInt..tyInt64, tyEnum, tyChar: result = putToSeq("0", indirect) - of tyFloat..tyFloat128: + of tyFloat..tyFloat128: result = putToSeq("0.0", indirect) - of tyRange, tyGenericInst: + of tyRange, tyGenericInst: result = createVar(p, lastSon(typ), indirect) - of tySet: + of tySet: result = toRope("{}") - of tyBool: + of tyBool: result = putToSeq("false", indirect) - of tyArray, tyArrayConstr: + of tyArray, tyArrayConstr: var length = int(lengthOrd(t)) var e = elemType(t) - if length > 32: + if length > 32: useMagic(p, "arrayConstr") # XXX: arrayConstr depends on nimCopy. This line shouldn't be necessary. useMagic(p, "nimCopy") - result = ropef("arrayConstr($1, $2, $3)", [toRope(length), + result = ropef("arrayConstr($1, $2, $3)", [toRope(length), createVar(p, e, false), genTypeInfo(p, e)]) - else: + else: result = toRope("[") var i = 0 - while i < length: + while i < length: if i > 0: app(result, ", ") app(result, createVar(p, e, false)) inc(i) app(result, "]") - of tyTuple: + of tyTuple: result = toRope("{") for i in 0.. <t.sonsLen: if i > 0: app(result, ", ") - appf(result, "Field$1: $2" | "Field$# = $#", i.toRope, + appf(result, "Field$1: $2" | "Field$# = $#", i.toRope, createVar(p, t.sons[i], false)) app(result, "}") - of tyObject: + of tyObject: result = toRope("{") var c = 0 if tfFinal notin t.flags or t.sons[0] != nil: inc(c) appf(result, "m_type: $1" | "m_type = $#", [genTypeInfo(p, t)]) - while t != nil: + while t != nil: app(result, createRecordVarAux(p, t.n, c)) t = t.sons[0] app(result, "}") - of tyVar, tyPtr, tyRef: + of tyVar, tyPtr, tyRef: if mapType(t) == etyBaseIndex: result = putToSeq("[null, 0]" | "{nil, 0}", indirect) else: result = putToSeq("null" | "nil", indirect) - of tySequence, tyString, tyCString, tyPointer, tyProc: + of tySequence, tyString, tyCString, tyPointer, tyProc: result = putToSeq("null" | "nil", indirect) else: internalError("createVar: " & $t.kind) @@ -1172,42 +1174,42 @@ proc isIndirect(v: PSym): bool = (mapType(v.typ) != etyObject) and v.kind notin {skProc, skConverter, skMethod, skIterator, skClosureIterator} -proc genVarInit(p: PProc, v: PSym, n: PNode) = - var +proc genVarInit(p: PProc, v: PSym, n: PNode) = + var a: TCompRes s: PRope - if n.kind == nkEmpty: - appf(p.body, "var $1 = $2;$n" | "local $1 = $2;$n", + if n.kind == nkEmpty: + appf(p.body, "var $1 = $2;$n" | "local $1 = $2;$n", [mangleName(v), createVar(p, v.typ, isIndirect(v))]) - else: + else: discard mangleName(v) gen(p, n, a) case mapType(v.typ) - of etyObject: - if needsNoCopy(n): + of etyObject: + if needsNoCopy(n): s = a.res - else: + else: useMagic(p, "nimCopy") s = ropef("nimCopy($1, $2)", [a.res, genTypeInfo(p, n.typ)]) - of etyBaseIndex: + of etyBaseIndex: if (a.typ != etyBaseIndex): internalError(n.info, "genVarInit") - if {sfAddrTaken, sfGlobal} * v.flags != {}: - appf(p.body, "var $1 = [$2, $3];$n" | "local $1 = {$2, $3};$n", + if {sfAddrTaken, sfGlobal} * v.flags != {}: + appf(p.body, "var $1 = [$2, $3];$n" | "local $1 = {$2, $3};$n", [v.loc.r, a.address, a.res]) else: - appf(p.body, "var $1 = $2; var $1_Idx = $3;$n" | + appf(p.body, "var $1 = $2; var $1_Idx = $3;$n" | "local $1 = $2; local $1_Idx = $3;$n", [ v.loc.r, a.address, a.res]) return else: s = a.res - if isIndirect(v): + if isIndirect(v): appf(p.body, "var $1 = [$2];$n" | "local $1 = {$2};$n", [v.loc.r, s]) - else: + else: appf(p.body, "var $1 = $2;$n" | "local $1 = $2;$n", [v.loc.r, s]) - -proc genVarStmt(p: PProc, n: PNode) = - for i in countup(0, sonsLen(n) - 1): + +proc genVarStmt(p: PProc, n: PNode) = + for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind != nkCommentStmt: if a.kind == nkVarTuple: @@ -1249,7 +1251,7 @@ proc genOrd(p: PProc, n: PNode, r: var TCompRes) = of tyEnum, tyInt..tyInt64, tyChar: gen(p, n.sons[1], r) of tyBool: unaryExpr(p, n, r, "", "($1 ? 1:0)" | "toBool($#)") else: internalError(n.info, "genOrd") - + proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes @@ -1282,7 +1284,7 @@ proc genRepr(p: PProc, n: PNode, r: var TCompRes) = gen(p, n.sons[1], r) useMagic(p, "cstrToNimstr") r.kind = resExpr - r.res = ropef("cstrToNimstr($1.node.sons[$2].name)", + r.res = ropef("cstrToNimstr($1.node.sons[$2].name)", [genTypeInfo(p, t), r.res]) else: # XXX: @@ -1303,11 +1305,11 @@ proc genReset(p: PProc, n: PNode) = var x: TCompRes useMagic(p, "genericReset") gen(p, n.sons[1], x) - appf(p.body, "$1 = genericReset($1, $2);$n", [x.res, + appf(p.body, "$1 = genericReset($1, $2);$n", [x.res, genTypeInfo(p, n.sons[1].typ)]) proc genMagic(p: PProc, n: PNode, r: var TCompRes) = - var + var a: TCompRes line, filen: PRope var op = n.sons[0].sym.magic @@ -1384,34 +1386,34 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of mCopyStr: binaryExpr(p, n, r, "", "($1.slice($2))") of mCopyStrLast: ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))") of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)") - of mNewStringOfCap: unaryExpr(p, n, r, "mnewString", "mnewString(0)") + of mNewStringOfCap: unaryExpr(p, n, r, "mnewString", "mnewString(0)") else: genCall(p, n, r) #else internalError(e.info, 'genMagic: ' + magicToStr[op]); - -proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) = + +proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) = var a, b: TCompRes useMagic(p, "SetConstr") r.res = toRope("SetConstr(") r.kind = resExpr - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): if i > 0: app(r.res, ", ") var it = n.sons[i] - if it.kind == nkRange: + if it.kind == nkRange: gen(p, it.sons[0], a) gen(p, it.sons[1], b) appf(r.res, "[$1, $2]", [a.res, b.res]) - else: + else: gen(p, it, a) app(r.res, a.res) app(r.res, ")") -proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) = +proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes r.res = toRope("[") r.kind = resExpr - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): if i > 0: app(r.res, ", ") gen(p, n.sons[i], a) app(r.res, a.res) @@ -1444,7 +1446,7 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = appf(r.res, "$#: $#" | "$# = $#" , [f.loc.r, a.res]) r.res.app("}") -proc genConv(p: PProc, n: PNode, r: var TCompRes) = +proc genConv(p: PProc, n: PNode, r: var TCompRes) = var dest = skipTypes(n.typ, abstractVarRange) var src = skipTypes(n.sons[1].typ, abstractVarRange) gen(p, n.sons[1], r) @@ -1460,24 +1462,24 @@ proc genConv(p: PProc, n: PNode, r: var TCompRes) = else: # TODO: What types must we handle here? discard - -proc upConv(p: PProc, n: PNode, r: var TCompRes) = + +proc upConv(p: PProc, n: PNode, r: var TCompRes) = gen(p, n.sons[0], r) # XXX - -proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) = + +proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) = var a, b: TCompRes gen(p, n.sons[0], r) - if optRangeCheck in p.options: + if optRangeCheck in p.options: gen(p, n.sons[1], a) gen(p, n.sons[2], b) useMagic(p, "chckRange") r.res = ropef("chckRange($1, $2, $3)", [r.res, a.res, b.res]) r.kind = resExpr -proc convStrToCStr(p: PProc, n: PNode, r: var TCompRes) = +proc convStrToCStr(p: PProc, n: PNode, r: var TCompRes) = # we do an optimization here as this is likely to slow down # much of the code otherwise: - if n.sons[0].kind == nkCStringToString: + if n.sons[0].kind == nkCStringToString: gen(p, n.sons[0].sons[0], r) else: gen(p, n.sons[0], r) @@ -1486,22 +1488,22 @@ proc convStrToCStr(p: PProc, n: PNode, r: var TCompRes) = r.res = ropef("toJSStr($1)", [r.res]) r.kind = resExpr -proc convCStrToStr(p: PProc, n: PNode, r: var TCompRes) = +proc convCStrToStr(p: PProc, n: PNode, r: var TCompRes) = # we do an optimization here as this is likely to slow down # much of the code otherwise: - if n.sons[0].kind == nkStringToCString: + if n.sons[0].kind == nkStringToCString: gen(p, n.sons[0].sons[0], r) - else: + else: gen(p, n.sons[0], r) if r.res == nil: internalError(n.info, "convCStrToStr") useMagic(p, "cstrToNimstr") r.res = ropef("cstrToNimstr($1)", [r.res]) r.kind = resExpr -proc genReturnStmt(p: PProc, n: PNode) = +proc genReturnStmt(p: PProc, n: PNode) = if p.procDef == nil: internalError(n.info, "genReturnStmt") p.beforeRetNeeded = true - if (n.sons[0].kind != nkEmpty): + if (n.sons[0].kind != nkEmpty): genStmt(p, n.sons[0]) else: genLineDir(p, n) @@ -1517,22 +1519,22 @@ proc genProcBody(p: PProc, prc: PSym): PRope = else: result = nil if p.beforeRetNeeded: - appf(result, "BeforeRet: do {$n$1} while (false); $n" | + appf(result, "BeforeRet: do {$n$1} while (false); $n" | "$#;::BeforeRet::$n", [p.body]) else: app(result, p.body) - if prc.typ.callConv == ccSysCall and p.target == targetJS: + if prc.typ.callConv == ccSysCall and p.target == targetJS: result = ropef("try {$n$1} catch (e) {$n" & " alert(\"Unhandled exception:\\n\" + e.message + \"\\n\"$n}", [result]) - if optStackTrace in prc.options: + if optStackTrace in prc.options: app(result, "framePtr = framePtr.prev;" & tnl) -proc genProc(oldProc: PProc, prc: PSym): PRope = +proc genProc(oldProc: PProc, prc: PSym): PRope = var resultSym: PSym name, returnStmt, resultAsgn, header: PRope a: TCompRes - #if gVerbosity >= 3: + #if gVerbosity >= 3: # echo "BEGIN generating code for: " & prc.name.s var p = newProc(oldProc.g, oldProc.module, prc.ast, prc.options) p.target = oldProc.target @@ -1541,17 +1543,17 @@ proc genProc(oldProc: PProc, prc: PSym): PRope = resultAsgn = nil name = mangleName(prc) header = generateHeader(p, prc.typ) - if prc.typ.sons[0] != nil and sfPure notin prc.flags: + if prc.typ.sons[0] != nil and sfPure notin prc.flags: resultSym = prc.ast.sons[resultPos].sym resultAsgn = ropef("var $# = $#;$n" | "local $# = $#;$n", [ - mangleName(resultSym), + mangleName(resultSym), createVar(p, resultSym.typ, isIndirect(resultSym))]) gen(p, prc.ast.sons[resultPos], a) returnStmt = ropef("return $#;$n", [a.res]) genStmt(p, prc.getBody) result = ropef("function $#($#) {$n$#$#$#$#}$n" | "function $#($#) $n$#$#$#$#$nend$n", - [name, header, p.locals, resultAsgn, + [name, header, p.locals, resultAsgn, genProcBody(p, prc), returnStmt]) #if gVerbosity >= 3: # echo "END generated code for: " & prc.name.s @@ -1584,28 +1586,28 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = r.res = toRope"null" | toRope"nil" r.kind = resExpr of nkStrLit..nkTripleStrLit: - if skipTypes(n.typ, abstractVarRange).kind == tyString: + if skipTypes(n.typ, abstractVarRange).kind == tyString: useMagic(p, "cstrToNimstr") r.res = ropef("cstrToNimstr($1)", [makeJSString(n.strVal)]) - else: + else: r.res = makeJSString(n.strVal) r.kind = resExpr - of nkFloatLit..nkFloat64Lit: + of nkFloatLit..nkFloat64Lit: let f = n.floatVal if f != f: r.res = toRope"NaN" elif f == 0.0: r.res = toRope"0.0" - elif f == 0.5 * f: + elif f == 0.5 * f: if f > 0.0: r.res = toRope"Infinity" else: r.res = toRope"-Infinity" else: r.res = toRope(f.toStrMaxPrecision) r.kind = resExpr of nkCallKinds: - if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone): + if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone): genMagic(p, n, r) elif n.sons[0].kind == nkSym and sfInfixCall in n.sons[0].sym.flags and n.len >= 2: genInfixCall(p, n, r) - else: + else: genCall(p, n, r) of nkCurly: genSetConstr(p, n, r) of nkBracket: genArrayConstr(p, n, r) @@ -1626,7 +1628,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = of nkStringToCString: convStrToCStr(p, n, r) of nkCStringToString: convCStrToStr(p, n, r) of nkEmpty: discard - of nkLambdaKinds: + of nkLambdaKinds: let s = n.sons[namePos].sym discard mangleName(s) r.res = s.loc.r @@ -1647,9 +1649,9 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = of nkWhileStmt: genWhileStmt(p, n) of nkVarSection, nkLetSection: genVarStmt(p, n) of nkConstSection: discard - of nkForStmt, nkParForStmt: + of nkForStmt, nkParForStmt: internalError(n.info, "for statement not eliminated") - of nkCaseStmt: + of nkCaseStmt: if p.target == targetJS: genCaseJS(p, n, r) else: genCaseLua(p, n, r) of nkReturnStmt: genReturnStmt(p, n) @@ -1663,8 +1665,8 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = of nkAsmStmt: genAsmStmt(p, n) of nkTryStmt: genTry(p, n, r) of nkRaiseStmt: genRaiseStmt(p, n) - of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt, - nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, + of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt, + nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, nkFromStmt, nkTemplateDef, nkMacroDef, nkPragma: discard of nkProcDef, nkMethodDef, nkConverterDef: var s = n.sons[namePos].sym @@ -1675,33 +1677,33 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = internalError(n.info, "first class iterators not implemented") of nkPragmaBlock: gen(p, n.lastSon, r) else: internalError(n.info, "gen: unknown node type: " & $n.kind) - + var globals: PGlobals -proc newModule(module: PSym): BModule = +proc newModule(module: PSym): BModule = new(result) result.module = module if globals == nil: globals = newGlobals() - + proc genHeader(): PRope = result = ropef("/* Generated by the Nim Compiler v$1 */$n" & "/* (c) 2015 Andreas Rumpf */$n$n" & - "var framePtr = null;$n" & + "var framePtr = null;$n" & "var excHandler = null;$n" & - "var lastJSError = null;$n", + "var lastJSError = null;$n", [toRope(VersionAsString)]) -proc genModule(p: PProc, n: PNode) = +proc genModule(p: PProc, n: PNode) = if optStackTrace in p.options: appf(p.body, "var F = {procname:$1,prev:framePtr,filename:$2,line:0};$n" & "framePtr = F;$n", [ - makeJSString("module " & p.module.module.name.s), + makeJSString("module " & p.module.module.name.s), makeJSString(toFilename(p.module.module.info))]) genStmt(p, n) if optStackTrace in p.options: appf(p.body, "framePtr = framePtr.prev;$n") -proc myProcess(b: PPassContext, n: PNode): PNode = +proc myProcess(b: PPassContext, n: PNode): PNode = if passes.skipCodegen(n): return n result = n var m = BModule(b) @@ -1716,17 +1718,17 @@ proc wholeCode*(m: BModule): PRope = if not globals.generatedSyms.containsOrIncl(prc.id): var p = newProc(globals, m, nil, m.module.options) app(p.g.code, genProc(p, prc)) - + var disp = generateMethodDispatchers() - for i in 0..sonsLen(disp)-1: + for i in 0..sonsLen(disp)-1: let prc = disp.sons[i].sym if not globals.generatedSyms.containsOrIncl(prc.id): var p = newProc(globals, m, nil, m.module.options) app(p.g.code, genProc(p, prc)) result = con(globals.typeInfo, globals.code) - -proc myClose(b: PPassContext, n: PNode): PNode = + +proc myClose(b: PPassContext, n: PNode): PNode = if passes.skipCodegen(n): return n result = myProcess(b, n) var m = BModule(b) @@ -1740,11 +1742,11 @@ proc myClose(b: PPassContext, n: PNode): PNode = changeFileExt(completeCFilePath(m.module.filename), "js") discard writeRopeIfNotEqual(con(genHeader(), code), outfile) -proc myOpenCached(s: PSym, rd: PRodReader): PPassContext = +proc myOpenCached(s: PSym, rd: PRodReader): PPassContext = internalError("symbol files are not possible with the JS code generator") result = nil -proc myOpen(s: PSym): PPassContext = +proc myOpen(s: PSym): PPassContext = result = newModule(s) const JSgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose) diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim index 8b032c3ce..1288c854d 100644 --- a/compiler/jstypes.nim +++ b/compiler/jstypes.nim @@ -123,7 +123,7 @@ proc genTypeInfo(p: PProc, typ: PType): PRope = case t.kind of tyDistinct: result = genTypeInfo(p, typ.sons[0]) - of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyFloat128: + of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64: var s = ropef( "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n", [result, toRope(ord(t.kind))]) diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 0f670ae7a..a51ca9ed6 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -23,7 +23,7 @@ proc newTupleAccess*(tup: PNode, i: int): PNode = lit.intVal = i addSon(result, lit) -proc addVar*(father, v: PNode) = +proc addVar*(father, v: PNode) = var vpart = newNodeI(nkIdentDefs, v.info, 3) vpart.sons[0] = v vpart.sons[1] = ast.emptyNode @@ -53,7 +53,7 @@ proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode = let tempAsNode = newSymNode(temp) v.addVar(tempAsNode) result.add(v) - + result.add newAsgnStmt(tempAsNode, value) for i in 0 .. n.len-3: if n.sons[i].kind == nkSym: v.addVar(n.sons[i]) @@ -70,7 +70,7 @@ proc rawAddField*(obj: PType; field: PSym) = field.position = sonsLen(obj.n) addSon(obj.n, newSymNode(field)) -proc rawIndirectAccess*(a: PNode; field: PSym; info: TLineInfo): PNode = +proc rawIndirectAccess*(a: PNode; field: PSym; info: TLineInfo): PNode = # returns a[].field as a node assert field.kind == skField var deref = newNodeI(nkHiddenDeref, info) @@ -109,7 +109,7 @@ proc newDotExpr(obj, b: PSym): PNode = addSon(result, newSymNode(field)) result.typ = field.typ -proc indirectAccess*(a: PNode, b: string, info: TLineInfo): PNode = +proc indirectAccess*(a: PNode, b: string, info: TLineInfo): PNode = # returns a[].b as a node var deref = newNodeI(nkHiddenDeref, info) deref.typ = a.typ.skipTypes(abstractInst).sons[0] @@ -144,7 +144,7 @@ proc getFieldFromObj*(t: PType; v: PSym): PSym = if t == nil: break t = t.skipTypes(abstractInst) -proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode = +proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode = # returns a[].b as a node result = indirectAccess(a, b.name.s & $b.id, info) @@ -158,11 +158,11 @@ proc genAddrOf*(n: PNode): PNode = result.typ.rawAddSon(n.typ) proc genDeref*(n: PNode): PNode = - result = newNodeIT(nkHiddenDeref, n.info, + result = newNodeIT(nkHiddenDeref, n.info, n.typ.skipTypes(abstractInst).sons[0]) result.add n -proc callCodegenProc*(name: string, arg1: PNode; +proc callCodegenProc*(name: string, arg1: PNode; arg2, arg3: PNode = nil): PNode = result = newNodeI(nkCall, arg1.info) let sym = magicsys.getCompilerProc(name) @@ -203,6 +203,17 @@ proc flowVarKind(t: PType): TFlowVarKind = elif containsGarbageCollectedRef(t): fvInvalid else: fvBlob +proc typeNeedsNoDeepCopy(t: PType): bool = + var t = t.skipTypes(abstractInst) + # for the tconvexhull example (and others) we're a bit lax here and pretend + # seqs and strings are *by value* only and 'shallow' doesn't exist! + if t.kind == tyString: return true + # note that seq[T] is fine, but 'var seq[T]' is not, so we need to skip 'var' + # for the stricter check and likewise we can skip 'seq' for a less + # strict check: + if t.kind in {tyVar, tySequence}: t = t.sons[0] + result = not containsGarbageCollectedRef(t) + proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType; v: PNode; useShallowCopy=false): PSym = result = newSym(skTemp, getIdent(genPrefix), owner, varSection.info) @@ -215,7 +226,7 @@ proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType; vpart.sons[2] = if varInit.isNil: v else: ast.emptyNode varSection.add vpart if varInit != nil: - if useShallowCopy: + if useShallowCopy and typeNeedsNoDeepCopy(typ): varInit.add newFastAsgnStmt(newSymNode(result), v) else: let deepCopyCall = newNodeI(nkCall, varInit.info, 3) @@ -236,10 +247,10 @@ proc f_wrapper(thread, args) = fv.owner = thread # optional nimArgsPassingDone() # signal parent that the work is done - # + # args.fv.blob = f(a, b, ...) nimFlowVarSignal(args.fv) - + # - or - f(a, b, ...) barrierLeave(args.barrier) # for parallel statement @@ -261,7 +272,7 @@ proc createWrapperProc(f: PNode; threadParam, argsParam: PSym; var threadLocalBarrier: PSym if barrier != nil: var varSection2 = newNodeI(nkVarSection, barrier.info) - threadLocalBarrier = addLocalVar(varSection2, nil, argsParam.owner, + threadLocalBarrier = addLocalVar(varSection2, nil, argsParam.owner, barrier.typ, barrier) body.add varSection2 body.add callCodegenProc("barrierEnter", threadLocalBarrier.newSymNode) @@ -285,7 +296,7 @@ proc createWrapperProc(f: PNode; threadParam, argsParam: PSym; elif fv != nil: let fk = fv.typ.sons[1].flowVarKind if fk == fvInvalid: - localError(f.info, "cannot create a flowVar of type: " & + localError(f.info, "cannot create a flowVar of type: " & typeToString(fv.typ.sons[1])) body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode, if fk == fvGC: "data" else: "blob", fv.info), call) @@ -330,8 +341,8 @@ proc createCastExpr(argsParam: PSym; objType: PType): PNode = result.typ = newType(tyPtr, objType.owner) result.typ.rawAddSon(objType) -proc setupArgsForConcurrency(n: PNode; objType: PType; scratchObj: PSym, - castExpr, call, +proc setupArgsForConcurrency(n: PNode; objType: PType; scratchObj: PSym, + castExpr, call, varSection, varInit, result: PNode) = let formals = n[0].typ.n let tmpName = getIdent(genPrefix) @@ -385,7 +396,7 @@ proc genHigh(n: PNode): PNode = result.sons[1] = n proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym; - castExpr, call, + castExpr, call, varSection, varInit, result: PNode) = let formals = n[0].typ.n let tmpName = getIdent(genPrefix) @@ -409,7 +420,7 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym; var fieldB = newSym(skField, tmpName, objType.owner, n.info) fieldB.typ = getSysType(tyInt) objType.addField(fieldB) - + if getMagic(n) == mSlice: let a = genAddrOf(n[1]) field.typ = a.typ @@ -464,7 +475,7 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym; useShallowCopy=true) call.add(threadLocal.newSymNode) -proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType; +proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType; barrier, dest: PNode = nil): PNode = # if 'barrier' != nil, then it is in a 'parallel' section and we # generate quite different code @@ -530,10 +541,10 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType; var varSection = newNodeI(nkVarSection, n.info) var varInit = newNodeI(nkStmtList, n.info) if barrier.isNil: - setupArgsForConcurrency(n, objType, scratchObj, castExpr, call, + setupArgsForConcurrency(n, objType, scratchObj, castExpr, call, varSection, varInit, result) else: - setupArgsForParallelism(n, objType, scratchObj, castExpr, call, + setupArgsForParallelism(n, objType, scratchObj, castExpr, call, varSection, varInit, result) var barrierAsExpr: PNode = nil @@ -566,7 +577,7 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType; fvAsExpr = indirectAccess(castExpr, field, n.info) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest)) - let wrapper = createWrapperProc(fn, threadParam, argsParam, + let wrapper = createWrapperProc(fn, threadParam, argsParam, varSection, varInit, call, barrierAsExpr, fvAsExpr, spawnKind) result.add callCodegenProc("nimSpawn", wrapper.newSymNode, diff --git a/compiler/main.nim b/compiler/main.nim index f7592a891..363327e40 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -58,7 +58,7 @@ proc commandCompileToC = registerPass(cgenPass) rodPass() #registerPass(cleanupPass()) - + compileProject() cgenWriteModules() if gCmd != cmdRun: @@ -363,7 +363,9 @@ proc mainCommand* = gVerbosity > 0): rawMessage(hintSuccessX, [$gLinesCompiled, formatFloat(epochTime() - gLastCmdTime, ffDecimal, 3), - formatSize(getTotalMem())]) + formatSize(getTotalMem()), + if condSyms.isDefined("release"): "Release Build" + else: "Debug Build"]) when PrintRopeCacheStats: echo "rope cache stats: " diff --git a/compiler/msgs.nim b/compiler/msgs.nim index e15cdc86d..0f7921ddb 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -10,95 +10,95 @@ import options, strutils, os, tables, ropes, platform -type - TMsgKind* = enum - errUnknown, errIllFormedAstX, errInternal, errCannotOpenFile, errGenerated, - errXCompilerDoesNotSupportCpp, errStringLiteralExpected, - errIntLiteralExpected, errInvalidCharacterConstant, - errClosingTripleQuoteExpected, errClosingQuoteExpected, - errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong, - errInvalidNumber, errNumberOutOfRange, errNnotAllowedInCharacter, - errClosingBracketExpected, errMissingFinalQuote, errIdentifierExpected, +type + TMsgKind* = enum + errUnknown, errIllFormedAstX, errInternal, errCannotOpenFile, errGenerated, + errXCompilerDoesNotSupportCpp, errStringLiteralExpected, + errIntLiteralExpected, errInvalidCharacterConstant, + errClosingTripleQuoteExpected, errClosingQuoteExpected, + errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong, + errInvalidNumber, errNumberOutOfRange, errNnotAllowedInCharacter, + errClosingBracketExpected, errMissingFinalQuote, errIdentifierExpected, errNewlineExpected, errInvalidModuleName, - errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected, - errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected, - errInvalidPragma, errUnknownPragma, errInvalidDirectiveX, - errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation, - errExceptionExpected, errExceptionAlreadyHandled, - errYieldNotAllowedHere, errYieldNotAllowedInTryStmt, - errInvalidNumberOfYieldExpr, errCannotReturnExpr, errAttemptToRedefine, - errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel, - errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected, - errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler, - errOnOrOffExpectedButXFound, errNoneBoehmRefcExpectedButXFound, - errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound, - errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound, - errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected, - errExprExpected, errUndeclaredIdentifier, errUseQualifier, errTypeExpected, - errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable, - errInvalidArgForX, errStmtHasNoEffect, errXExpectsTypeOrValue, - errXExpectsArrayType, errIteratorCannotBeInstantiated, errExprXAmbiguous, - errConstantDivisionByZero, errOrdinalTypeExpected, - errOrdinalOrFloatTypeExpected, errOverOrUnderflow, - errCannotEvalXBecauseIncompletelyDefined, errChrExpectsRange0_255, - errDynlibRequiresExportc, errUndeclaredFieldX, errNilAccess, - errIndexOutOfBounds, errIndexTypesDoNotMatch, errBracketsInvalidForType, - errValueOutOfSetBounds, errFieldInitTwice, errFieldNotInit, - errExprXCannotBeCalled, errExprHasNoType, errExprXHasNoType, - errCastNotInSafeMode, errExprCannotBeCastedToX, errCommaOrParRiExpected, - errCurlyLeOrParLeExpected, errSectionExpected, errRangeExpected, - errMagicOnlyInSystem, errPowerOfTwoExpected, - errStringMayNotBeEmpty, errCallConvExpected, errProcOnlyOneCallConv, - errSymbolMustBeImported, errExprMustBeBool, errConstExprExpected, - errDuplicateCaseLabel, errRangeIsEmpty, errSelectorMustBeOfCertainTypes, - errSelectorMustBeOrdinal, errOrdXMustNotBeNegative, errLenXinvalid, - errWrongNumberOfVariables, errExprCannotBeRaised, errBreakOnlyInLoop, - errTypeXhasUnknownSize, errConstNeedsConstExpr, errConstNeedsValue, - errResultCannotBeOpenArray, errSizeTooBig, errSetTooBig, - errBaseTypeMustBeOrdinal, errInheritanceOnlyWithNonFinalObjects, - errInheritanceOnlyWithEnums, errIllegalRecursionInTypeX, + errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected, + errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected, + errInvalidPragma, errUnknownPragma, errInvalidDirectiveX, + errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation, + errExceptionExpected, errExceptionAlreadyHandled, + errYieldNotAllowedHere, errYieldNotAllowedInTryStmt, + errInvalidNumberOfYieldExpr, errCannotReturnExpr, errAttemptToRedefine, + errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel, + errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected, + errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler, + errOnOrOffExpectedButXFound, errNoneBoehmRefcExpectedButXFound, + errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound, + errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound, + errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected, + errExprExpected, errUndeclaredIdentifier, errUseQualifier, errTypeExpected, + errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable, + errInvalidArgForX, errStmtHasNoEffect, errXExpectsTypeOrValue, + errXExpectsArrayType, errIteratorCannotBeInstantiated, errExprXAmbiguous, + errConstantDivisionByZero, errOrdinalTypeExpected, + errOrdinalOrFloatTypeExpected, errOverOrUnderflow, + errCannotEvalXBecauseIncompletelyDefined, errChrExpectsRange0_255, + errDynlibRequiresExportc, errUndeclaredFieldX, errNilAccess, + errIndexOutOfBounds, errIndexTypesDoNotMatch, errBracketsInvalidForType, + errValueOutOfSetBounds, errFieldInitTwice, errFieldNotInit, + errExprXCannotBeCalled, errExprHasNoType, errExprXHasNoType, + errCastNotInSafeMode, errExprCannotBeCastedToX, errCommaOrParRiExpected, + errCurlyLeOrParLeExpected, errSectionExpected, errRangeExpected, + errMagicOnlyInSystem, errPowerOfTwoExpected, + errStringMayNotBeEmpty, errCallConvExpected, errProcOnlyOneCallConv, + errSymbolMustBeImported, errExprMustBeBool, errConstExprExpected, + errDuplicateCaseLabel, errRangeIsEmpty, errSelectorMustBeOfCertainTypes, + errSelectorMustBeOrdinal, errOrdXMustNotBeNegative, errLenXinvalid, + errWrongNumberOfVariables, errExprCannotBeRaised, errBreakOnlyInLoop, + errTypeXhasUnknownSize, errConstNeedsConstExpr, errConstNeedsValue, + errResultCannotBeOpenArray, errSizeTooBig, errSetTooBig, + errBaseTypeMustBeOrdinal, errInheritanceOnlyWithNonFinalObjects, + errInheritanceOnlyWithEnums, errIllegalRecursionInTypeX, errCannotInstantiateX, errExprHasNoAddress, errXStackEscape, - errVarForOutParamNeeded, - errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX, - errAmbiguousCallXYZ, errWrongNumberOfArguments, - errXCannotBePassedToProcVar, - errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProc, errImplOfXNotAllowed, - errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX, - errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice, + errVarForOutParamNeeded, + errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX, + errAmbiguousCallXYZ, errWrongNumberOfArguments, + errXCannotBePassedToProcVar, + errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProc, errImplOfXNotAllowed, + errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX, + errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice, errInvalidOrderInArrayConstructor, - errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry, - errOptionExpected, errXisNoLabel, errNotAllCasesCovered, - errUnknownSubstitionVar, errComplexStmtRequiresInd, errXisNotCallable, - errNoPragmasAllowedForX, errNoGenericParamsAllowedForX, - errInvalidParamKindX, errDefaultArgumentInvalid, errNamedParamHasToBeIdent, - errNoReturnTypeForX, errConvNeedsOneArg, errInvalidPragmaX, + errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry, + errOptionExpected, errXisNoLabel, errNotAllCasesCovered, + errUnknownSubstitionVar, errComplexStmtRequiresInd, errXisNotCallable, + errNoPragmasAllowedForX, errNoGenericParamsAllowedForX, + errInvalidParamKindX, errDefaultArgumentInvalid, errNamedParamHasToBeIdent, + errNoReturnTypeForX, errConvNeedsOneArg, errInvalidPragmaX, errXNotAllowedHere, errInvalidControlFlowX, - errXisNoType, errCircumNeedsPointer, errInvalidExpression, - errInvalidExpressionX, errEnumHasNoValueX, errNamedExprExpected, - errNamedExprNotAllowed, errXExpectsOneTypeParam, - errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed, - errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType, + errXisNoType, errCircumNeedsPointer, errInvalidExpression, + errInvalidExpressionX, errEnumHasNoValueX, errNamedExprExpected, + errNamedExprNotAllowed, errXExpectsOneTypeParam, + errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed, + errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType, errNoReturnTypeDeclared, - errInvalidCommandX, errXOnlyAtModuleScope, + errInvalidCommandX, errXOnlyAtModuleScope, errXNeedsParamObjectType, - errTemplateInstantiationTooNested, errInstantiationFrom, + errTemplateInstantiationTooNested, errInstantiationFrom, errInvalidIndexValueForTuple, errCommandExpectsFilename, errMainModuleMustBeSpecified, errXExpected, errTIsNotAConcreteType, - errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError, - errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, + errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError, + errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly, errOnlyACallOpCanBeDelegator, errUsingNoSymbol, errMacroBodyDependsOnGenericTypes, errDestructorNotGenericEnough, errInlineIteratorsAsProcParams, errXExpectsTwoArguments, - errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations, - errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX, - errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument, - errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate, + errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations, + errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX, + errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument, + errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate, errXhasSideEffects, errIteratorExpected, errLetNeedsInit, errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX, errXCannotBeClosure, errXMustBeCompileTime, @@ -107,17 +107,18 @@ type errGenericLambdaNotAllowed, errCompilerDoesntSupportTarget, errUser, - warnCannotOpenFile, - warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, + warnCannotOpenFile, + warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, warnDeprecated, warnConfigDeprecated, - warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, + warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, warnUnknownSubstitutionX, warnLanguageXNotSupported, - warnFieldXNotSupported, warnCommentXIgnored, + warnFieldXNotSupported, warnCommentXIgnored, warnNilStatement, warnTypelessParam, warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode, - warnEachIdentIsTuple, warnShadowIdent, + warnEachIdentIsTuple, warnShadowIdent, warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, - warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnUser, + warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, + warnUser, hintSuccess, hintSuccessX, hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, @@ -125,205 +126,205 @@ type hintConditionAlwaysTrue, hintName, hintPattern, hintUser -const +const MsgKindToStr*: array[TMsgKind, string] = [ - errUnknown: "unknown error", + errUnknown: "unknown error", errIllFormedAstX: "illformed AST: $1", - errInternal: "internal error: $1", - errCannotOpenFile: "cannot open \'$1\'", - errGenerated: "$1", - errXCompilerDoesNotSupportCpp: "\'$1\' compiler does not support C++", - errStringLiteralExpected: "string literal expected", - errIntLiteralExpected: "integer literal expected", - errInvalidCharacterConstant: "invalid character constant", - errClosingTripleQuoteExpected: "closing \"\"\" expected, but end of file reached", - errClosingQuoteExpected: "closing \" expected", - errTabulatorsAreNotAllowed: "tabulators are not allowed", - errInvalidToken: "invalid token: $1", - errLineTooLong: "line too long", - errInvalidNumber: "$1 is not a valid number", - errNumberOutOfRange: "number $1 out of valid range", - errNnotAllowedInCharacter: "\\n not allowed in character literal", - errClosingBracketExpected: "closing ']' expected, but end of file reached", - errMissingFinalQuote: "missing final \' for character literal", - errIdentifierExpected: "identifier expected, but found \'$1\'", + errInternal: "internal error: $1", + errCannotOpenFile: "cannot open \'$1\'", + errGenerated: "$1", + errXCompilerDoesNotSupportCpp: "\'$1\' compiler does not support C++", + errStringLiteralExpected: "string literal expected", + errIntLiteralExpected: "integer literal expected", + errInvalidCharacterConstant: "invalid character constant", + errClosingTripleQuoteExpected: "closing \"\"\" expected, but end of file reached", + errClosingQuoteExpected: "closing \" expected", + errTabulatorsAreNotAllowed: "tabulators are not allowed", + errInvalidToken: "invalid token: $1", + errLineTooLong: "line too long", + errInvalidNumber: "$1 is not a valid number", + errNumberOutOfRange: "number $1 out of valid range", + errNnotAllowedInCharacter: "\\n not allowed in character literal", + errClosingBracketExpected: "closing ']' expected, but end of file reached", + errMissingFinalQuote: "missing final \' for character literal", + errIdentifierExpected: "identifier expected, but found \'$1\'", errNewlineExpected: "newline expected, but found \'$1\'", errInvalidModuleName: "invalid module name: '$1'", - errOperatorExpected: "operator expected, but found \'$1\'", - errTokenExpected: "\'$1\' expected", - errStringAfterIncludeExpected: "string after \'include\' expected", - errRecursiveDependencyX: "recursive dependency: \'$1\'", - errOnOrOffExpected: "\'on\' or \'off\' expected", - errNoneSpeedOrSizeExpected: "\'none\', \'speed\' or \'size\' expected", - errInvalidPragma: "invalid pragma", - errUnknownPragma: "unknown pragma: \'$1\'", - errInvalidDirectiveX: "invalid directive: \'$1\'", - errAtPopWithoutPush: "\'pop\' without a \'push\' pragma", - errEmptyAsm: "empty asm statement", - errInvalidIndentation: "invalid indentation", - errExceptionExpected: "exception expected", - errExceptionAlreadyHandled: "exception already handled", + errOperatorExpected: "operator expected, but found \'$1\'", + errTokenExpected: "\'$1\' expected", + errStringAfterIncludeExpected: "string after \'include\' expected", + errRecursiveDependencyX: "recursive dependency: \'$1\'", + errOnOrOffExpected: "\'on\' or \'off\' expected", + errNoneSpeedOrSizeExpected: "\'none\', \'speed\' or \'size\' expected", + errInvalidPragma: "invalid pragma", + errUnknownPragma: "unknown pragma: \'$1\'", + errInvalidDirectiveX: "invalid directive: \'$1\'", + errAtPopWithoutPush: "\'pop\' without a \'push\' pragma", + errEmptyAsm: "empty asm statement", + errInvalidIndentation: "invalid indentation", + errExceptionExpected: "exception expected", + errExceptionAlreadyHandled: "exception already handled", errYieldNotAllowedHere: "'yield' only allowed in an iterator", errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator", - errInvalidNumberOfYieldExpr: "invalid number of \'yield\' expressions", - errCannotReturnExpr: "current routine cannot return an expression", - errAttemptToRedefine: "redefinition of \'$1\'", - errStmtInvalidAfterReturn: "statement not allowed after \'return\', \'break\', \'raise\' or \'continue'", - errStmtExpected: "statement expected", - errInvalidLabel: "\'$1\' is no label", - errInvalidCmdLineOption: "invalid command line option: \'$1\'", - errCmdLineArgExpected: "argument for command line option expected: \'$1\'", - errCmdLineNoArgExpected: "invalid argument for command line option: \'$1\'", - errInvalidVarSubstitution: "invalid variable substitution in \'$1\'", - errUnknownVar: "unknown variable: \'$1\'", - errUnknownCcompiler: "unknown C compiler: \'$1\'", - errOnOrOffExpectedButXFound: "\'on\' or \'off\' expected, but \'$1\' found", - errNoneBoehmRefcExpectedButXFound: "'none', 'boehm' or 'refc' expected, but '$1' found", - errNoneSpeedOrSizeExpectedButXFound: "'none', 'speed' or 'size' expected, but '$1' found", - errGuiConsoleOrLibExpectedButXFound: "'gui', 'console' or 'lib' expected, but '$1' found", - errUnknownOS: "unknown OS: '$1'", - errUnknownCPU: "unknown CPU: '$1'", - errGenOutExpectedButXFound: "'c', 'c++' or 'yaml' expected, but '$1' found", - errArgsNeedRunOption: "arguments can only be given if the '--run' option is selected", - errInvalidMultipleAsgn: "multiple assignment is not allowed", - errColonOrEqualsExpected: "\':\' or \'=\' expected, but found \'$1\'", - errExprExpected: "expression expected, but found \'$1\'", - errUndeclaredIdentifier: "undeclared identifier: \'$1\'", - errUseQualifier: "ambiguous identifier: \'$1\' -- use a qualifier", - errTypeExpected: "type expected", - errSystemNeeds: "system module needs \'$1\'", - errExecutionOfProgramFailed: "execution of an external program failed", - errNotOverloadable: "overloaded \'$1\' leads to ambiguous calls", - errInvalidArgForX: "invalid argument for \'$1\'", - errStmtHasNoEffect: "statement has no effect", - errXExpectsTypeOrValue: "\'$1\' expects a type or value", - errXExpectsArrayType: "\'$1\' expects an array type", - errIteratorCannotBeInstantiated: "'$1' cannot be instantiated because its body has not been compiled yet", - errExprXAmbiguous: "expression '$1' ambiguous in this context", - errConstantDivisionByZero: "division by zero", - errOrdinalTypeExpected: "ordinal type expected", - errOrdinalOrFloatTypeExpected: "ordinal or float type expected", - errOverOrUnderflow: "over- or underflow", - errCannotEvalXBecauseIncompletelyDefined: "cannot evalutate '$1' because type is not defined completely", - errChrExpectsRange0_255: "\'chr\' expects an int in the range 0..255", - errDynlibRequiresExportc: "\'dynlib\' requires \'exportc\'", - errUndeclaredFieldX: "undeclared field: \'$1\'", + errInvalidNumberOfYieldExpr: "invalid number of \'yield\' expressions", + errCannotReturnExpr: "current routine cannot return an expression", + errAttemptToRedefine: "redefinition of \'$1\'", + errStmtInvalidAfterReturn: "statement not allowed after \'return\', \'break\', \'raise\' or \'continue'", + errStmtExpected: "statement expected", + errInvalidLabel: "\'$1\' is no label", + errInvalidCmdLineOption: "invalid command line option: \'$1\'", + errCmdLineArgExpected: "argument for command line option expected: \'$1\'", + errCmdLineNoArgExpected: "invalid argument for command line option: \'$1\'", + errInvalidVarSubstitution: "invalid variable substitution in \'$1\'", + errUnknownVar: "unknown variable: \'$1\'", + errUnknownCcompiler: "unknown C compiler: \'$1\'", + errOnOrOffExpectedButXFound: "\'on\' or \'off\' expected, but \'$1\' found", + errNoneBoehmRefcExpectedButXFound: "'none', 'boehm' or 'refc' expected, but '$1' found", + errNoneSpeedOrSizeExpectedButXFound: "'none', 'speed' or 'size' expected, but '$1' found", + errGuiConsoleOrLibExpectedButXFound: "'gui', 'console' or 'lib' expected, but '$1' found", + errUnknownOS: "unknown OS: '$1'", + errUnknownCPU: "unknown CPU: '$1'", + errGenOutExpectedButXFound: "'c', 'c++' or 'yaml' expected, but '$1' found", + errArgsNeedRunOption: "arguments can only be given if the '--run' option is selected", + errInvalidMultipleAsgn: "multiple assignment is not allowed", + errColonOrEqualsExpected: "\':\' or \'=\' expected, but found \'$1\'", + errExprExpected: "expression expected, but found \'$1\'", + errUndeclaredIdentifier: "undeclared identifier: \'$1\'", + errUseQualifier: "ambiguous identifier: \'$1\' -- use a qualifier", + errTypeExpected: "type expected", + errSystemNeeds: "system module needs \'$1\'", + errExecutionOfProgramFailed: "execution of an external program failed", + errNotOverloadable: "overloaded \'$1\' leads to ambiguous calls", + errInvalidArgForX: "invalid argument for \'$1\'", + errStmtHasNoEffect: "statement has no effect", + errXExpectsTypeOrValue: "\'$1\' expects a type or value", + errXExpectsArrayType: "\'$1\' expects an array type", + errIteratorCannotBeInstantiated: "'$1' cannot be instantiated because its body has not been compiled yet", + errExprXAmbiguous: "expression '$1' ambiguous in this context", + errConstantDivisionByZero: "division by zero", + errOrdinalTypeExpected: "ordinal type expected", + errOrdinalOrFloatTypeExpected: "ordinal or float type expected", + errOverOrUnderflow: "over- or underflow", + errCannotEvalXBecauseIncompletelyDefined: "cannot evalutate '$1' because type is not defined completely", + errChrExpectsRange0_255: "\'chr\' expects an int in the range 0..255", + errDynlibRequiresExportc: "\'dynlib\' requires \'exportc\'", + errUndeclaredFieldX: "undeclared field: \'$1\'", errNilAccess: "attempt to access a nil address", - errIndexOutOfBounds: "index out of bounds", - errIndexTypesDoNotMatch: "index types do not match", - errBracketsInvalidForType: "\'[]\' operator invalid for this type", - errValueOutOfSetBounds: "value out of set bounds", - errFieldInitTwice: "field initialized twice: \'$1\'", - errFieldNotInit: "field \'$1\' not initialized", - errExprXCannotBeCalled: "expression \'$1\' cannot be called", - errExprHasNoType: "expression has no type", - errExprXHasNoType: "expression \'$1\' has no type (or is ambiguous)", + errIndexOutOfBounds: "index out of bounds", + errIndexTypesDoNotMatch: "index types do not match", + errBracketsInvalidForType: "\'[]\' operator invalid for this type", + errValueOutOfSetBounds: "value out of set bounds", + errFieldInitTwice: "field initialized twice: \'$1\'", + errFieldNotInit: "field \'$1\' not initialized", + errExprXCannotBeCalled: "expression \'$1\' cannot be called", + errExprHasNoType: "expression has no type", + errExprXHasNoType: "expression \'$1\' has no type (or is ambiguous)", errCastNotInSafeMode: "\'cast\' not allowed in safe mode", - errExprCannotBeCastedToX: "expression cannot be casted to $1", - errCommaOrParRiExpected: "',' or ')' expected", - errCurlyLeOrParLeExpected: "\'{\' or \'(\' expected", - errSectionExpected: "section (\'type\', \'proc\', etc.) expected", - errRangeExpected: "range expected", - errMagicOnlyInSystem: "\'magic\' only allowed in system module", + errExprCannotBeCastedToX: "expression cannot be casted to $1", + errCommaOrParRiExpected: "',' or ')' expected", + errCurlyLeOrParLeExpected: "\'{\' or \'(\' expected", + errSectionExpected: "section (\'type\', \'proc\', etc.) expected", + errRangeExpected: "range expected", + errMagicOnlyInSystem: "\'magic\' only allowed in system module", errPowerOfTwoExpected: "power of two expected", - errStringMayNotBeEmpty: "string literal may not be empty", - errCallConvExpected: "calling convention expected", - errProcOnlyOneCallConv: "a proc can only have one calling convention", - errSymbolMustBeImported: "symbol must be imported if 'lib' pragma is used", - errExprMustBeBool: "expression must be of type 'bool'", - errConstExprExpected: "constant expression expected", - errDuplicateCaseLabel: "duplicate case label", - errRangeIsEmpty: "range is empty", - errSelectorMustBeOfCertainTypes: "selector must be of an ordinal type, float or string", - errSelectorMustBeOrdinal: "selector must be of an ordinal type", - errOrdXMustNotBeNegative: "ord($1) must not be negative", + errStringMayNotBeEmpty: "string literal may not be empty", + errCallConvExpected: "calling convention expected", + errProcOnlyOneCallConv: "a proc can only have one calling convention", + errSymbolMustBeImported: "symbol must be imported if 'lib' pragma is used", + errExprMustBeBool: "expression must be of type 'bool'", + errConstExprExpected: "constant expression expected", + errDuplicateCaseLabel: "duplicate case label", + errRangeIsEmpty: "range is empty", + errSelectorMustBeOfCertainTypes: "selector must be of an ordinal type, float or string", + errSelectorMustBeOrdinal: "selector must be of an ordinal type", + errOrdXMustNotBeNegative: "ord($1) must not be negative", errLenXinvalid: "len($1) must be less than 32768", - errWrongNumberOfVariables: "wrong number of variables", - errExprCannotBeRaised: "only a 'ref object' can be raised", - errBreakOnlyInLoop: "'break' only allowed in loop construct", - errTypeXhasUnknownSize: "type \'$1\' has unknown size", - errConstNeedsConstExpr: "a constant can only be initialized with a constant expression", - errConstNeedsValue: "a constant needs a value", - errResultCannotBeOpenArray: "the result type cannot be on open array", - errSizeTooBig: "computing the type\'s size produced an overflow", - errSetTooBig: "set is too large", - errBaseTypeMustBeOrdinal: "base type of a set must be an ordinal", - errInheritanceOnlyWithNonFinalObjects: "inheritance only works with non-final objects", + errWrongNumberOfVariables: "wrong number of variables", + errExprCannotBeRaised: "only a 'ref object' can be raised", + errBreakOnlyInLoop: "'break' only allowed in loop construct", + errTypeXhasUnknownSize: "type \'$1\' has unknown size", + errConstNeedsConstExpr: "a constant can only be initialized with a constant expression", + errConstNeedsValue: "a constant needs a value", + errResultCannotBeOpenArray: "the result type cannot be on open array", + errSizeTooBig: "computing the type\'s size produced an overflow", + errSetTooBig: "set is too large", + errBaseTypeMustBeOrdinal: "base type of a set must be an ordinal", + errInheritanceOnlyWithNonFinalObjects: "inheritance only works with non-final objects", errInheritanceOnlyWithEnums: "inheritance only works with an enum", - errIllegalRecursionInTypeX: "illegal recursion in type \'$1\'", - errCannotInstantiateX: "cannot instantiate: \'$1\'", - errExprHasNoAddress: "expression has no address", + errIllegalRecursionInTypeX: "illegal recursion in type \'$1\'", + errCannotInstantiateX: "cannot instantiate: \'$1\'", + errExprHasNoAddress: "expression has no address", errXStackEscape: "address of '$1' may not escape its stack frame", errVarForOutParamNeeded: "for a \'var\' type a variable needs to be passed", - errPureTypeMismatch: "type mismatch", - errTypeMismatch: "type mismatch: got (", + errPureTypeMismatch: "type mismatch", + errTypeMismatch: "type mismatch: got (", errButExpected: "but expected one of: ", - errButExpectedX: "but expected \'$1\'", - errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3", - errWrongNumberOfArguments: "wrong number of arguments", - errXCannotBePassedToProcVar: "\'$1\' cannot be passed to a procvar", - errXCannotBeInParamDecl: "$1 cannot be declared in parameter declaration", - errPragmaOnlyInHeaderOfProc: "pragmas are only allowed in the header of a proc", - errImplOfXNotAllowed: "implementation of \'$1\' is not allowed", - errImplOfXexpected: "implementation of \'$1\' expected", - errNoSymbolToBorrowFromFound: "no symbol to borrow from found", - errDiscardValueX: "value of type '$1' has to be discarded", - errInvalidDiscard: "statement returns no value that can be discarded", + errButExpectedX: "but expected \'$1\'", + errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3", + errWrongNumberOfArguments: "wrong number of arguments", + errXCannotBePassedToProcVar: "\'$1\' cannot be passed to a procvar", + errXCannotBeInParamDecl: "$1 cannot be declared in parameter declaration", + errPragmaOnlyInHeaderOfProc: "pragmas are only allowed in the header of a proc", + errImplOfXNotAllowed: "implementation of \'$1\' is not allowed", + errImplOfXexpected: "implementation of \'$1\' expected", + errNoSymbolToBorrowFromFound: "no symbol to borrow from found", + errDiscardValueX: "value of type '$1' has to be discarded", + errInvalidDiscard: "statement returns no value that can be discarded", errIllegalConvFromXtoY: "conversion from $1 to $2 is invalid", - errCannotBindXTwice: "cannot bind parameter \'$1\' twice", + errCannotBindXTwice: "cannot bind parameter \'$1\' twice", errInvalidOrderInArrayConstructor: "invalid order in array constructor", - errInvalidOrderInEnumX: "invalid order in enum \'$1\'", - errEnumXHasHoles: "enum \'$1\' has holes", - errExceptExpected: "\'except\' or \'finally\' expected", - errInvalidTry: "after catch all \'except\' or \'finally\' no section may follow", + errInvalidOrderInEnumX: "invalid order in enum \'$1\'", + errEnumXHasHoles: "enum \'$1\' has holes", + errExceptExpected: "\'except\' or \'finally\' expected", + errInvalidTry: "after catch all \'except\' or \'finally\' no section may follow", errOptionExpected: "option expected, but found \'$1\'", - errXisNoLabel: "\'$1\' is not a label", - errNotAllCasesCovered: "not all cases are covered", - errUnknownSubstitionVar: "unknown substitution variable: \'$1\'", + errXisNoLabel: "\'$1\' is not a label", + errNotAllCasesCovered: "not all cases are covered", + errUnknownSubstitionVar: "unknown substitution variable: \'$1\'", errComplexStmtRequiresInd: "complex statement requires indentation", - errXisNotCallable: "\'$1\' is not callable", - errNoPragmasAllowedForX: "no pragmas allowed for $1", - errNoGenericParamsAllowedForX: "no generic parameters allowed for $1", - errInvalidParamKindX: "invalid param kind: \'$1\'", - errDefaultArgumentInvalid: "default argument invalid", - errNamedParamHasToBeIdent: "named parameter has to be an identifier", - errNoReturnTypeForX: "no return type allowed for $1", - errConvNeedsOneArg: "a type conversion needs exactly one argument", - errInvalidPragmaX: "invalid pragma: $1", + errXisNotCallable: "\'$1\' is not callable", + errNoPragmasAllowedForX: "no pragmas allowed for $1", + errNoGenericParamsAllowedForX: "no generic parameters allowed for $1", + errInvalidParamKindX: "invalid param kind: \'$1\'", + errDefaultArgumentInvalid: "default argument invalid", + errNamedParamHasToBeIdent: "named parameter has to be an identifier", + errNoReturnTypeForX: "no return type allowed for $1", + errConvNeedsOneArg: "a type conversion needs exactly one argument", + errInvalidPragmaX: "invalid pragma: $1", errXNotAllowedHere: "$1 not allowed here", errInvalidControlFlowX: "invalid control flow: $1", errXisNoType: "invalid type: \'$1\'", - errCircumNeedsPointer: "'[]' needs a pointer or reference type", + errCircumNeedsPointer: "'[]' needs a pointer or reference type", errInvalidExpression: "invalid expression", - errInvalidExpressionX: "invalid expression: \'$1\'", + errInvalidExpressionX: "invalid expression: \'$1\'", errEnumHasNoValueX: "enum has no value \'$1\'", - errNamedExprExpected: "named expression expected", - errNamedExprNotAllowed: "named expression not allowed here", - errXExpectsOneTypeParam: "\'$1\' expects one type parameter", - errArrayExpectsTwoTypeParams: "array expects two type parameters", - errInvalidVisibilityX: "invalid visibility: \'$1\'", - errInitHereNotAllowed: "initialization not allowed here", - errXCannotBeAssignedTo: "\'$1\' cannot be assigned to", - errIteratorNotAllowed: "iterators can only be defined at the module\'s top level", + errNamedExprExpected: "named expression expected", + errNamedExprNotAllowed: "named expression not allowed here", + errXExpectsOneTypeParam: "\'$1\' expects one type parameter", + errArrayExpectsTwoTypeParams: "array expects two type parameters", + errInvalidVisibilityX: "invalid visibility: \'$1\'", + errInitHereNotAllowed: "initialization not allowed here", + errXCannotBeAssignedTo: "\'$1\' cannot be assigned to", + errIteratorNotAllowed: "iterators can only be defined at the module\'s top level", errXNeedsReturnType: "$1 needs a return type", errNoReturnTypeDeclared: "no return type declared", - errInvalidCommandX: "invalid command: \'$1\'", - errXOnlyAtModuleScope: "\'$1\' is only allowed at top level", + errInvalidCommandX: "invalid command: \'$1\'", + errXOnlyAtModuleScope: "\'$1\' is only allowed at top level", errXNeedsParamObjectType: "'$1' needs a parameter that has an object type", errTemplateInstantiationTooNested: "template/macro instantiation too nested", - errInstantiationFrom: "template/generic instantiation from here", - errInvalidIndexValueForTuple: "invalid index value for tuple subscript", + errInstantiationFrom: "template/generic instantiation from here", + errInvalidIndexValueForTuple: "invalid index value for tuple subscript", errCommandExpectsFilename: "command expects a filename argument", errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file", - errXExpected: "\'$1\' expected", + errXExpected: "\'$1\' expected", errTIsNotAConcreteType: "\'$1\' is not a concrete type.", errInvalidSectionStart: "invalid section start", - errGridTableNotImplemented: "grid table is not implemented", + errGridTableNotImplemented: "grid table is not implemented", errGeneralParseError: "general parse error", - errNewSectionExpected: "new section expected", + errNewSectionExpected: "new section expected", errWhitespaceExpected: "whitespace expected, got \'$1\'", - errXisNoValidIndexFile: "\'$1\' is no valid index file", - errCannotRenderX: "cannot render reStructuredText element \'$1\'", + errXisNoValidIndexFile: "\'$1\' is no valid index file", + errCannotRenderX: "cannot render reStructuredText element \'$1\'", errVarVarTypeNotAllowed: "type \'var var\' is not allowed", errInstantiateXExplicitly: "instantiate '$1' explicitly", errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator", @@ -334,24 +335,24 @@ const "A destructor must be associated will all instantiations of a generic type", errInlineIteratorsAsProcParams: "inline iterators can be used as parameters only for " & "templates, macros and other inline iterators", - errXExpectsTwoArguments: "\'$1\' expects two arguments", + errXExpectsTwoArguments: "\'$1\' expects two arguments", errXExpectsObjectTypes: "\'$1\' expects object types", - errXcanNeverBeOfThisSubtype: "\'$1\' can never be of this subtype", - errTooManyIterations: "interpretation requires too many iterations", - errCannotInterpretNodeX: "cannot evaluate \'$1\'", - errFieldXNotFound: "field \'$1\' cannot be found", + errXcanNeverBeOfThisSubtype: "\'$1\' can never be of this subtype", + errTooManyIterations: "interpretation requires too many iterations", + errCannotInterpretNodeX: "cannot evaluate \'$1\'", + errFieldXNotFound: "field \'$1\' cannot be found", errInvalidConversionFromTypeX: "invalid conversion from type \'$1\'", - errAssertionFailed: "assertion failed", - errCannotGenerateCodeForX: "cannot generate code for \'$1\'", - errXRequiresOneArgument: "$1 requires one parameter", - errUnhandledExceptionX: "unhandled exception: $1", - errCyclicTree: "macro returned a cyclic abstract syntax tree", + errAssertionFailed: "assertion failed", + errCannotGenerateCodeForX: "cannot generate code for \'$1\'", + errXRequiresOneArgument: "$1 requires one parameter", + errUnhandledExceptionX: "unhandled exception: $1", + errCyclicTree: "macro returned a cyclic abstract syntax tree", errXisNoMacroOrTemplate: "\'$1\' is no macro or template", - errXhasSideEffects: "\'$1\' can have side effects", + errXhasSideEffects: "\'$1\' can have side effects", errIteratorExpected: "iterator within for loop context expected", errLetNeedsInit: "'let' symbol requires an initialization", errThreadvarCannotInit: "a thread var cannot be initialized explicitly", - errWrongSymbolX: "usage of \'$1\' is a user-defined error", + errWrongSymbolX: "usage of \'$1\' is a user-defined error", errIllegalCaptureX: "illegal capture '$1'", errXCannotBeClosure: "'$1' cannot have 'closure' calling convention", errXMustBeCompileTime: "'$1' can only be used in compile-time context", @@ -361,21 +362,21 @@ const "it is used as an operand to another routine and the types " & "of the generic paramers can be inferred from the expected signature.", errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target", - errUser: "$1", + errUser: "$1", warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]", - warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]", - warnXIsNeverRead: "\'$1\' is never read [XIsNeverRead]", - warnXmightNotBeenInit: "\'$1\' might not have been initialized [XmightNotBeenInit]", - warnDeprecated: "$1 is deprecated [Deprecated]", + warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]", + warnXIsNeverRead: "\'$1\' is never read [XIsNeverRead]", + warnXmightNotBeenInit: "\'$1\' might not have been initialized [XmightNotBeenInit]", + warnDeprecated: "$1 is deprecated [Deprecated]", warnConfigDeprecated: "config file '$1' is deprecated [ConfigDeprecated]", - warnSmallLshouldNotBeUsed: "\'l\' should not be used as an identifier; may look like \'1\' (one) [SmallLshouldNotBeUsed]", - warnUnknownMagic: "unknown magic \'$1\' might crash the compiler [UnknownMagic]", - warnRedefinitionOfLabel: "redefinition of label \'$1\' [RedefinitionOfLabel]", - warnUnknownSubstitutionX: "unknown substitution \'$1\' [UnknownSubstitutionX]", - warnLanguageXNotSupported: "language \'$1\' not supported [LanguageXNotSupported]", - warnFieldXNotSupported: "field \'$1\' not supported [FieldXNotSupported]", - warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]", - warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead [NilStmt]", + warnSmallLshouldNotBeUsed: "\'l\' should not be used as an identifier; may look like \'1\' (one) [SmallLshouldNotBeUsed]", + warnUnknownMagic: "unknown magic \'$1\' might crash the compiler [UnknownMagic]", + warnRedefinitionOfLabel: "redefinition of label \'$1\' [RedefinitionOfLabel]", + warnUnknownSubstitutionX: "unknown substitution \'$1\' [UnknownSubstitutionX]", + warnLanguageXNotSupported: "language \'$1\' not supported [LanguageXNotSupported]", + warnFieldXNotSupported: "field \'$1\' not supported [FieldXNotSupported]", + warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]", + warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead [NilStmt]", warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template' [TypelessParam]", warnDifferentHeaps: "possible inconsistency of thread local heaps [DifferentHeaps]", warnWriteToForeignHeap: "write to foreign heap [WriteToForeignHeap]", @@ -391,19 +392,20 @@ const warnGcMem: "'$1' uses GC'ed memory [GcMem]", warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future. [Destructor]", warnLockLevel: "$1 [LockLevel]", - warnUser: "$1 [User]", - hintSuccess: "operation successful [Success]", - hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#) [SuccessX]", - hintLineTooLong: "line too long [LineTooLong]", - hintXDeclaredButNotUsed: "\'$1\' is declared but not used [XDeclaredButNotUsed]", - hintConvToBaseNotNeeded: "conversion to base object is not needed [ConvToBaseNotNeeded]", - hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless [ConvFromXtoItselfNotNeeded]", - hintExprAlwaysX: "expression evaluates always to \'$1\' [ExprAlwaysX]", + warnResultShadowed: "Special variable 'result' is shadowed. [ResultShadowed]", + warnUser: "$1 [User]", + hintSuccess: "operation successful [Success]", + hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#) [SuccessX]", + hintLineTooLong: "line too long [LineTooLong]", + hintXDeclaredButNotUsed: "\'$1\' is declared but not used [XDeclaredButNotUsed]", + hintConvToBaseNotNeeded: "conversion to base object is not needed [ConvToBaseNotNeeded]", + hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless [ConvFromXtoItselfNotNeeded]", + hintExprAlwaysX: "expression evaluates always to \'$1\' [ExprAlwaysX]", hintQuitCalled: "quit() called [QuitCalled]", - hintProcessing: "$1 [Processing]", + hintProcessing: "$1 [Processing]", hintCodeBegin: "generated code listing: [CodeBegin]", - hintCodeEnd: "end of listing [CodeEnd]", - hintConf: "used config file \'$1\' [Conf]", + hintCodeEnd: "end of listing [CodeEnd]", + hintConf: "used config file \'$1\' [Conf]", hintPath: "added path: '$1' [Path]", hintConditionAlwaysTrue: "condition is always true: '$1' [CondTrue]", hintName: "name should be: '$1' [Name]", @@ -411,25 +413,25 @@ const hintUser: "$1 [User]"] const - WarningsToStr*: array[0..29, string] = ["CannotOpenFile", "OctalEscape", + WarningsToStr*: array[0..30, string] = ["CannotOpenFile", "OctalEscape", "XIsNeverRead", "XmightNotBeenInit", "Deprecated", "ConfigDeprecated", - "SmallLshouldNotBeUsed", "UnknownMagic", + "SmallLshouldNotBeUsed", "UnknownMagic", "RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported", "FieldXNotSupported", "CommentXIgnored", "NilStmt", "TypelessParam", "DifferentHeaps", "WriteToForeignHeap", - "UnsafeCode", "EachIdentIsTuple", "ShadowIdent", + "UnsafeCode", "EachIdentIsTuple", "ShadowIdent", "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", - "GcMem", "Destructor", "LockLevel", "User"] + "GcMem", "Destructor", "LockLevel", "ResultShadowed", "User"] - HintsToStr*: array[0..16, string] = ["Success", "SuccessX", "LineTooLong", - "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", - "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", + HintsToStr*: array[0..16, string] = ["Success", "SuccessX", "LineTooLong", + "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", + "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", "Path", "CondTrue", "Name", "Pattern", "User"] -const +const fatalMin* = errUnknown fatalMax* = errInternal errMin* = errUnknown @@ -438,18 +440,18 @@ const warnMax* = pred(hintSuccess) hintMin* = hintSuccess hintMax* = high(TMsgKind) - -type + +type TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints TNoteKinds* = set[TNoteKind] - TFileInfo* = object + TFileInfo* = object fullPath: string # This is a canonical full filesystem path projPath*: string # This is relative to the project's root shortName*: string # short name of the module quotedName*: PRope # cached quoted short name for codegen # purposes - + lines*: seq[PRope] # the source code of the module # used for better error messages and # embedding the original source in the @@ -460,13 +462,13 @@ type TLineInfo* = object # This is designed to be as small as possible, # because it is used - # in syntax nodes. We save space here by using + # in syntax nodes. We save space here by using # two int16 and an int32. - # On 64 bit and on 32 bit systems this is + # On 64 bit and on 32 bit systems this is # only 8 bytes. line*, col*: int16 fileIndex*: int32 - + TErrorOutput* = enum eStdOut eStdErr @@ -485,7 +487,7 @@ var fileInfos*: seq[TFileInfo] = @[] systemFileIdx*: int32 -proc toCChar*(c: char): string = +proc toCChar*(c: char): string = case c of '\0'..'\x1F', '\x80'..'\xFF': result = '\\' & toOctal(c) of '\'', '\"', '\\': result = '\\' & c @@ -495,7 +497,7 @@ proc makeCString*(s: string): PRope = # BUGFIX: We have to split long strings into many ropes. Otherwise # this could trigger an internalError(). See the ropes module for # further information. - const + const MaxLineLength = 64 result = nil var res = "\"" @@ -569,7 +571,7 @@ proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} = proc sourceLine*(i: TLineInfo): PRope var - gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} - + gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit, warnProveField, warnProveIndex, warnGcUnsafe} gErrorCounter*: int = 0 # counts the number of errors @@ -582,7 +584,7 @@ proc unknownLineInfo*(): TLineInfo = result.col = int16(-1) result.fileIndex = -1 -var +var msgContext: seq[TLineInfo] = @[] lastError = unknownLineInfo() bufferedMsgs*: seq[string] @@ -597,7 +599,7 @@ proc suggestWriteln*(s: string) = if eStdOut in errorOutputs: if isNil(writelnHook): writeln(stdout, s) else: writelnHook(s) - + if eInMemory in errorOutputs: bufferedMsgs.safeAdd(s) @@ -621,10 +623,10 @@ const proc getInfoContextLen*(): int = return msgContext.len proc setInfoContextLen*(L: int) = setLen(msgContext, L) -proc pushInfoContext*(info: TLineInfo) = +proc pushInfoContext*(info: TLineInfo) = msgContext.add(info) - -proc popInfoContext*() = + +proc popInfoContext*() = setLen(msgContext, len(msgContext) - 1) proc getInfoContext*(index: int): TLineInfo = @@ -667,10 +669,10 @@ proc toMsgFilename*(info: TLineInfo): string = else: result = fileInfos[info.fileIndex].projPath -proc toLinenumber*(info: TLineInfo): int {.inline.} = +proc toLinenumber*(info: TLineInfo): int {.inline.} = result = info.line -proc toColumn*(info: TLineInfo): int {.inline.} = +proc toColumn*(info: TLineInfo): int {.inline.} = result = info.col proc toFileLine*(info: TLineInfo): string {.inline.} = @@ -687,11 +689,11 @@ proc `??`* (info: TLineInfo, filename: string): bool = var gTrackPos*: TLineInfo -proc outWriteln*(s: string) = +proc outWriteln*(s: string) = ## Writes to stdout. Always. if eStdOut in errorOutputs: writeln(stdout, s) - -proc msgWriteln*(s: string) = + +proc msgWriteln*(s: string) = ## Writes to stdout. If --stdout option is given, writes to stderr instead. #if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return @@ -702,18 +704,18 @@ proc msgWriteln*(s: string) = if eStdErr in errorOutputs: writeln(stderr, s) else: if eStdOut in errorOutputs: writeln(stdout, s) - + if eInMemory in errorOutputs: bufferedMsgs.safeAdd(s) -proc coordToStr(coord: int): string = +proc coordToStr(coord: int): string = if coord == -1: result = "???" else: result = $coord - -proc msgKindToString*(kind: TMsgKind): string = + +proc msgKindToString*(kind: TMsgKind): string = # later versions may provide translated error messages result = MsgKindToStr[kind] -proc getMessageStr(msg: TMsgKind, arg: string): string = +proc getMessageStr(msg: TMsgKind, arg: string): string = result = msgKindToString(msg) % [arg] type @@ -743,34 +745,34 @@ proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) = proc `==`*(a, b: TLineInfo): bool = result = a.line == b.line and a.fileIndex == b.fileIndex -proc writeContext(lastinfo: TLineInfo) = +proc writeContext(lastinfo: TLineInfo) = var info = lastinfo - for i in countup(0, len(msgContext) - 1): - if msgContext[i] != lastinfo and msgContext[i] != info: - msgWriteln(PosContextFormat % [toMsgFilename(msgContext[i]), - coordToStr(msgContext[i].line), - coordToStr(msgContext[i].col), + for i in countup(0, len(msgContext) - 1): + if msgContext[i] != lastinfo and msgContext[i] != info: + msgWriteln(PosContextFormat % [toMsgFilename(msgContext[i]), + coordToStr(msgContext[i].line), + coordToStr(msgContext[i].col), getMessageStr(errInstantiationFrom, "")]) info = msgContext[i] proc ignoreMsgBecauseOfIdeTools(msg: TMsgKind): bool = msg >= errGenerated and gCmd == cmdIdeTools and optIdeDebug notin gGlobalOptions -proc rawMessage*(msg: TMsgKind, args: openArray[string]) = +proc rawMessage*(msg: TMsgKind, args: openArray[string]) = var frmt: string case msg - of errMin..errMax: + of errMin..errMax: writeContext(unknownLineInfo()) frmt = RawErrorFormat - of warnMin..warnMax: - if optWarns notin gOptions: return - if msg notin gNotes: return + of warnMin..warnMax: + if optWarns notin gOptions: return + if msg notin gNotes: return writeContext(unknownLineInfo()) frmt = RawWarningFormat inc(gWarnCounter) - of hintMin..hintMax: - if optHints notin gOptions: return - if msg notin gNotes: return + of hintMin..hintMax: + if optHints notin gOptions: return + if msg notin gNotes: return frmt = RawHintFormat inc(gHintCounter) let s = `%`(frmt, `%`(msgKindToString(msg), args)) @@ -778,7 +780,7 @@ proc rawMessage*(msg: TMsgKind, args: openArray[string]) = msgWriteln(s) handleError(msg, doAbort, s) -proc rawMessage*(msg: TMsgKind, arg: string) = +proc rawMessage*(msg: TMsgKind, arg: string) = rawMessage(msg, [arg]) proc writeSurroundingSrc(info: TLineInfo) = @@ -794,7 +796,7 @@ proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string = result = frmt % [toMsgFilename(info), coordToStr(info.line), coordToStr(info.col), getMessageStr(msg, arg)] -proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, +proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, eh: TErrorHandling) = var frmt: string var ignoreMsg = false @@ -811,7 +813,7 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, if not ignoreMsg: writeContext(info) frmt = PosWarningFormat inc(gWarnCounter) - of hintMin..hintMax: + of hintMin..hintMax: ignoreMsg = optHints notin gOptions or msg notin gNotes frmt = PosHintFormat inc(gHintCounter) @@ -822,11 +824,11 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, if optPrintSurroundingSrc and msg in errMin..errMax: info.writeSurroundingSrc handleError(msg, eh, s) - -proc fatal*(info: TLineInfo, msg: TMsgKind, arg = "") = + +proc fatal*(info: TLineInfo, msg: TMsgKind, arg = "") = liMessage(info, msg, arg, doAbort) -proc globalError*(info: TLineInfo, msg: TMsgKind, arg = "") = +proc globalError*(info: TLineInfo, msg: TMsgKind, arg = "") = liMessage(info, msg, arg, doRaise) proc globalError*(info: TLineInfo, arg: string) = @@ -841,12 +843,12 @@ proc localError*(info: TLineInfo, arg: string) = proc message*(info: TLineInfo, msg: TMsgKind, arg = "") = liMessage(info, msg, arg, doNothing) -proc internalError*(info: TLineInfo, errMsg: string) = +proc internalError*(info: TLineInfo, errMsg: string) = if gCmd == cmdIdeTools: return writeContext(info) liMessage(info, errInternal, errMsg, doAbort) -proc internalError*(errMsg: string) = +proc internalError*(errMsg: string) = if gCmd == cmdIdeTools: return writeContext(unknownLineInfo()) rawMessage(errInternal, errMsg) @@ -863,7 +865,7 @@ proc addSourceLine*(fileIdx: int32, line: string) = proc sourceLine*(i: TLineInfo): PRope = if i.fileIndex < 0: return nil - + if not optPreserveOrigSource and fileInfos[i.fileIndex].lines.len == 0: try: for line in lines(i.toFullPath): diff --git a/compiler/nim.nim.cfg b/compiler/nim.nim.cfg index f4d8b9dcb..64631a437 100644 --- a/compiler/nim.nim.cfg +++ b/compiler/nim.nim.cfg @@ -18,3 +18,4 @@ define:useStdoutAsStdmsg cs:partial #define:useNodeIds symbol:nimfix +#gc:markAndSweep 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 f5cabb4bc..689bf23c8 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -9,20 +9,20 @@ # This module implements the renderer of the standard Nim representation. -import +import lexer, options, idents, strutils, ast, msgs, lists -type - TRenderFlag* = enum - renderNone, renderNoBody, renderNoComments, renderDocComments, +type + TRenderFlag* = enum + renderNone, renderNoBody, renderNoComments, renderDocComments, renderNoPragmas, renderIds, renderNoProcDefs TRenderFlags* = set[TRenderFlag] - TRenderTok*{.final.} = object + TRenderTok*{.final.} = object kind*: TTokType length*: int16 TRenderTokSeq* = seq[TRenderTok] - TSrcGen*{.final.} = object + TSrcGen*{.final.} = object indent*: int lineLen*: int pos*: int # current position for iteration over the buffer @@ -41,6 +41,8 @@ proc renderModule*(n: PNode, filename: string, renderFlags: TRenderFlags = {}) proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string proc initTokRender*(r: var TSrcGen, n: PNode, renderFlags: TRenderFlags = {}) proc getNextTok*(r: var TSrcGen, kind: var TTokType, literal: var string) + +proc `$`*(n: PNode): string = n.renderTree # implementation # We render the source code in a two phases: The first # determines how long the subtree will likely be, the second @@ -65,13 +67,13 @@ proc renderDefinitionName*(s: PSym, noQuotes = false): string = else: result = '`' & x & '`' -const +const IndentWidth = 2 longIndentWid = 4 MaxLineLen = 80 LineCommentColumn = 30 -proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags) = +proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags) = g.comStack = @[] g.tokens = @[] g.indent = 0 @@ -83,67 +85,67 @@ proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags) = g.pendingNL = -1 g.checkAnon = false -proc addTok(g: var TSrcGen, kind: TTokType, s: string) = +proc addTok(g: var TSrcGen, kind: TTokType, s: string) = var length = len(g.tokens) setLen(g.tokens, length + 1) g.tokens[length].kind = kind g.tokens[length].length = int16(len(s)) add(g.buf, s) -proc addPendingNL(g: var TSrcGen) = - if g.pendingNL >= 0: +proc addPendingNL(g: var TSrcGen) = + if g.pendingNL >= 0: addTok(g, tkSpaces, "\n" & spaces(g.pendingNL)) g.lineLen = g.pendingNL g.pendingNL = - 1 -proc putNL(g: var TSrcGen, indent: int) = +proc putNL(g: var TSrcGen, indent: int) = if g.pendingNL >= 0: addPendingNL(g) else: addTok(g, tkSpaces, "\n") g.pendingNL = indent g.lineLen = indent -proc putNL(g: var TSrcGen) = +proc putNL(g: var TSrcGen) = putNL(g, g.indent) -proc optNL(g: var TSrcGen, indent: int) = +proc optNL(g: var TSrcGen, indent: int) = g.pendingNL = indent g.lineLen = indent # BUGFIX - -proc optNL(g: var TSrcGen) = + +proc optNL(g: var TSrcGen) = optNL(g, g.indent) -proc indentNL(g: var TSrcGen) = +proc indentNL(g: var TSrcGen) = inc(g.indent, IndentWidth) g.pendingNL = g.indent g.lineLen = g.indent -proc dedent(g: var TSrcGen) = +proc dedent(g: var TSrcGen) = dec(g.indent, IndentWidth) assert(g.indent >= 0) - if g.pendingNL > IndentWidth: + if g.pendingNL > IndentWidth: dec(g.pendingNL, IndentWidth) dec(g.lineLen, IndentWidth) -proc put(g: var TSrcGen, kind: TTokType, s: string) = +proc put(g: var TSrcGen, kind: TTokType, s: string) = addPendingNL(g) - if len(s) > 0: + if len(s) > 0: addTok(g, kind, s) inc(g.lineLen, len(s)) -proc putLong(g: var TSrcGen, kind: TTokType, s: string, lineLen: int) = +proc putLong(g: var TSrcGen, kind: TTokType, s: string, lineLen: int) = # use this for tokens over multiple lines. addPendingNL(g) addTok(g, kind, s) g.lineLen = lineLen -proc toNimChar(c: char): string = +proc toNimChar(c: char): string = case c of '\0': result = "\\0" of '\x01'..'\x1F', '\x80'..'\xFF': result = "\\x" & strutils.toHex(ord(c), 2) of '\'', '\"', '\\': result = '\\' & c else: result = c & "" - -proc makeNimString(s: string): string = + +proc makeNimString(s: string): string = result = "\"" for i in countup(0, len(s)-1): add(result, toNimChar(s[i])) add(result, '\"') @@ -174,7 +176,7 @@ proc putComment(g: var TSrcGen, s: string) = add(com, s[i]) inc(i) comIndent = 0 - while s[i] == ' ': + while s[i] == ' ': add(com, s[i]) inc(i) inc(comIndent) @@ -187,109 +189,109 @@ proc putComment(g: var TSrcGen, s: string) = # compute length of the following word: var j = i while s[j] > ' ': inc(j) - if not isCode and (g.lineLen + (j - i) > MaxLineLen): + if not isCode and (g.lineLen + (j - i) > MaxLineLen): put(g, tkComment, com) optNL(g, ind) com = '#' & spaces(comIndent) - while s[i] > ' ': + while s[i] > ' ': add(com, s[i]) inc(i) put(g, tkComment, com) optNL(g) -proc maxLineLength(s: string): int = +proc maxLineLength(s: string): int = if s.isNil: return 0 var i = 0 var lineLen = 0 while true: case s[i] - of '\0': - break - of '\x0D': + of '\0': + break + of '\x0D': inc(i) if s[i] == '\x0A': inc(i) result = max(result, lineLen) lineLen = 0 - of '\x0A': + of '\x0A': inc(i) result = max(result, lineLen) lineLen = 0 - else: + else: inc(lineLen) inc(i) -proc putRawStr(g: var TSrcGen, kind: TTokType, s: string) = +proc putRawStr(g: var TSrcGen, kind: TTokType, s: string) = var i = 0 var hi = len(s) - 1 var str = "" - while i <= hi: + while i <= hi: case s[i] - of '\x0D': + of '\x0D': put(g, kind, str) str = "" inc(i) if (i <= hi) and (s[i] == '\x0A'): inc(i) optNL(g, 0) - of '\x0A': + of '\x0A': put(g, kind, str) str = "" inc(i) optNL(g, 0) - else: + else: add(str, s[i]) inc(i) put(g, kind, str) -proc containsNL(s: string): bool = - for i in countup(0, len(s) - 1): +proc containsNL(s: string): bool = + for i in countup(0, len(s) - 1): case s[i] - of '\x0D', '\x0A': + of '\x0D', '\x0A': return true - else: + else: discard result = false -proc pushCom(g: var TSrcGen, n: PNode) = +proc pushCom(g: var TSrcGen, n: PNode) = var length = len(g.comStack) setLen(g.comStack, length + 1) g.comStack[length] = n -proc popAllComs(g: var TSrcGen) = +proc popAllComs(g: var TSrcGen) = setLen(g.comStack, 0) -proc popCom(g: var TSrcGen) = +proc popCom(g: var TSrcGen) = setLen(g.comStack, len(g.comStack) - 1) -const +const Space = " " -proc shouldRenderComment(g: var TSrcGen, n: PNode): bool = +proc shouldRenderComment(g: var TSrcGen, n: PNode): bool = result = false - if n.comment != nil: + if n.comment != nil: result = (renderNoComments notin g.flags) or (renderDocComments in g.flags) and startsWith(n.comment, "##") - -proc gcom(g: var TSrcGen, n: PNode) = + +proc gcom(g: var TSrcGen, n: PNode) = assert(n != nil) - if shouldRenderComment(g, n): + if shouldRenderComment(g, n): if (g.pendingNL < 0) and (len(g.buf) > 0) and (g.buf[len(g.buf)-1] != ' '): - put(g, tkSpaces, Space) + put(g, tkSpaces, Space) # Before long comments we cannot make sure that a newline is generated, # because this might be wrong. But it is no problem in practice. if (g.pendingNL < 0) and (len(g.buf) > 0) and - (g.lineLen < LineCommentColumn): + (g.lineLen < LineCommentColumn): var ml = maxLineLength(n.comment) - if ml + LineCommentColumn <= MaxLineLen: + if ml + LineCommentColumn <= MaxLineLen: put(g, tkSpaces, spaces(LineCommentColumn - g.lineLen)) putComment(g, n.comment) #assert(g.comStack[high(g.comStack)] = n); - -proc gcoms(g: var TSrcGen) = + +proc gcoms(g: var TSrcGen) = for i in countup(0, high(g.comStack)): gcom(g, g.comStack[i]) popAllComs(g) proc lsub(n: PNode): int proc litAux(n: PNode, x: BiggestInt, size: int): string = - proc skip(t: PType): PType = + proc skip(t: PType): PType = result = t while result.kind in {tyGenericInst, tyRange, tyVar, tyDistinct, tyOrdinal, tyConst, tyMutable}: @@ -299,20 +301,20 @@ proc litAux(n: PNode, x: BiggestInt, size: int): string = # we need a slow linear search because of enums with holes: for e in items(enumfields): if e.sym.position == x: return e.sym.name.s - + if nfBase2 in n.flags: result = "0b" & toBin(x, size * 8) elif nfBase8 in n.flags: result = "0o" & toOct(x, size * 3) elif nfBase16 in n.flags: result = "0x" & toHex(x, size * 2) else: result = $x -proc ulitAux(n: PNode, x: BiggestInt, size: int): string = +proc ulitAux(n: PNode, x: BiggestInt, size: int): string = if nfBase2 in n.flags: result = "0b" & toBin(x, size * 8) elif nfBase8 in n.flags: result = "0o" & toOct(x, size * 3) elif nfBase16 in n.flags: result = "0x" & toHex(x, size * 2) else: result = $x # XXX proper unsigned output! - -proc atom(n: PNode): string = + +proc atom(n: PNode): string = var f: float32 case n.kind of nkEmpty: result = "" @@ -335,49 +337,49 @@ proc atom(n: PNode): string = of nkFloatLit: if n.flags * {nfBase2, nfBase8, nfBase16} == {}: result = $(n.floatVal) else: result = litAux(n, (cast[PInt64](addr(n.floatVal)))[] , 8) - of nkFloat32Lit: - if n.flags * {nfBase2, nfBase8, nfBase16} == {}: + of nkFloat32Lit: + if n.flags * {nfBase2, nfBase8, nfBase16} == {}: result = $n.floatVal & "\'f32" - else: + else: f = n.floatVal.float32 result = litAux(n, (cast[PInt32](addr(f)))[], 4) & "\'f32" - of nkFloat64Lit: - if n.flags * {nfBase2, nfBase8, nfBase16} == {}: + of nkFloat64Lit: + if n.flags * {nfBase2, nfBase8, nfBase16} == {}: result = $n.floatVal & "\'f64" - else: + else: result = litAux(n, (cast[PInt64](addr(n.floatVal)))[], 8) & "\'f64" of nkNilLit: result = "nil" - of nkType: + of nkType: if (n.typ != nil) and (n.typ.sym != nil): result = n.typ.sym.name.s else: result = "[type node]" - else: + else: internalError("rnimsyn.atom " & $n.kind) result = "" - -proc lcomma(n: PNode, start: int = 0, theEnd: int = - 1): int = + +proc lcomma(n: PNode, start: int = 0, theEnd: int = - 1): int = assert(theEnd < 0) result = 0 - for i in countup(start, sonsLen(n) + theEnd): + for i in countup(start, sonsLen(n) + theEnd): inc(result, lsub(n.sons[i])) inc(result, 2) # for ``, `` - if result > 0: + if result > 0: dec(result, 2) # last does not get a comma! - -proc lsons(n: PNode, start: int = 0, theEnd: int = - 1): int = + +proc lsons(n: PNode, start: int = 0, theEnd: int = - 1): int = assert(theEnd < 0) result = 0 for i in countup(start, sonsLen(n) + theEnd): inc(result, lsub(n.sons[i])) - -proc lsub(n: PNode): int = + +proc lsub(n: PNode): int = # computes the length of a tree if isNil(n): return 0 if n.comment != nil: return MaxLineLen + 1 case n.kind of nkEmpty: result = 0 - of nkTripleStrLit: + of nkTripleStrLit: if containsNL(n.strVal): result = MaxLineLen + 1 else: result = len(atom(n)) - of succ(nkEmpty)..pred(nkTripleStrLit), succ(nkTripleStrLit)..nkNilLit: + of succ(nkEmpty)..pred(nkTripleStrLit), succ(nkTripleStrLit)..nkNilLit: result = len(atom(n)) of nkCall, nkBracketExpr, nkCurlyExpr, nkConv, nkPattern, nkObjConstr: result = lsub(n.sons[0]) + lcomma(n, 1) + 2 @@ -392,9 +394,10 @@ proc lsub(n: PNode): int = of nkArgList: result = lcomma(n) of nkTableConstr: result = if n.len > 0: lcomma(n) + 2 else: len("{:}") - of nkClosedSymChoice, nkOpenSymChoice: + 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_") @@ -402,7 +405,7 @@ proc lsub(n: PNode): int = of nkCheckedFieldExpr: result = lsub(n.sons[0]) of nkLambda: result = lsons(n) + len("proc__=_") of nkDo: result = lsons(n) + len("do__:_") - of nkConstDef, nkIdentDefs: + of nkConstDef, nkIdentDefs: result = lcomma(n, 0, - 3) var L = sonsLen(n) if n.sons[L - 2].kind != nkEmpty: result = result + lsub(n.sons[L - 2]) + 2 @@ -411,7 +414,7 @@ proc lsub(n: PNode): int = of nkChckRangeF: result = len("chckRangeF") + 2 + lcomma(n) of nkChckRange64: result = len("chckRange64") + 2 + lcomma(n) of nkChckRange: result = len("chckRange") + 2 + lcomma(n) - of nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString: + of nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString: result = 2 if sonsLen(n) >= 1: result = result + lsub(n.sons[0]) result = result + lcomma(n, 1) @@ -425,7 +428,7 @@ proc lsub(n: PNode): int = of nkRange: result = lsons(n) + 2 of nkDerefExpr: result = lsub(n.sons[0]) + 2 of nkAccQuoted: result = lsons(n) + 2 - of nkIfExpr: + of nkIfExpr: result = lsub(n.sons[0].sons[0]) + lsub(n.sons[0].sons[1]) + lsons(n, 1) + len("if_:_") of nkElifExpr: result = lsons(n) + len("_elif_:_") @@ -446,13 +449,13 @@ proc lsub(n: PNode): int = of nkProcTy: result = lsons(n) + len("proc_") of nkIteratorTy: result = lsons(n) + len("iterator_") of nkSharedTy: result = lsons(n) + len("shared_") - of nkEnumTy: + of nkEnumTy: if sonsLen(n) > 0: result = lsub(n.sons[0]) + lcomma(n, 1) + len("enum_") else: result = len("enum") of nkEnumFieldDef: result = lsons(n) + 3 - of nkVarSection, nkLetSection: + of nkVarSection, nkLetSection: if sonsLen(n) > 1: result = MaxLineLen + 1 else: result = lsons(n) + len("var_") of nkReturnStmt: result = lsub(n.sons[0]) + len("return_") @@ -469,50 +472,50 @@ proc lsub(n: PNode): int = of nkElse: result = lsub(n.sons[0]) + len("else:_") of nkFinally: result = lsub(n.sons[0]) + len("finally:_") of nkGenericParams: result = lcomma(n) + 2 - of nkFormalParams: + of nkFormalParams: result = lcomma(n, 1) + 2 if n.sons[0].kind != nkEmpty: result = result + lsub(n.sons[0]) + 2 - of nkExceptBranch: + of nkExceptBranch: result = lcomma(n, 0, -2) + lsub(lastSon(n)) + len("except_:_") else: result = MaxLineLen + 1 - -proc fits(g: TSrcGen, x: int): bool = + +proc fits(g: TSrcGen, x: int): bool = result = x + g.lineLen <= MaxLineLen -type - TSubFlag = enum +type + TSubFlag = enum rfLongMode, rfNoIndent, rfInConstExpr TSubFlags = set[TSubFlag] TContext = tuple[spacing: int, flags: TSubFlags] -const +const emptyContext: TContext = (spacing: 0, flags: {}) -proc initContext(c: var TContext) = +proc initContext(c: var TContext) = c.spacing = 0 c.flags = {} proc gsub(g: var TSrcGen, n: PNode, c: TContext) -proc gsub(g: var TSrcGen, n: PNode) = +proc gsub(g: var TSrcGen, n: PNode) = var c: TContext initContext(c) gsub(g, n, c) -proc hasCom(n: PNode): bool = +proc hasCom(n: PNode): bool = result = false if n.comment != nil: return true case n.kind of nkEmpty..nkNilLit: discard - else: - for i in countup(0, sonsLen(n) - 1): + else: + for i in countup(0, sonsLen(n) - 1): if hasCom(n.sons[i]): return true - -proc putWithSpace(g: var TSrcGen, kind: TTokType, s: string) = + +proc putWithSpace(g: var TSrcGen, kind: TTokType, s: string) = put(g, kind, s) put(g, tkSpaces, Space) -proc gcommaAux(g: var TSrcGen, n: PNode, ind: int, start: int = 0, - theEnd: int = - 1, separator = tkComma) = +proc gcommaAux(g: var TSrcGen, n: PNode, ind: int, start: int = 0, + theEnd: int = - 1, separator = tkComma) = for i in countup(start, sonsLen(n) + theEnd): var c = i < sonsLen(n) + theEnd var sublen = lsub(n.sons[i]) + ord(c) @@ -522,54 +525,54 @@ proc gcommaAux(g: var TSrcGen, n: PNode, ind: int, start: int = 0, if c: if g.tokens.len > oldLen: putWithSpace(g, separator, TokTypeToStr[separator]) - if hasCom(n.sons[i]): + if hasCom(n.sons[i]): gcoms(g) optNL(g, ind) -proc gcomma(g: var TSrcGen, n: PNode, c: TContext, start: int = 0, - theEnd: int = - 1) = +proc gcomma(g: var TSrcGen, n: PNode, c: TContext, start: int = 0, + theEnd: int = - 1) = var ind: int - if rfInConstExpr in c.flags: + if rfInConstExpr in c.flags: ind = g.indent + IndentWidth - else: + else: ind = g.lineLen if ind > MaxLineLen div 2: ind = g.indent + longIndentWid gcommaAux(g, n, ind, start, theEnd) -proc gcomma(g: var TSrcGen, n: PNode, start: int = 0, theEnd: int = - 1) = +proc gcomma(g: var TSrcGen, n: PNode, start: int = 0, theEnd: int = - 1) = var ind = g.lineLen if ind > MaxLineLen div 2: ind = g.indent + longIndentWid gcommaAux(g, n, ind, start, theEnd) -proc gsemicolon(g: var TSrcGen, n: PNode, start: int = 0, theEnd: int = - 1) = +proc gsemicolon(g: var TSrcGen, n: PNode, start: int = 0, theEnd: int = - 1) = var ind = g.lineLen if ind > MaxLineLen div 2: ind = g.indent + longIndentWid gcommaAux(g, n, ind, start, theEnd, tkSemiColon) -proc gsons(g: var TSrcGen, n: PNode, c: TContext, start: int = 0, - theEnd: int = - 1) = +proc gsons(g: var TSrcGen, n: PNode, c: TContext, start: int = 0, + theEnd: int = - 1) = for i in countup(start, sonsLen(n) + theEnd): gsub(g, n.sons[i], c) -proc gsection(g: var TSrcGen, n: PNode, c: TContext, kind: TTokType, - k: string) = +proc gsection(g: var TSrcGen, n: PNode, c: TContext, kind: TTokType, + k: string) = if sonsLen(n) == 0: return # empty var sections are possible putWithSpace(g, kind, k) gcoms(g) indentNL(g) - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): optNL(g) gsub(g, n.sons[i], c) gcoms(g) dedent(g) -proc longMode(n: PNode, start: int = 0, theEnd: int = - 1): bool = +proc longMode(n: PNode, start: int = 0, theEnd: int = - 1): bool = result = n.comment != nil - if not result: + if not result: # check further - for i in countup(start, sonsLen(n) + theEnd): - if (lsub(n.sons[i]) > MaxLineLen): + for i in countup(start, sonsLen(n) + theEnd): + if (lsub(n.sons[i]) > MaxLineLen): result = true - break + break proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) = if n.kind == nkEmpty: return @@ -589,33 +592,33 @@ proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) = gcoms(g) optNL(g) if rfLongMode in c.flags: dedent(g) - -proc gif(g: var TSrcGen, n: PNode) = + +proc gif(g: var TSrcGen, n: PNode) = var c: TContext gsub(g, n.sons[0].sons[0]) initContext(c) putWithSpace(g, tkColon, ":") - if longMode(n) or (lsub(n.sons[0].sons[1]) + g.lineLen > MaxLineLen): + if longMode(n) or (lsub(n.sons[0].sons[1]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) gcoms(g) # a good place for comments gstmts(g, n.sons[0].sons[1], c) var length = sonsLen(n) - for i in countup(1, length - 1): + for i in countup(1, length - 1): optNL(g) gsub(g, n.sons[i], c) -proc gwhile(g: var TSrcGen, n: PNode) = +proc gwhile(g: var TSrcGen, n: PNode) = var c: TContext putWithSpace(g, tkWhile, "while") gsub(g, n.sons[0]) putWithSpace(g, tkColon, ":") initContext(c) - if longMode(n) or (lsub(n.sons[1]) + g.lineLen > MaxLineLen): + if longMode(n) or (lsub(n.sons[1]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) gcoms(g) # a good place for comments gstmts(g, n.sons[1], c) -proc gpattern(g: var TSrcGen, n: PNode) = +proc gpattern(g: var TSrcGen, n: PNode) = var c: TContext put(g, tkCurlyLe, "{") initContext(c) @@ -625,7 +628,7 @@ proc gpattern(g: var TSrcGen, n: PNode) = gstmts(g, n, c) put(g, tkCurlyRi, "}") -proc gpragmaBlock(g: var TSrcGen, n: PNode) = +proc gpragmaBlock(g: var TSrcGen, n: PNode) = var c: TContext gsub(g, n.sons[0]) putWithSpace(g, tkColon, ":") @@ -635,25 +638,25 @@ proc gpragmaBlock(g: var TSrcGen, n: PNode) = gcoms(g) # a good place for comments gstmts(g, n.sons[1], c) -proc gtry(g: var TSrcGen, n: PNode) = +proc gtry(g: var TSrcGen, n: PNode) = var c: TContext put(g, tkTry, "try") putWithSpace(g, tkColon, ":") initContext(c) - if longMode(n) or (lsub(n.sons[0]) + g.lineLen > MaxLineLen): + if longMode(n) or (lsub(n.sons[0]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) gcoms(g) # a good place for comments gstmts(g, n.sons[0], c) gsons(g, n, c, 1) -proc gfor(g: var TSrcGen, n: PNode) = +proc gfor(g: var TSrcGen, n: PNode) = var c: TContext var length = sonsLen(n) putWithSpace(g, tkFor, "for") initContext(c) if longMode(n) or (lsub(n.sons[length - 1]) + lsub(n.sons[length - 2]) + 6 + g.lineLen > - MaxLineLen): + MaxLineLen): incl(c.flags, rfLongMode) gcomma(g, n, c, 0, - 3) put(g, tkSpaces, Space) @@ -663,17 +666,17 @@ proc gfor(g: var TSrcGen, n: PNode) = gcoms(g) gstmts(g, n.sons[length - 1], c) -proc gmacro(g: var TSrcGen, n: PNode) = +proc gmacro(g: var TSrcGen, n: PNode) = var c: TContext initContext(c) gsub(g, n.sons[0]) putWithSpace(g, tkColon, ":") - if longMode(n) or (lsub(n.sons[1]) + g.lineLen > MaxLineLen): + if longMode(n) or (lsub(n.sons[1]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) gcoms(g) gsons(g, n, c, 1) -proc gcase(g: var TSrcGen, n: PNode) = +proc gcase(g: var TSrcGen, n: PNode) = var c: TContext initContext(c) var length = sonsLen(n) @@ -684,18 +687,18 @@ proc gcase(g: var TSrcGen, n: PNode) = gcoms(g) optNL(g) gsons(g, n, c, 1, last) - if last == - 2: + if last == - 2: initContext(c) if longMode(n.sons[length - 1]): incl(c.flags, rfLongMode) gsub(g, n.sons[length - 1], c) -proc gproc(g: var TSrcGen, n: PNode) = +proc gproc(g: var TSrcGen, n: PNode) = var c: TContext if n.sons[namePos].kind == nkSym: put(g, tkSymbol, renderDefinitionName(n.sons[namePos].sym)) else: gsub(g, n.sons[namePos]) - + if n.sons[patternPos].kind != nkEmpty: gpattern(g, n.sons[patternPos]) let oldCheckAnon = g.checkAnon @@ -732,7 +735,7 @@ proc gTypeClassTy(g: var TSrcGen, n: PNode) = gstmts(g, n[3], c) dedent(g) -proc gblock(g: var TSrcGen, n: PNode) = +proc gblock(g: var TSrcGen, n: PNode) = var c: TContext initContext(c) if n.sons[0].kind != nkEmpty: @@ -741,7 +744,7 @@ proc gblock(g: var TSrcGen, n: PNode) = else: put(g, tkBlock, "block") putWithSpace(g, tkColon, ":") - if longMode(n) or (lsub(n.sons[1]) + g.lineLen > MaxLineLen): + if longMode(n) or (lsub(n.sons[1]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) gcoms(g) # XXX I don't get why this is needed here! gstmts should already handle this! @@ -749,17 +752,17 @@ proc gblock(g: var TSrcGen, n: PNode) = gstmts(g, n.sons[1], c) dedent(g) -proc gstaticStmt(g: var TSrcGen, n: PNode) = +proc gstaticStmt(g: var TSrcGen, n: PNode) = var c: TContext putWithSpace(g, tkStatic, "static") putWithSpace(g, tkColon, ":") initContext(c) - if longMode(n) or (lsub(n.sons[0]) + g.lineLen > MaxLineLen): + if longMode(n) or (lsub(n.sons[0]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) gcoms(g) # a good place for comments gstmts(g, n.sons[0], c) -proc gasm(g: var TSrcGen, n: PNode) = +proc gasm(g: var TSrcGen, n: PNode) = putWithSpace(g, tkAsm, "asm") gsub(g, n.sons[0]) gcoms(g) @@ -769,16 +772,16 @@ proc gident(g: var TSrcGen, n: PNode) = if g.checkAnon and n.kind == nkSym and sfAnon in n.sym.flags: return var t: TTokType var s = atom(n) - if (s[0] in lexer.SymChars): - if (n.kind == nkIdent): + if (s[0] in lexer.SymChars): + if (n.kind == nkIdent): if (n.ident.id < ord(tokKeywordLow) - ord(tkSymbol)) or - (n.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)): + (n.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)): t = tkSymbol - else: + else: t = TTokType(n.ident.id + ord(tkSymbol)) - else: + else: t = tkSymbol - else: + else: t = tkOpr put(g, t, s) if n.kind == nkSym and renderIds in g.flags: put(g, tkIntLit, $n.sym.id) @@ -788,12 +791,12 @@ proc doParamsAux(g: var TSrcGen, params: PNode) = put(g, tkParLe, "(") gsemicolon(g, params, 1) put(g, tkParRi, ")") - - if params.sons[0].kind != nkEmpty: + + if params.sons[0].kind != nkEmpty: putWithSpace(g, tkOpr, "->") gsub(g, params.sons[0]) -proc gsub(g: var TSrcGen, n: PNode, c: TContext) = +proc gsub(g: var TSrcGen, n: PNode, c: TContext) = if isNil(n): return var a: TContext @@ -826,14 +829,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkParLe, "(") gcomma(g, n, 1) put(g, tkParRi, ")") - of nkCallStrLit: + of nkCallStrLit: gsub(g, n.sons[0]) - if n.sons[1].kind == nkRStrLit: + if n.sons[1].kind == nkRStrLit: put(g, tkRStrLit, '\"' & replace(n[1].strVal, "\"", "\"\"") & '\"') - else: + else: gsub(g, n.sons[1]) of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv: gsub(g, n.sons[1]) - of nkCast: + of nkCast: put(g, tkCast, "cast") put(g, tkBracketLe, "[") gsub(g, n.sons[0]) @@ -841,7 +844,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkParLe, "(") gsub(g, n.sons[1]) put(g, tkParRi, ")") - of nkAddr: + of nkAddr: put(g, tkAddr, "addr") put(g, tkParLe, "(") gsub(g, n.sons[0]) @@ -850,7 +853,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkStatic, "static") put(g, tkSpaces, Space) gsub(g, n.sons[0]) - of nkBracketExpr: + of nkBracketExpr: gsub(g, n.sons[0]) put(g, tkBracketLe, "[") gcomma(g, n, 1) @@ -860,41 +863,41 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkCurlyLe, "{") gcomma(g, n, 1) put(g, tkCurlyRi, "}") - of nkPragmaExpr: + of nkPragmaExpr: gsub(g, n.sons[0]) gcomma(g, n, 1) - of nkCommand: + of nkCommand: gsub(g, n.sons[0]) put(g, tkSpaces, Space) gcomma(g, n, 1) - of nkExprEqExpr, nkAsgn, nkFastAsgn: + of nkExprEqExpr, nkAsgn, nkFastAsgn: gsub(g, n.sons[0]) put(g, tkSpaces, Space) putWithSpace(g, tkEquals, "=") gsub(g, n.sons[1]) - of nkChckRangeF: + of nkChckRangeF: put(g, tkSymbol, "chckRangeF") put(g, tkParLe, "(") gcomma(g, n) put(g, tkParRi, ")") - of nkChckRange64: + of nkChckRange64: put(g, tkSymbol, "chckRange64") put(g, tkParLe, "(") gcomma(g, n) put(g, tkParRi, ")") - of nkChckRange: + of nkChckRange: put(g, tkSymbol, "chckRange") put(g, tkParLe, "(") gcomma(g, n) put(g, tkParRi, ")") - of nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString: + of nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString: if sonsLen(n) >= 1: gsub(g, n.sons[0]) put(g, tkParLe, "(") gcomma(g, n, 1) put(g, tkParRi, ")") of nkClosedSymChoice, nkOpenSymChoice: put(g, tkParLe, "(") - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): if i > 0: put(g, tkOpr, "|") if n.sons[i].kind == nkSym: let s = n[i].sym @@ -905,11 +908,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = else: gsub(g, n.sons[i], c) put(g, tkParRi, if n.kind == nkOpenSymChoice: "|...)" else: ")") - of nkPar, nkClosure: + of nkPar, nkClosure: put(g, tkParLe, "(") gcomma(g, n, c) put(g, tkParRi, ")") - of nkCurly: + of nkCurly: put(g, tkCurlyLe, "{") gcomma(g, n, c) put(g, tkCurlyRi, "}") @@ -924,14 +927,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkBracketLe, "[") gcomma(g, n, c) put(g, tkBracketRi, "]") - of nkDotExpr: + of nkDotExpr: gsub(g, n.sons[0]) put(g, tkDot, ".") gsub(g, n.sons[1]) - of nkBind: + of nkBind: putWithSpace(g, tkBind, "bind") gsub(g, n.sons[0]) - of nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref: + of nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref: gsub(g, n.sons[0]) of nkLambda: putWithSpace(g, tkProc, "proc") @@ -949,34 +952,34 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkConstDef, nkIdentDefs: gcomma(g, n, 0, -3) var L = sonsLen(n) - if L >= 2 and n.sons[L - 2].kind != nkEmpty: + if L >= 2 and n.sons[L - 2].kind != nkEmpty: putWithSpace(g, tkColon, ":") gsub(g, n.sons[L - 2]) - if L >= 1 and n.sons[L - 1].kind != nkEmpty: + if L >= 1 and n.sons[L - 1].kind != nkEmpty: put(g, tkSpaces, Space) putWithSpace(g, tkEquals, "=") gsub(g, n.sons[L - 1], c) - of nkVarTuple: + of nkVarTuple: put(g, tkParLe, "(") gcomma(g, n, 0, -3) put(g, tkParRi, ")") put(g, tkSpaces, Space) putWithSpace(g, tkEquals, "=") gsub(g, lastSon(n), c) - of nkExprColonExpr: + of nkExprColonExpr: gsub(g, n.sons[0]) putWithSpace(g, tkColon, ":") gsub(g, n.sons[1]) - of nkInfix: + of nkInfix: gsub(g, n.sons[1]) put(g, tkSpaces, Space) gsub(g, n.sons[0]) # binary operator - if not fits(g, lsub(n.sons[2]) + lsub(n.sons[0]) + 1): + if not fits(g, lsub(n.sons[2]) + lsub(n.sons[0]) + 1): optNL(g, g.indent + longIndentWid) - else: + else: put(g, tkSpaces, Space) gsub(g, n.sons[2]) - of nkPrefix: + of nkPrefix: gsub(g, n.sons[0]) if n.len > 1: put(g, tkSpaces, Space) @@ -986,10 +989,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkParRi, ")") else: gsub(g, n.sons[1]) - of nkPostfix: + of nkPostfix: gsub(g, n.sons[1]) gsub(g, n.sons[0]) - of nkRange: + of nkRange: gsub(g, n.sons[0]) put(g, tkDotDot, "..") gsub(g, n.sons[1]) @@ -1003,43 +1006,43 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkSpaces, Space) gsub(g, n.sons[i]) put(g, tkAccent, "`") - of nkIfExpr: + of nkIfExpr: putWithSpace(g, tkIf, "if") gsub(g, n.sons[0].sons[0]) putWithSpace(g, tkColon, ":") gsub(g, n.sons[0].sons[1]) gsons(g, n, emptyContext, 1) - of nkElifExpr: + of nkElifExpr: putWithSpace(g, tkElif, " elif") gsub(g, n.sons[0]) putWithSpace(g, tkColon, ":") gsub(g, n.sons[1]) - of nkElseExpr: + of nkElseExpr: put(g, tkElse, " else") putWithSpace(g, tkColon, ":") gsub(g, n.sons[0]) of nkTypeOfExpr: putWithSpace(g, tkType, "type") if n.len > 0: gsub(g, n.sons[0]) - of nkRefTy: + of nkRefTy: if sonsLen(n) > 0: putWithSpace(g, tkRef, "ref") gsub(g, n.sons[0]) else: put(g, tkRef, "ref") - of nkPtrTy: + of nkPtrTy: if sonsLen(n) > 0: putWithSpace(g, tkPtr, "ptr") gsub(g, n.sons[0]) else: put(g, tkPtr, "ptr") - of nkVarTy: + of nkVarTy: if sonsLen(n) > 0: putWithSpace(g, tkVar, "var") gsub(g, n.sons[0]) else: put(g, tkVar, "var") - of nkDistinctTy: + of nkDistinctTy: if n.len > 0: putWithSpace(g, tkDistinct, "distinct") gsub(g, n.sons[0]) @@ -1051,14 +1054,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gcomma(g, n[1]) else: put(g, tkDistinct, "distinct") - of nkTypeDef: + of nkTypeDef: gsub(g, n.sons[0]) gsub(g, n.sons[1]) put(g, tkSpaces, Space) - if n.sons[2].kind != nkEmpty: + if n.sons[2].kind != nkEmpty: putWithSpace(g, tkEquals, "=") gsub(g, n.sons[2]) - of nkObjectTy: + of nkObjectTy: if sonsLen(n) > 0: putWithSpace(g, tkObject, "object") gsub(g, n.sons[0]) @@ -1067,18 +1070,18 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gsub(g, n.sons[2]) else: put(g, tkObject, "object") - of nkRecList: + of nkRecList: indentNL(g) - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): optNL(g) gsub(g, n.sons[i], c) gcoms(g) dedent(g) putNL(g) - of nkOfInherit: + of nkOfInherit: putWithSpace(g, tkOf, "of") gsub(g, n.sons[0]) - of nkProcTy: + of nkProcTy: if sonsLen(n) > 0: putWithSpace(g, tkProc, "proc") gsub(g, n.sons[0]) @@ -1097,7 +1100,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkBracketLe, "[") if n.len > 0: gsub(g, n.sons[0]) - put(g, tkBracketRi, "]") + put(g, tkBracketRi, "]") of nkEnumTy: if sonsLen(n) > 0: putWithSpace(g, tkEnum, "enum") @@ -1109,16 +1112,16 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = dedent(g) else: put(g, tkEnum, "enum") - of nkEnumFieldDef: + of nkEnumFieldDef: gsub(g, n.sons[0]) put(g, tkSpaces, Space) putWithSpace(g, tkEquals, "=") gsub(g, n.sons[1]) of nkStmtList, nkStmtListExpr, nkStmtListType: gstmts(g, n, emptyContext) - of nkIfStmt: + of nkIfStmt: putWithSpace(g, tkIf, "if") gif(g, n) - of nkWhen, nkRecWhen: + of nkWhen, nkRecWhen: putWithSpace(g, tkWhen, "when") gif(g, n) of nkWhileStmt: gwhile(g, n) @@ -1129,27 +1132,27 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkBlockStmt, nkBlockExpr: gblock(g, n) of nkStaticStmt: gstaticStmt(g, n) of nkAsmStmt: gasm(g, n) - of nkProcDef: + of nkProcDef: if renderNoProcDefs notin g.flags: putWithSpace(g, tkProc, "proc") gproc(g, n) of nkConverterDef: if renderNoProcDefs notin g.flags: putWithSpace(g, tkConverter, "converter") gproc(g, n) - of nkMethodDef: + of nkMethodDef: if renderNoProcDefs notin g.flags: putWithSpace(g, tkMethod, "method") gproc(g, n) - of nkIteratorDef: + of nkIteratorDef: if renderNoProcDefs notin g.flags: putWithSpace(g, tkIterator, "iterator") gproc(g, n) - of nkMacroDef: + of nkMacroDef: if renderNoProcDefs notin g.flags: putWithSpace(g, tkMacro, "macro") gproc(g, n) - of nkTemplateDef: + of nkTemplateDef: if renderNoProcDefs notin g.flags: putWithSpace(g, tkTemplate, "template") gproc(g, n) - of nkTypeSection: + of nkTypeSection: gsection(g, n, emptyContext, tkType, "type") - of nkConstSection: + of nkConstSection: initContext(a) incl(a.flags, rfInConstExpr) gsection(g, n, a, tkConst, "const") @@ -1158,32 +1161,32 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = if L == 0: return if n.kind == nkVarSection: putWithSpace(g, tkVar, "var") else: putWithSpace(g, tkLet, "let") - if L > 1: + if L > 1: gcoms(g) indentNL(g) - for i in countup(0, L - 1): + for i in countup(0, L - 1): optNL(g) gsub(g, n.sons[i]) gcoms(g) dedent(g) - else: + else: gsub(g, n.sons[0]) - of nkReturnStmt: + of nkReturnStmt: putWithSpace(g, tkReturn, "return") gsub(g, n.sons[0]) - of nkRaiseStmt: + of nkRaiseStmt: putWithSpace(g, tkRaise, "raise") gsub(g, n.sons[0]) - of nkYieldStmt: + of nkYieldStmt: putWithSpace(g, tkYield, "yield") gsub(g, n.sons[0]) - of nkDiscardStmt: + of nkDiscardStmt: putWithSpace(g, tkDiscard, "discard") gsub(g, n.sons[0]) - of nkBreakStmt: + of nkBreakStmt: putWithSpace(g, tkBreak, "break") gsub(g, n.sons[0]) - of nkContinueStmt: + of nkContinueStmt: putWithSpace(g, tkContinue, "continue") gsub(g, n.sons[0]) of nkPragma: @@ -1218,24 +1221,24 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gcommaAux(g, n, g.indent, 1) gcoms(g) putNL(g) - of nkFromStmt: + of nkFromStmt: putWithSpace(g, tkFrom, "from") gsub(g, n.sons[0]) put(g, tkSpaces, Space) putWithSpace(g, tkImport, "import") gcomma(g, n, emptyContext, 1) putNL(g) - of nkIncludeStmt: + of nkIncludeStmt: putWithSpace(g, tkInclude, "include") gcoms(g) indentNL(g) gcommaAux(g, n, g.indent) dedent(g) putNL(g) - of nkCommentStmt: + of nkCommentStmt: gcoms(g) optNL(g) - of nkOfBranch: + of nkOfBranch: optNL(g) putWithSpace(g, tkOf, "of") gcomma(g, n, c, 0, - 2) @@ -1247,55 +1250,56 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkSpaces, Space) putWithSpace(g, tkAs, "as") gsub(g, n.sons[1]) - of nkBindStmt: + of nkBindStmt: putWithSpace(g, tkBind, "bind") gcomma(g, n, c) of nkMixinStmt: putWithSpace(g, tkMixin, "mixin") gcomma(g, n, c) - of nkElifBranch: + of nkElifBranch: optNL(g) putWithSpace(g, tkElif, "elif") gsub(g, n.sons[0]) putWithSpace(g, tkColon, ":") gcoms(g) gstmts(g, n.sons[1], c) - of nkElse: + of nkElse: optNL(g) put(g, tkElse, "else") putWithSpace(g, tkColon, ":") gcoms(g) gstmts(g, n.sons[0], c) - of nkFinally: + of nkFinally: optNL(g) put(g, tkFinally, "finally") putWithSpace(g, tkColon, ":") gcoms(g) gstmts(g, n.sons[0], c) - of nkExceptBranch: + of nkExceptBranch: optNL(g) putWithSpace(g, tkExcept, "except") gcomma(g, n, 0, - 2) putWithSpace(g, tkColon, ":") gcoms(g) gstmts(g, lastSon(n), c) - of nkGenericParams: + of nkGenericParams: put(g, tkBracketLe, "[") gcomma(g, n) put(g, tkBracketRi, "]") - of nkFormalParams: + of nkFormalParams: put(g, tkParLe, "(") gsemicolon(g, n, 1) put(g, tkParRi, ")") - if n.sons[0].kind != nkEmpty: + if n.sons[0].kind != nkEmpty: putWithSpace(g, tkColon, ":") gsub(g, n.sons[0]) - of nkTupleTy: + of nkTupleTy: + put(g, tkTuple, "tuple") + put(g, tkBracketLe, "[") + gcomma(g, n) + put(g, tkBracketRi, "]") + of nkTupleClassTy: put(g, tkTuple, "tuple") - if sonsLen(n) > 0: - put(g, tkBracketLe, "[") - gcomma(g, n) - put(g, tkBracketRi, "]") of nkMetaNode_Obsolete: put(g, tkParLe, "(META|") gsub(g, n.sons[0]) @@ -1307,17 +1311,17 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gsons(g, n, c) of nkTypeClassTy: gTypeClassTy(g, n) - else: - #nkNone, nkExplicitTypeListCall: + else: + #nkNone, nkExplicitTypeListCall: internalError(n.info, "rnimsyn.gsub(" & $n.kind & ')') -proc renderTree(n: PNode, renderFlags: TRenderFlags = {}): string = +proc renderTree(n: PNode, renderFlags: TRenderFlags = {}): string = var g: TSrcGen initSrcGen(g, renderFlags) gsub(g, n) result = g.buf -proc renderModule(n: PNode, filename: string, +proc renderModule(n: PNode, filename: string, renderFlags: TRenderFlags = {}) = var f: File @@ -1337,18 +1341,18 @@ proc renderModule(n: PNode, filename: string, write(f, g.buf) close(f) else: - rawMessage(errCannotOpenFile, filename) + rawMessage(errCannotOpenFile, filename) -proc initTokRender(r: var TSrcGen, n: PNode, renderFlags: TRenderFlags = {}) = +proc initTokRender(r: var TSrcGen, n: PNode, renderFlags: TRenderFlags = {}) = initSrcGen(r, renderFlags) gsub(r, n) -proc getNextTok(r: var TSrcGen, kind: var TTokType, literal: var string) = - if r.idx < len(r.tokens): +proc getNextTok(r: var TSrcGen, kind: var TTokType, literal: var string) = + if r.idx < len(r.tokens): kind = r.tokens[r.idx].kind var length = r.tokens[r.idx].length.int literal = substr(r.buf, r.pos, r.pos + length - 1) inc(r.pos, length) inc(r.idx) - else: + else: kind = tkEof diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 718a87232..4309661f3 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -41,48 +41,55 @@ 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 z: TCandidate + 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 - if sym == nil: return - initCandidate(c, best, sym, initialBinding, symScope) - initCandidate(c, alt, sym, initialBinding, symScope) + var z: TCandidate + 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 - - #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) + 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 == "cmp" and (n.info ?? "rstgen.nim") and n.info.line == 516: + # 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, diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 89469ae50..d236687c3 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: @@ -1899,12 +1907,11 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = var ids = initIntSet() for i in 1.. <n.len: let it = n.sons[i] - if it.kind != nkExprColonExpr or it.sons[0].kind notin {nkSym, nkIdent}: + if it.kind != nkExprColonExpr: localError(n.info, errNamedExprExpected) break - var id: PIdent - if it.sons[0].kind == nkIdent: id = it.sons[0].ident - else: id = it.sons[0].sym.name + let id = considerQuotedIdent(it.sons[0]) + if containsOrIncl(ids, id.id): localError(it.info, errFieldInitTwice, id.s) var e = semExprWithType(c, it.sons[1], flags*{efAllowDestructor}) @@ -1991,10 +1998,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 +2018,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 +2062,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 +2091,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 +2130,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 +2156,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 +2164,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 +2205,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/semparallel.nim b/compiler/semparallel.nim index a914832de..6572a7f49 100644 --- a/compiler/semparallel.nim +++ b/compiler/semparallel.nim @@ -37,7 +37,7 @@ is valid, but spawn f(a[i]) spawn f(a[i]) inc i -is not! However, +is not! However, spawn f(a[i]) if guard: inc i spawn f(a[i]) @@ -460,7 +460,7 @@ proc liftParallel*(owner: PSym; n: PNode): PNode = # - detect used slices # - detect used arguments #echo "PAR ", renderTree(n) - + var a = initAnalysisCtx() let body = n.lastSon analyse(a, body) 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 3fbb6f8f3..19514263f 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -12,13 +12,13 @@ var enforceVoidContext = PType(kind: tyStmt) -proc semDiscard(c: PContext, n: PNode): PNode = +proc semDiscard(c: PContext, n: PNode): PNode = result = n checkSonsLen(n, 1) if n.sons[0].kind != nkEmpty: n.sons[0] = semExprWithType(c, n.sons[0]) if isEmptyType(n.sons[0].typ): localError(n.info, errInvalidDiscard) - + proc semBreakOrContinue(c: PContext, n: PNode): PNode = result = n checkSonsLen(n, 1) @@ -29,7 +29,7 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode = of nkIdent: s = lookUp(c, n.sons[0]) of nkSym: s = n.sons[0].sym else: illFormedAst(n) - if s.kind == skLabel and s.owner.id == c.p.owner.id: + if s.kind == skLabel and s.owner.id == c.p.owner.id: var x = newSymNode(s) x.info = n.info incl(s.flags, sfUsed) @@ -41,16 +41,16 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode = else: localError(n.info, errGenerated, "'continue' cannot have a label") elif (c.p.nestedLoopCounter <= 0) and (c.p.nestedBlockCounter <= 0): - localError(n.info, errInvalidControlFlowX, + localError(n.info, errInvalidControlFlowX, renderTree(n, {renderNoComments})) -proc semAsm(con: PContext, n: PNode): PNode = +proc semAsm(con: PContext, n: PNode): PNode = checkSonsLen(n, 2) var marker = pragmaAsm(con, n.sons[0]) if marker == '\0': marker = '`' # default marker result = semAsmOrEmit(con, n, marker) - -proc semWhile(c: PContext, n: PNode): PNode = + +proc semWhile(c: PContext, n: PNode): PNode = result = n checkSonsLen(n, 2) openScope(c) @@ -62,16 +62,16 @@ proc semWhile(c: PContext, n: PNode): PNode = if n.sons[1].typ == enforceVoidContext: result.typ = enforceVoidContext -proc toCover(t: PType): BiggestInt = +proc toCover(t: PType): BiggestInt = var t2 = skipTypes(t, abstractVarRange-{tyTypeDesc}) - if t2.kind == tyEnum and enumHasHoles(t2): + if t2.kind == tyEnum and enumHasHoles(t2): result = sonsLen(t2.n) else: result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc})) proc performProcvarCheck(c: PContext, n: PNode, s: PSym) = ## Checks that the given symbol is a proper procedure variable, meaning - ## that it + ## that it var smoduleId = getModule(s).id if sfProcvar notin s.flags and s.typ.callConv == ccDefault and smoduleId != c.module.id: @@ -98,11 +98,11 @@ proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} = localError(n.info, warnDestructor) # This still breaks too many things: when false: - if efDetermineType notin flags and n.typ.kind == tyTypeDesc and + if efDetermineType notin flags and n.typ.kind == tyTypeDesc and c.p.owner.kind notin {skTemplate, skMacro}: localError(n.info, errGenerated, "value expected, but got a type") -proc newDeref(n: PNode): PNode {.inline.} = +proc newDeref(n: PNode): PNode {.inline.} = result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0]) addSon(result, n) @@ -127,7 +127,7 @@ const proc implicitlyDiscardable(n: PNode): bool = var n = n while n.kind in skipForDiscardable: n = n.lastSon - result = isCallExpr(n) and n.sons[0].kind == nkSym and + result = isCallExpr(n) and n.sons[0].kind == nkSym and sfDiscardable in n.sons[0].sym.flags proc fixNilType(n: PNode) = @@ -160,11 +160,11 @@ proc discardCheck(c: PContext, result: PNode) = while n.kind in skipForDiscardable: n = n.lastSon localError(n.info, errDiscardValueX, result.typ.typeToString) -proc semIf(c: PContext, n: PNode): PNode = +proc semIf(c: PContext, n: PNode): PNode = result = n var typ = commonTypeBegin var hasElse = false - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): var it = n.sons[i] if it.len == 2: when newScopeForIf: openScope(c) @@ -208,10 +208,10 @@ proc semCase(c: PContext, n: PNode): PNode = else: localError(n.info, errSelectorMustBeOfCertainTypes) return - for i in countup(1, sonsLen(n) - 1): + for i in countup(1, sonsLen(n) - 1): var x = n.sons[i] case x.kind - of nkOfBranch: + of nkOfBranch: checkMinSonsLen(x, 2) semCaseBranch(c, n, x, i, covered) var last = sonsLen(x)-1 @@ -304,9 +304,9 @@ proc semTry(c: PContext, n: PNode): PNode = it.sons[j] = fitNode(c, typ, it.sons[j]) result.typ = typ -proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode = +proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode = result = fitNode(c, typ, n) - if result.kind in {nkHiddenStdConv, nkHiddenSubConv}: + if result.kind in {nkHiddenStdConv, nkHiddenSubConv}: changeType(result.sons[1], typ, check=true) result = result.sons[1] elif not sameType(result.typ, typ): @@ -325,7 +325,7 @@ proc identWithin(n: PNode, s: PIdent): bool = result = n.kind == nkSym and n.sym.name.id == s.id proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym = - if isTopLevel(c): + if isTopLevel(c): result = semIdentWithPragma(c, kind, n, {sfExported}) incl(result.flags, sfGlobal) else: @@ -340,14 +340,14 @@ proc checkNilable(v: PSym) = elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags: message(v.info, warnProveInit, v.name.s) -proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = +proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var b: PNode result = copyNode(n) var hasCompileTime = false - for i in countup(0, sonsLen(n)-1): + for i in countup(0, sonsLen(n)-1): var a = n.sons[i] if gCmd == cmdIdeTools: suggestStmt(c, a) - if a.kind == nkCommentStmt: continue + if a.kind == nkCommentStmt: continue if a.kind notin {nkIdentDefs, nkVarTuple, nkConstDef}: illFormedAst(a) checkMinSonsLen(a, 3) var length = sonsLen(a) @@ -369,7 +369,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = typ = def.typ else: # BUGFIX: ``fitNode`` is needed here! - # check type compatibility between def.typ and typ + # check type compatibility between def.typ and typ def = fitNode(c, typ, def) #changeType(def.skipConv, typ, check=true) else: @@ -381,15 +381,15 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = else: def = ast.emptyNode if symkind == skLet: localError(a.info, errLetNeedsInit) - + # this can only happen for errornous var statements: if typ == nil: continue typeAllowedCheck(a.info, typ, symkind) var tup = skipTypes(typ, {tyGenericInst}) - if a.kind == nkVarTuple: - if tup.kind != tyTuple: + if a.kind == nkVarTuple: + if tup.kind != tyTuple: localError(a.info, errXExpected, "tuple") - elif length-2 != sonsLen(tup): + elif length-2 != sonsLen(tup): localError(a.info, errWrongNumberOfVariables) else: b = newNodeI(nkVarTuple, a.info) @@ -397,9 +397,10 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator b.sons[length-1] = def addSon(result, b) - elif tup.kind == tyTuple and def.kind == nkPar and + elif tup.kind == tyTuple and def.kind == nkPar and a.kind == nkIdentDefs and a.len > 3: message(a.info, warnEachIdentIsTuple) + for j in countup(0, length-3): var v = semIdentDef(c, a.sons[j], symkind) if sfGenSym notin v.flags: addInterfaceDecl(c, v) @@ -409,6 +410,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = let shadowed = findShadowedVar(c, v) if shadowed != nil: shadowed.flags.incl(sfShadowed) + if shadowed.kind == skResult and sfGenSym notin v.flags: + message(a.info, warnResultShadowed) # a shadowed variable is an error unless it appears on the right # side of the '=': if warnShadowIdent in gNotes and not identWithin(def, v.name): @@ -435,12 +438,12 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if sfCompileTime in v.flags: hasCompileTime = true if hasCompileTime: vm.setupCompileTimeVar(c.module, result) -proc semConst(c: PContext, n: PNode): PNode = +proc semConst(c: PContext, n: PNode): PNode = result = copyNode(n) - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if gCmd == cmdIdeTools: suggestStmt(c, a) - if a.kind == nkCommentStmt: continue + if a.kind == nkCommentStmt: continue if (a.kind != nkConstDef): illFormedAst(a) checkSonsLen(a, 3) var v = semIdentDef(c, a.sons[0], skConst) @@ -495,7 +498,7 @@ proc semForVars(c: PContext, n: PNode): PNode = var iter = skipTypes(iterBase, {tyGenericInst}) # length == 3 means that there is one for loop variable # and thus no tuple unpacking: - if iter.kind != tyTuple or length == 3: + if iter.kind != tyTuple or length == 3: if length == 3: var v = symForVar(c, n.sons[0]) if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal) @@ -523,13 +526,13 @@ proc semForVars(c: PContext, n: PNode): PNode = proc implicitIterator(c: PContext, it: string, arg: PNode): PNode = result = newNodeI(nkCall, arg.info) result.add(newIdentNode(it.getIdent, arg.info)) - if arg.typ != nil and arg.typ.kind == tyVar: + if arg.typ != nil and arg.typ.kind == tyVar: result.add newDeref(arg) else: result.add arg result = semExprNoDeref(c, result, {efWantIterator}) -proc semFor(c: PContext, n: PNode): PNode = +proc semFor(c: PContext, n: PNode): PNode = result = n checkMinSonsLen(n, 3) var length = sonsLen(n) @@ -564,13 +567,13 @@ proc semFor(c: PContext, n: PNode): PNode = result.typ = enforceVoidContext closeScope(c) -proc semRaise(c: PContext, n: PNode): PNode = +proc semRaise(c: PContext, n: PNode): PNode = result = n checkSonsLen(n, 1) - if n.sons[0].kind != nkEmpty: + if n.sons[0].kind != nkEmpty: n.sons[0] = semExprWithType(c, n.sons[0]) var typ = n.sons[0].typ - if typ.kind != tyRef or typ.sons[0].kind != tyObject: + if typ.kind != tyRef or typ.sons[0].kind != tyObject: localError(n.info, errExprCannotBeRaised) proc addGenericParamListToScope(c: PContext, n: PNode) = @@ -580,13 +583,13 @@ proc addGenericParamListToScope(c: PContext, n: PNode) = if a.kind == nkSym: addDecl(c, a.sym) else: illFormedAst(a) -proc typeSectionLeftSidePass(c: PContext, n: PNode) = +proc typeSectionLeftSidePass(c: PContext, n: PNode) = # process the symbols on the left side for the whole type section, before # we even look at the type definitions on the right - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if gCmd == cmdIdeTools: suggestStmt(c, a) - if a.kind == nkCommentStmt: continue + if a.kind == nkCommentStmt: continue if a.kind != nkTypeDef: illFormedAst(a) checkSonsLen(a, 3) var s = semIdentDef(c, a.sons[0], skType) @@ -599,29 +602,29 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = a.sons[0] = newSymNode(s) proc typeSectionRightSidePass(c: PContext, n: PNode) = - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] - if a.kind == nkCommentStmt: continue + if a.kind == nkCommentStmt: continue if (a.kind != nkTypeDef): illFormedAst(a) checkSonsLen(a, 3) if (a.sons[0].kind != nkSym): illFormedAst(a) var s = a.sons[0].sym - if s.magic == mNone and a.sons[2].kind == nkEmpty: + if s.magic == mNone and a.sons[2].kind == nkEmpty: localError(a.info, errImplOfXexpected, s.name.s) if s.magic != mNone: processMagicType(c, s) - if a.sons[1].kind != nkEmpty: + if a.sons[1].kind != nkEmpty: # We have a generic type declaration here. In generic types, # symbol lookup needs to be done here. openScope(c) pushOwner(s) if s.magic == mNone: s.typ.kind = tyGenericBody # XXX for generic type aliases this is not correct! We need the - # underlying Id really: + # underlying Id really: # # type # TGObj[T] = object # TAlias[T] = TGObj[T] - # + # s.typ.n = semGenericParamList(c, a.sons[1], s.typ) a.sons[1] = s.typ.n s.typ.size = -1 # could not be computed properly @@ -639,13 +642,13 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = s.typ.sons[sonsLen(s.typ) - 1] = body popOwner() closeScope(c) - elif a.sons[2].kind != nkEmpty: + elif a.sons[2].kind != nkEmpty: # process the type's body: pushOwner(s) var t = semTypeNode(c, a.sons[2], s.typ) - if s.typ == nil: + if s.typ == nil: s.typ = t - elif t != s.typ: + elif t != s.typ: # this can happen for e.g. tcan_alias_specialised_generic: assignType(s.typ, t) #debug s.typ @@ -676,23 +679,23 @@ proc checkForMetaFields(n: PNode) = else: internalAssert false -proc typeSectionFinalPass(c: PContext, n: PNode) = - for i in countup(0, sonsLen(n) - 1): +proc typeSectionFinalPass(c: PContext, n: PNode) = + for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] - if a.kind == nkCommentStmt: continue + if a.kind == nkCommentStmt: continue if a.sons[0].kind != nkSym: illFormedAst(a) var s = a.sons[0].sym # compute the type's size and check for illegal recursions: - if a.sons[1].kind == nkEmpty: + if a.sons[1].kind == nkEmpty: if a.sons[2].kind in {nkSym, nkIdent, nkAccQuoted}: # type aliases are hard: #MessageOut('for type ' + typeToString(s.typ)); var t = semTypeNode(c, a.sons[2], nil) - if t.kind in {tyObject, tyEnum}: + if t.kind in {tyObject, tyEnum}: 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 @@ -720,21 +723,21 @@ proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) = if s.typ.sons[0] != nil and s.typ.sons[0].kind == tyStmt: localError(n.info, errGenerated, "invalid return type: 'stmt'") -proc addParams(c: PContext, n: PNode, kind: TSymKind) = - for i in countup(1, sonsLen(n)-1): +proc addParams(c: PContext, n: PNode, kind: TSymKind) = + for i in countup(1, sonsLen(n)-1): if n.sons[i].kind == nkSym: addParamOrResult(c, n.sons[i].sym, kind) else: illFormedAst(n) -proc semBorrow(c: PContext, n: PNode, s: PSym) = +proc semBorrow(c: PContext, n: PNode, s: PSym) = # search for the correct alias: var b = searchForBorrowProc(c, c.currentScope.parent, s) - if b != nil: + if b != nil: # store the alias: n.sons[bodyPos] = newSymNode(b) else: - localError(n.info, errNoSymbolToBorrowFromFound) - -proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind) = + localError(n.info, errNoSymbolToBorrowFromFound) + +proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind) = if t != nil: var s = newSym(skResult, getIdent"result", getCurrOwner(), info) s.typ = t @@ -742,7 +745,7 @@ proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind) = addParamOrResult(c, s, owner) c.p.resultSym = s -proc addResultNode(c: PContext, n: PNode) = +proc addResultNode(c: PContext, n: PNode) = if c.p.resultSym != nil: addSon(n, newSymNode(c.p.resultSym)) proc copyExcept(n: PNode, i: int): PNode = @@ -786,7 +789,7 @@ proc semProcAnnotation(c: PContext, prc: PNode; result = semStmt(c, x) # since a proc annotation can set pragmas, we process these here again. # This is required for SqueakNim-like export pragmas. - if result.kind in procDefs and result[namePos].kind == nkSym and + if result.kind in procDefs and result[namePos].kind == nkSym and result[pragmasPos].kind != nkEmpty: pragma(c, result[namePos].sym, result[pragmasPos], validPragmas) return @@ -808,7 +811,7 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = pushOwner(s) openScope(c) var gp: PNode - if n.sons[genericParamsPos].kind != nkEmpty: + if n.sons[genericParamsPos].kind != nkEmpty: n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos]) gp = n.sons[genericParamsPos] else: @@ -858,13 +861,13 @@ proc semDo(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = var n = n - + n = replaceTypesInBody(c, pt, n) result = n - + n.sons[genericParamsPos] = emptyNode n.sons[paramsPos] = n.typ.n - + openScope(c) var s = n.sons[namePos].sym pushOwner(s) @@ -877,7 +880,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = popProcCon(c) popOwner() closeScope(c) - + s.ast = result # alternative variant (not quite working): @@ -923,7 +926,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = else: break if t.kind in {tyObject, tyDistinct, tyEnum}: if t.deepCopy.isNil: t.deepCopy = s - else: + else: localError(n.info, errGenerated, "cannot bind another 'deepCopy' to: " & typeToString(t)) else: @@ -990,10 +993,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, pushOwner(s) openScope(c) var gp: PNode - if n.sons[genericParamsPos].kind != nkEmpty: + if n.sons[genericParamsPos].kind != nkEmpty: n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos]) gp = n.sons[genericParamsPos] - else: + else: gp = newNodeI(nkGenericParams, n.info) # process parameters: if n.sons[paramsPos].kind != nkEmpty: @@ -1010,7 +1013,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, n.sons[patternPos] = semPattern(c, n.sons[patternPos]) if s.kind in skIterators: s.typ.flags.incl(tfIterator) - + var proto = searchForProc(c, s.scope, s) if proto == nil: if s.kind == skClosureIterator: s.typ.callConv = ccClosure @@ -1028,14 +1031,14 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, else: implicitPragmas(c, s, n, validPragmas) else: - if n.sons[pragmasPos].kind != nkEmpty: + if n.sons[pragmasPos].kind != nkEmpty: localError(n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProc) - if sfForward notin proto.flags: + if sfForward notin proto.flags: wrongRedefinition(n.info, proto.name.s) excl(proto.flags, sfForward) closeScope(c) # close scope with wrong parameter symbols openScope(c) # open scope for old (correct) parameter symbols - if proto.ast.sons[genericParamsPos].kind != nkEmpty: + if proto.ast.sons[genericParamsPos].kind != nkEmpty: addGenericParamListToScope(c, proto.ast.sons[genericParamsPos]) addParams(c, proto.typ.n, proto.kind) proto.info = s.info # more accurate line information @@ -1085,7 +1088,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, n.sons[bodyPos] = ast.emptyNode else: if proto != nil: localError(n.info, errImplOfXexpected, proto.name.s) - if {sfImportc, sfBorrow} * s.flags == {} and s.magic == mNone: + if {sfImportc, sfBorrow} * s.flags == {} and s.magic == mNone: incl(s.flags, sfForward) elif sfBorrow in s.flags: semBorrow(c, n, s) sideEffectsCheck(c, s) @@ -1123,14 +1126,14 @@ proc semIterator(c: PContext, n: PNode): PNode = else: s.typ.callConv = ccInline when false: - if s.typ.callConv != ccInline: + if s.typ.callConv != ccInline: s.typ.callConv = ccClosure # and they always at least use the 'env' for the state field: incl(s.typ.flags, tfCapturesEnv) if n.sons[bodyPos].kind == nkEmpty and s.magic == mNone: localError(n.info, errImplOfXexpected, s.name.s) - -proc semProc(c: PContext, n: PNode): PNode = + +proc semProc(c: PContext, n: PNode): PNode = result = semProcAux(c, n, skProc, procPragmas) proc hasObjParam(s: PSym): bool = @@ -1143,10 +1146,10 @@ proc finishMethod(c: PContext, s: PSym) = if hasObjParam(s): methodDef(s, false) -proc semMethod(c: PContext, n: PNode): PNode = +proc semMethod(c: PContext, n: PNode): PNode = if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "method") result = semProcAux(c, n, skMethod, methodPragmas) - + var s = result.sons[namePos].sym if not isGenericRoutine(s) and result.sons[bodyPos].kind != nkEmpty: if hasObjParam(s): @@ -1154,7 +1157,7 @@ proc semMethod(c: PContext, n: PNode): PNode = else: localError(n.info, errXNeedsParamObjectType, "method") -proc semConverterDef(c: PContext, n: PNode): PNode = +proc semConverterDef(c: PContext, n: PNode): PNode = if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "converter") checkSonsLen(n, bodyPos + 1) result = semProcAux(c, n, skConverter, converterPragmas) @@ -1164,7 +1167,7 @@ proc semConverterDef(c: PContext, n: PNode): PNode = if sonsLen(t) != 2: localError(n.info, errXRequiresOneArgument, "converter") addConverter(c, s) -proc semMacroDef(c: PContext, n: PNode): PNode = +proc semMacroDef(c: PContext, n: PNode): PNode = checkSonsLen(n, bodyPos + 1) result = semProcAux(c, n, skMacro, macroPragmas) var s = result.sons[namePos].sym @@ -1172,23 +1175,23 @@ proc semMacroDef(c: PContext, n: PNode): PNode = if t.sons[0] == nil: localError(n.info, errXNeedsReturnType, "macro") if n.sons[bodyPos].kind == nkEmpty: localError(n.info, errImplOfXexpected, s.name.s) - + proc evalInclude(c: PContext, n: PNode): PNode = result = newNodeI(nkStmtList, n.info) addSon(result, n) - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): var f = checkModuleName(n.sons[i]) if f != InvalidFileIDX: - if containsOrIncl(c.includedFiles, f): + if containsOrIncl(c.includedFiles, f): localError(n.info, errRecursiveDependencyX, f.toFilename) else: addSon(result, semStmt(c, gIncludeFile(c.module, f))) excl(c.includedFiles, f) - + proc setLine(n: PNode, info: TLineInfo) = for i in 0 .. <safeLen(n): setLine(n.sons[i], info) n.info = info - + proc semPragmaBlock(c: PContext, n: PNode): PNode = let pragmaList = n.sons[0] pragma(c, nil, pragmaList, exprPragmas) @@ -1197,7 +1200,7 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode = for i in 0 .. <pragmaList.len: case whichPragma(pragmaList.sons[i]) of wLine: setLine(result, pragmaList.sons[i].info) - of wLocks: + of wLocks: result = n result.typ = n.sons[1].typ else: discard @@ -1312,8 +1315,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = inner.addSon(semStmtList(c, rest, flags)) n.sons.setLen(i+1) return - of LastBlockStmts: - for j in countup(i + 1, length - 1): + of LastBlockStmts: + for j in countup(i + 1, length - 1): case n.sons[j].kind of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: discard else: localError(n.sons[j].info, errStmtInvalidAfterReturn) @@ -1335,7 +1338,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = # "Last expression must be explicitly returned if it " & # "is discardable or discarded") -proc semStmt(c: PContext, n: PNode): PNode = +proc semStmt(c: PContext, n: PNode): PNode = # now: simply an alias: result = semExprNoType(c, n) 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..f1fd84326 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 @@ -157,17 +157,40 @@ proc sumGeneric(t: PType): int = result = ord(t.kind == tyGenericInvocation) for i in 0 .. <t.len: result += t.sons[i].sumGeneric break - of tyProc: - # proc matches proc better than 'stmt' to disambiguate 'spawn' - return 1 of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break + of tyBool, tyChar, tyEnum, tyObject, tyProc, tyPointer, + tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128, + tyUInt..tyUInt64: + return 1 else: return 0 +#var ggDebug: bool + proc complexDisambiguation(a, b: PType): int = - var x, y: int - for i in 1 .. <a.len: x += a.sons[i].sumGeneric - for i in 1 .. <b.len: y += b.sons[i].sumGeneric - result = x - y + # 'a' matches better if *every* argument matches better or equal than 'b'. + var winner = 0 + for i in 1 .. <min(a.len, b.len): + let x = a.sons[i].sumGeneric + let y = b.sons[i].sumGeneric + #if ggDebug: + # echo "came her ", typeToString(a.sons[i]), " ", typeToString(b.sons[i]) + if x != y: + if winner == 0: + if x > y: winner = 1 + else: winner = -1 + elif x > y: + if winner != 1: + # contradiction + return 0 + else: + if winner != -1: + return 0 + result = winner + when false: + var x, y: int + for i in 1 .. <a.len: x += a.sons[i].sumGeneric + for i in 1 .. <b.len: y += b.sons[i].sumGeneric + result = x - y when false: proc betterThan(a, b: PType): bool {.inline.} = a.sumGeneric > b.sumGeneric @@ -176,7 +199,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 +226,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 +248,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 +264,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 +278,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 +290,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 +309,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 +341,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 +355,18 @@ 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 + 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,32 +397,32 @@ 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: # no luck resolving the type, so the inference fails return isNone let reverseRel = typeRel(c, a, f) - if reverseRel == isGeneric: + if reverseRel >= isGeneric: result = isInferred - inc c.genericMatches + #inc c.genericMatches else: result = typeRel(c, f, a) if result <= isSubtype or inconsistentVarTypes(f, a): result = isNone - - if result == isEqual: - inc c.exactMatches - + + #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 +431,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]) @@ -433,6 +457,7 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = return isNone when useEffectSystem: if not compatibleEffects(f, a): return isNone + of tyNil: result = f.allowsNil of tyIter: @@ -487,14 +512,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 +545,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 +579,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 +594,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 +607,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, @@ -590,10 +615,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = return typeRel(c, f, lastSon(a)) template bindingRet(res) = - when res == isGeneric: - if doBind: - let bound = aOrig.skipTypes({tyRange}).skipIntLit - if doBind: put(c.bindings, f, bound) + if doBind: + let bound = aOrig.skipTypes({tyRange}).skipIntLit + if doBind: put(c.bindings, f, bound) return res template considerPreviousT(body: stmt) {.immediate.} = @@ -605,20 +629,21 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyOr: # seq[int|string] vs seq[number] # both int and string must match against number + # but ensure that '[T: A|A]' matches as good as '[T: A]' (bug #2219): + result = isGeneric for branch in a.sons: - if typeRel(c, f, branch, false) == isNone: - return isNone - - return isGeneric + let x = typeRel(c, f, branch, false) + if x == isNone: return isNone + if x < result: result = x of tyAnd: # seq[Sortable and Iterable] vs seq[Sortable] # only one match is enough for branch in a.sons: - if typeRel(c, f, branch, false) != isNone: - return isGeneric - - return isNone + let x = typeRel(c, f, branch, false) + if x != isNone: + return if x >= isGeneric: isGeneric else: x + result = isNone of tyNot: case f.kind @@ -626,9 +651,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 +752,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 +793,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: @@ -781,15 +806,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = inc(c.inheritancePenalty, depth) result = isSubtype of tyDistinct: - if (a.kind == tyDistinct) and sameDistinctTypes(f, a): result = isEqual + 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 +848,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 +873,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,9 +888,9 @@ 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 + #result = isGeneric # XXX See bug #2220. A[int] should match A[int] better than some generic X else: result = typeRel(c, lastSon(f), a) @@ -883,13 +908,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,29 +925,34 @@ 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: - if typeRel(c, branch, aOrig) < isSubtype: - return isNone - - bindingRet isGeneric + let x = typeRel(c, branch, aOrig) + if x < isSubtype: return isNone + # 'and' implies minimum matching result: + if x < result: result = x + bindingRet result of tyOr: considerPreviousT: + result = isNone for branch in f.sons: - if typeRel(c, branch, aOrig) >= isSubtype: - bindingRet isGeneric - - return isNone + let x = typeRel(c, branch, aOrig) + # 'or' implies maximum matching result: + if x > result: result = x + if result >= isSubtype: + bindingRet result + else: + result = isNone of tyNot: considerPreviousT: for branch in f.sons: if typeRel(c, branch, aOrig) != isNone: return isNone - + bindingRet isGeneric of tyAnything: @@ -961,7 +991,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 @@ -975,6 +1005,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = internalAssert a.sons != nil and a.sons.len > 0 c.typedescMatched = true result = typeRel(c, f.base, a.skipTypes({tyGenericParam, tyTypeDesc})) + if result > isGeneric: result = isGeneric else: result = isNone else: @@ -998,13 +1029,16 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = return isNone if doBind: put(c.bindings, f, concrete) + elif result > isGeneric: + result = isGeneric elif a.kind == tyEmpty: result = isGeneric elif x.kind == tyGenericParam: result = isGeneric else: result = typeRel(c, x, a) # check if it fits - + if result > isGeneric: result = isGeneric + of tyStatic: let prev = PType(idTableGet(c.bindings, f)) if prev == nil: @@ -1034,12 +1068,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 +1083,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 +1093,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyStmt: result = isGeneric - + of tyProxy: result = isEqual @@ -1078,11 +1112,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 +1129,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 +1144,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 +1155,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 +1172,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 +1215,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 +1239,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 @@ -1220,8 +1254,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, of isConvertible, isIntConv: inc(m.convMatches) of isSubtype, isSubrange: inc(m.subtypeMatches) of isGeneric, isInferred: inc(m.genericMatches) - of isInferredConvertible: inc(m.genericMatches); inc(m.convMatches) of isFromIntLit: inc(m.intConvMatches, 256) + of isInferredConvertible: + inc(m.convMatches) of isEqual: inc(m.exactMatches) of isNone: discard @@ -1232,7 +1267,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,21 +1279,22 @@ 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) + if f.kind == tyVar: + result = arg + else: + result = implicitConv(nkHiddenStdConv, f, arg, m, c) of isInferred, isInferredConvertible: - inc(m.genericMatches) if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds: result = c.semInferredLambda(c, m.bindings, arg) else: @@ -1267,44 +1303,36 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, if r == isInferredConvertible: inc(m.convMatches) result = implicitConv(nkHiddenStdConv, f, result, m, c) + else: + inc(m.genericMatches) 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 +1353,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 +1367,37 @@ 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) + z.callee = arg.sons[i].typ + z.calleeSym = arg.sons[i].sym + #if arg.sons[i].sym.name.s == "cmp": + # ggDebug = true + # echo "CALLLEEEEEEEE ", typeToString(z.callee) var r = typeRel(z, f, arg.sons[i].typ) - if r != isNone: + #if arg.sons[i].sym.name.s == "cmp": # and arg.info.line == 606: + # echo "M ", r, " ", arg.info, " ", typeToString(arg.sons[i].sym.typ) + # debug arg.sons[i].sym + # writeMatches(z) + 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 +1410,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 +1454,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 +1484,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 +1507,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 +1544,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 +1552,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 +1565,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 +1640,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 +1661,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) @@ -1655,7 +1692,7 @@ tests: setup: var c: TCandidate - InitCandidate(nil, c, nil) + initCandidate(nil, c, nil) template yes(x, y) = test astToStr(x) & " is " & astToStr(y): @@ -1664,7 +1701,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 +1709,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 +1727,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 +1751,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..325ce9d5e 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) @@ -152,13 +152,14 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode = defs[0] = newSymNode(newVar).PTransNode defs[1] = it.sons[1].PTransNode defs[2] = transform(c, it.sons[2]) + newVar.ast = defs[2].PNode 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 +189,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 +225,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 +240,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 +274,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 +302,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 +341,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 +369,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 +379,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 and diff != high(int): 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 and diff != high(int): 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 +467,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 +497,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 +528,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 +565,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 +574,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 +641,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 +674,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 +703,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 +714,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 +731,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 +745,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 +765,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 +775,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 f0a0135e8..3b5c8e7f3 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -814,7 +814,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = leValueConv(regs[ra].regToNode, regs[rc].regToNode)): stackTrace(c, tos, pc, errGenerated, msgKindToString(errIllegalConvFromXtoY) % [ - "unknown type" , "unknown type"]) + $regs[ra].regToNode, "[" & $regs[rb].regToNode & ".." & $regs[rc].regToNode & "]"]) of opcIndCall, opcIndCallAsgn: # dest = call regStart, n; where regStart = fn, arg1, ... let rb = instr.regB diff --git a/config/nim.cfg b/config/nim.cfg index 32709137d..cb3f897d4 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -108,7 +108,7 @@ path="$lib/pure/unidecode" gcc.options.always = "-w" gcc.cpp.options.always = "-w -fpermissive" @else: - gcc.options.always = "-w" + gcc.options.always = "-w" gcc.cpp.options.always = "-w -fpermissive" @end @@ -151,7 +151,7 @@ clang.options.size = "-Os" vcc.options.linker = "/DEBUG /Zi /Fd\"$projectName.pdb\" /F33554432" # set the stack size to 8 MB vcc.options.debug = "/Zi /Fd\"$projectName.pdb\"" vcc.options.always = "/nologo" -vcc.options.speed = "/Ox /arch:SSE2" +vcc.options.speed = "/O2 /arch:SSE2" vcc.options.size = "/O1" # Configuration for the Digital Mars C/C++ compiler: diff --git a/doc/advopt.txt b/doc/advopt.txt index d4b1b7e57..195122cc7 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -12,19 +12,6 @@ Advanced commands: module dependency graph //dump dump all defined conditionals and search paths //check checks the project for syntax and semantic - //idetools compiler support for IDEs: possible options: - --track:FILE,LINE,COL track a file/cursor position - --trackDirty:DIRTY_FILE,ORIG_FILE,LINE,COL - track a file, currently not saved to disk - --suggest suggest all possible symbols at position - --def list all possible definitions at position - --context list possible invocation context - --usages list all usages of the symbol at position - --eval evaluates an expression - //serve start the compiler as a service mode (CAAS) - --server.type:TYPE either stdin or tcp - --server.port:PORT port for tcp mode, by default 6000 - --server.address:HOST binds to that address, by default "" Advanced options: -o, --out:FILE set the output filename @@ -81,7 +68,7 @@ Advanced options: --dynlibOverride:SYMBOL marks SYMBOL so that dynlib:SYMBOL has no effect and can be statically linked instead; symbol matching is fuzzy so - that --dynlibOverride:lua matches + that --dynlibOverride:lua matches dynlib: "liblua.so.3" --listCmd list the commands used to execute external programs --parallelBuild:0|1|... perform a parallel build diff --git a/doc/basicopt.txt b/doc/basicopt.txt index 7d08f1159..6a905bd53 100644 --- a/doc/basicopt.txt +++ b/doc/basicopt.txt @@ -29,6 +29,7 @@ Options: --infChecks:on|off turn Inf checks on|off --deadCodeElim:on|off whole program dead code elimination on|off --opt:none|speed|size optimize not at all or for speed|size + Note: use -d:release for a release build! --debugger:native|endb use native debugger (gdb) | ENDB (experimental) --app:console|gui|lib|staticlib generate a console app|GUI app|DLL|static library 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/doc/manual/procs.txt b/doc/manual/procs.txt index f48dfc6b9..9b04bf518 100644 --- a/doc/manual/procs.txt +++ b/doc/manual/procs.txt @@ -351,7 +351,7 @@ dispatch. .. code-block:: nim type - Expression = object ## abstract base class for an expression + Expression = object of RootObj ## abstract base class for an expression Literal = object of Expression x: int PlusExpr = object of Expression @@ -387,7 +387,7 @@ dispatching: .. code-block:: nim type - Thing = object + Thing = object of RootObj Unit = object of Thing x: int 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/osinfo_posix.nim b/lib/impure/osinfo_posix.nim index 0ed4289c4..0362fca12 100644 --- a/lib/impure/osinfo_posix.nim +++ b/lib/impure/osinfo_posix.nim @@ -1,77 +1,10 @@ -import posix, strutils, os - -when false: - type - Tstatfs {.importc: "struct statfs64", - header: "<sys/statfs.h>", final, pure.} = object - f_type: int - f_bsize: int - f_blocks: int - f_bfree: int - f_bavail: int - f_files: int - f_ffree: int - f_fsid: int - f_namelen: int - - proc statfs(path: string, buf: var Tstatfs): int {. - importc, header: "<sys/vfs.h>".} - - -proc getSystemVersion*(): string = - result = "" - - var unix_info: TUtsname - - if uname(unix_info) != 0: - os.raiseOSError(osLastError()) - - if $unix_info.sysname == "Linux": - # Linux - result.add("Linux ") - - result.add($unix_info.release & " ") - result.add($unix_info.machine) - elif $unix_info.sysname == "Darwin": - # Darwin - result.add("Mac OS X ") - if "14" in $unix_info.release: - result.add("v10.10 Yosemite") - elif "13" in $unix_info.release: - result.add("v10.9 Mavericks") - elif "12" in $unix_info.release: - result.add("v10.8 Mountian Lion") - elif "11" in $unix_info.release: - result.add("v10.7 Lion") - elif "10" in $unix_info.release: - result.add("v10.6 Snow Leopard") - elif "9" in $unix_info.release: - result.add("v10.5 Leopard") - elif "8" in $unix_info.release: - result.add("v10.4 Tiger") - elif "7" in $unix_info.release: - result.add("v10.3 Panther") - elif "6" in $unix_info.release: - result.add("v10.2 Jaguar") - elif "1.4" in $unix_info.release: - result.add("v10.1 Puma") - elif "1.3" in $unix_info.release: - result.add("v10.0 Cheetah") - elif "0" in $unix_info.release: - result.add("Server 1.0 Hera") - else: - result.add($unix_info.sysname & " " & $unix_info.release) - - -when false: - var unix_info: TUtsname - echo(uname(unix_info)) - echo(unix_info.sysname) - echo("8" in $unix_info.release) - - echo(getSystemVersion()) - - var stfs: TStatfs - echo(statfs("sysinfo_posix.nim", stfs)) - echo(stfs.f_files) - +# +# +# Nim's Runtime Library +# (c) Copyright 2015 Dominik Picheta +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +{.error: "This module has been moved to the 'osinfo' nimble package.".} diff --git a/lib/impure/osinfo_win.nim b/lib/impure/osinfo_win.nim index 94a27eb03..0362fca12 100644 --- a/lib/impure/osinfo_win.nim +++ b/lib/impure/osinfo_win.nim @@ -1,411 +1,10 @@ -# XXX clean up this mess! - -import winlean - -const - INVALID_HANDLE_VALUE = int(- 1) # GetStockObject - -type - TMEMORYSTATUSEX {.final, pure.} = object - dwLength: int32 - dwMemoryLoad: int32 - ullTotalPhys: int64 - ullAvailPhys: int64 - ullTotalPageFile: int64 - ullAvailPageFile: int64 - ullTotalVirtual: int64 - ullAvailVirtual: int64 - ullAvailExtendedVirtual: int64 - - SYSTEM_INFO* {.final, pure.} = object - wProcessorArchitecture*: int16 - wReserved*: int16 - dwPageSize*: int32 - lpMinimumApplicationAddress*: pointer - lpMaximumApplicationAddress*: pointer - dwActiveProcessorMask*: int32 - dwNumberOfProcessors*: int32 - dwProcessorType*: int32 - dwAllocationGranularity*: int32 - wProcessorLevel*: int16 - wProcessorRevision*: int16 - - LPSYSTEM_INFO* = ptr SYSTEM_INFO - TSYSTEMINFO* = SYSTEM_INFO - - TMemoryInfo* = object - MemoryLoad*: int ## occupied memory, in percent - TotalPhysMem*: int64 ## Total Physical memory, in bytes - AvailablePhysMem*: int64 ## Available physical memory, in bytes - TotalPageFile*: int64 ## The current committed memory limit - ## for the system or the current process, whichever is smaller, in bytes. - AvailablePageFile*: int64 ## The maximum amount of memory the current process can commit, in bytes. - TotalVirtualMem*: int64 ## Total virtual memory, in bytes - AvailableVirtualMem*: int64 ## Available virtual memory, in bytes - - TOSVERSIONINFOEX {.final, pure.} = object - dwOSVersionInfoSize: int32 - dwMajorVersion: int32 - dwMinorVersion: int32 - dwBuildNumber: int32 - dwPlatformId: int32 - szCSDVersion: array[0..127, char] - wServicePackMajor: int16 - wServicePackMinor: int16 - wSuiteMask: int16 - wProductType: int8 - wReserved: char - - TVersionInfo* = object - majorVersion*: int - minorVersion*: int - buildNumber*: int - platformID*: int - SPVersion*: string ## Full Service pack version string - SPMajor*: int ## Major service pack version - SPMinor*: int ## Minor service pack version - SuiteMask*: int - ProductType*: int - - TPartitionInfo* = tuple[FreeSpace, TotalSpace: Tfiletime] - -const - # SuiteMask - VersionInfo.SuiteMask - VER_SUITE_BACKOFFICE* = 0x00000004 - VER_SUITE_BLADE* = 0x00000400 - VER_SUITE_COMPUTE_SERVER* = 0x00004000 - VER_SUITE_DATACENTER* = 0x00000080 - VER_SUITE_ENTERPRISE* = 0x00000002 - VER_SUITE_EMBEDDEDNT* = 0x00000040 - VER_SUITE_PERSONAL* = 0x00000200 - VER_SUITE_SINGLEUSERTS* = 0x00000100 - VER_SUITE_SMALLBUSINESS* = 0x00000001 - VER_SUITE_SMALLBUSINESS_RESTRICTED* = 0x00000020 - VER_SUITE_STORAGE_SERVER* = 0x00002000 - VER_SUITE_TERMINAL* = 0x00000010 - VER_SUITE_WH_SERVER* = 0x00008000 - - # ProductType - VersionInfo.ProductType - VER_NT_DOMAIN_CONTROLLER* = 0x0000002 - VER_NT_SERVER* = 0x0000003 - VER_NT_WORKSTATION* = 0x0000001 - - VER_PLATFORM_WIN32_NT* = 2 - - # Product Info - getProductInfo() - (Remove unused ones ?) - PRODUCT_BUSINESS* = 0x00000006 - PRODUCT_BUSINESS_N* = 0x00000010 - PRODUCT_CLUSTER_SERVER* = 0x00000012 - PRODUCT_DATACENTER_SERVER* = 0x00000008 - PRODUCT_DATACENTER_SERVER_CORE* = 0x0000000C - PRODUCT_DATACENTER_SERVER_CORE_V* = 0x00000027 - PRODUCT_DATACENTER_SERVER_V* = 0x00000025 - PRODUCT_ENTERPRISE* = 0x00000004 - PRODUCT_ENTERPRISE_E* = 0x00000046 - PRODUCT_ENTERPRISE_N* = 0x0000001B - PRODUCT_ENTERPRISE_SERVER* = 0x0000000A - PRODUCT_ENTERPRISE_SERVER_CORE* = 0x0000000E - PRODUCT_ENTERPRISE_SERVER_CORE_V* = 0x00000029 - PRODUCT_ENTERPRISE_SERVER_IA64* = 0x0000000F - PRODUCT_ENTERPRISE_SERVER_V* = 0x00000026 - PRODUCT_HOME_BASIC* = 0x00000002 - PRODUCT_HOME_BASIC_E* = 0x00000043 - PRODUCT_HOME_BASIC_N* = 0x00000005 - PRODUCT_HOME_PREMIUM* = 0x00000003 - PRODUCT_HOME_PREMIUM_E* = 0x00000044 - PRODUCT_HOME_PREMIUM_N* = 0x0000001A - PRODUCT_HYPERV* = 0x0000002A - PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT* = 0x0000001E - PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING* = 0x00000020 - PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY* = 0x0000001F - PRODUCT_PROFESSIONAL* = 0x00000030 - PRODUCT_PROFESSIONAL_E* = 0x00000045 - PRODUCT_PROFESSIONAL_N* = 0x00000031 - PRODUCT_SERVER_FOR_SMALLBUSINESS* = 0x00000018 - PRODUCT_SERVER_FOR_SMALLBUSINESS_V* = 0x00000023 - PRODUCT_SERVER_FOUNDATION* = 0x00000021 - PRODUCT_SMALLBUSINESS_SERVER* = 0x00000009 - PRODUCT_STANDARD_SERVER* = 0x00000007 - PRODUCT_STANDARD_SERVER_CORE * = 0x0000000D - PRODUCT_STANDARD_SERVER_CORE_V* = 0x00000028 - PRODUCT_STANDARD_SERVER_V* = 0x00000024 - PRODUCT_STARTER* = 0x0000000B - PRODUCT_STARTER_E* = 0x00000042 - PRODUCT_STARTER_N* = 0x0000002F - PRODUCT_STORAGE_ENTERPRISE_SERVER* = 0x00000017 - PRODUCT_STORAGE_EXPRESS_SERVER* = 0x00000014 - PRODUCT_STORAGE_STANDARD_SERVER* = 0x00000015 - PRODUCT_STORAGE_WORKGROUP_SERVER* = 0x00000016 - PRODUCT_UNDEFINED* = 0x00000000 - PRODUCT_ULTIMATE* = 0x00000001 - PRODUCT_ULTIMATE_E* = 0x00000047 - PRODUCT_ULTIMATE_N* = 0x0000001C - PRODUCT_WEB_SERVER* = 0x00000011 - PRODUCT_WEB_SERVER_CORE* = 0x0000001D - - PROCESSOR_ARCHITECTURE_AMD64* = 9 ## x64 (AMD or Intel) - PROCESSOR_ARCHITECTURE_IA64* = 6 ## Intel Itanium Processor Family (IPF) - PROCESSOR_ARCHITECTURE_INTEL* = 0 ## x86 - PROCESSOR_ARCHITECTURE_UNKNOWN* = 0xffff ## Unknown architecture. - - # GetSystemMetrics - SM_SERVERR2 = 89 - -proc globalMemoryStatusEx*(lpBuffer: var TMEMORYSTATUSEX){.stdcall, dynlib: "kernel32", - importc: "GlobalMemoryStatusEx".} - -proc getMemoryInfo*(): TMemoryInfo = - ## Retrieves memory info - var statex: TMEMORYSTATUSEX - statex.dwLength = sizeof(statex).int32 - - globalMemoryStatusEx(statex) - result.MemoryLoad = statex.dwMemoryLoad - result.TotalPhysMem = statex.ullTotalPhys - result.AvailablePhysMem = statex.ullAvailPhys - result.TotalPageFile = statex.ullTotalPageFile - result.AvailablePageFile = statex.ullAvailPageFile - result.TotalVirtualMem = statex.ullTotalVirtual - result.AvailableVirtualMem = statex.ullAvailExtendedVirtual - -proc getVersionEx*(lpVersionInformation: var TOSVERSIONINFOEX): WINBOOL{.stdcall, - dynlib: "kernel32", importc: "GetVersionExA".} - -proc getProcAddress*(hModule: int, lpProcName: cstring): pointer{.stdcall, - dynlib: "kernel32", importc: "GetProcAddress".} - -proc getModuleHandleA*(lpModuleName: cstring): int{.stdcall, - dynlib: "kernel32", importc: "GetModuleHandleA".} - -proc getVersionInfo*(): TVersionInfo = - ## Retrieves operating system info - var osvi: TOSVERSIONINFOEX - osvi.dwOSVersionInfoSize = sizeof(osvi).int32 - discard getVersionEx(osvi) - result.majorVersion = osvi.dwMajorVersion - result.minorVersion = osvi.dwMinorVersion - result.buildNumber = osvi.dwBuildNumber - result.platformID = osvi.dwPlatformId - result.SPVersion = $osvi.szCSDVersion - result.SPMajor = osvi.wServicePackMajor - result.SPMinor = osvi.wServicePackMinor - result.SuiteMask = osvi.wSuiteMask - result.ProductType = osvi.wProductType - -proc getProductInfo*(majorVersion, minorVersion, SPMajorVersion, - SPMinorVersion: int): int = - ## Retrieves Windows' ProductInfo, this function only works in Vista and 7 - var pGPI = cast[proc (dwOSMajorVersion, dwOSMinorVersion, - dwSpMajorVersion, dwSpMinorVersion: int32, outValue: Pint32){.stdcall.}](getProcAddress( - getModuleHandleA("kernel32.dll"), "GetProductInfo")) - - if pGPI != nil: - var dwType: int32 - pGPI(int32(majorVersion), int32(minorVersion), int32(SPMajorVersion), int32(SPMinorVersion), addr(dwType)) - result = int(dwType) - else: - return PRODUCT_UNDEFINED - -proc getSystemInfo*(lpSystemInfo: LPSYSTEM_INFO){.stdcall, dynlib: "kernel32", - importc: "GetSystemInfo".} - -proc getSystemInfo*(): TSYSTEM_INFO = - ## Returns the SystemInfo - - # Use GetNativeSystemInfo if it's available - var pGNSI = cast[proc (lpSystemInfo: LPSYSTEM_INFO){.stdcall.}](getProcAddress( - getModuleHandleA("kernel32.dll"), "GetNativeSystemInfo")) - - var systemi: TSYSTEM_INFO - if pGNSI != nil: - pGNSI(addr(systemi)) - else: - getSystemInfo(addr(systemi)) - - return systemi - -proc getSystemMetrics*(nIndex: int32): int32{.stdcall, dynlib: "user32", - importc: "GetSystemMetrics".} - -proc `$`*(osvi: TVersionInfo): string = - ## Turns a VersionInfo object, into a string - - if osvi.platformID == VER_PLATFORM_WIN32_NT and osvi.majorVersion > 4: - result = "Microsoft " - - var si = getSystemInfo() - # Test for the specific product - if osvi.majorVersion == 6: - if osvi.minorVersion == 0: - if osvi.ProductType == VER_NT_WORKSTATION: - result.add("Windows Vista ") - else: result.add("Windows Server 2008 ") - elif osvi.minorVersion == 1: - if osvi.ProductType == VER_NT_WORKSTATION: - result.add("Windows 7 ") - else: result.add("Windows Server 2008 R2 ") - elif osvi.minorVersion == 2: - if osvi.ProductType == VER_NT_WORKSTATION: - result.add("Windows 8 ") - else: result.add("Windows Server 2012 ") - elif osvi.minorVersion == 3: - if osvi.ProductType == VER_NT_WORKSTATION: - result.add("Windows 8.1 ") - else: result.add("Windows Server 2012 R2 ") - - var dwType = getProductInfo(osvi.majorVersion, osvi.minorVersion, 0, 0) - case dwType - of PRODUCT_ULTIMATE: - result.add("Ultimate Edition") - of PRODUCT_PROFESSIONAL: - result.add("Professional") - of PRODUCT_HOME_PREMIUM: - result.add("Home Premium Edition") - of PRODUCT_HOME_BASIC: - result.add("Home Basic Edition") - of PRODUCT_ENTERPRISE: - result.add("Enterprise Edition") - of PRODUCT_BUSINESS: - result.add("Business Edition") - of PRODUCT_STARTER: - result.add("Starter Edition") - of PRODUCT_CLUSTER_SERVER: - result.add("Cluster Server Edition") - of PRODUCT_DATACENTER_SERVER: - result.add("Datacenter Edition") - of PRODUCT_DATACENTER_SERVER_CORE: - result.add("Datacenter Edition (core installation)") - of PRODUCT_ENTERPRISE_SERVER: - result.add("Enterprise Edition") - of PRODUCT_ENTERPRISE_SERVER_CORE: - result.add("Enterprise Edition (core installation)") - of PRODUCT_ENTERPRISE_SERVER_IA64: - result.add("Enterprise Edition for Itanium-based Systems") - of PRODUCT_SMALLBUSINESS_SERVER: - result.add("Small Business Server") - of PRODUCT_STANDARD_SERVER: - result.add("Standard Edition") - of PRODUCT_STANDARD_SERVER_CORE: - result.add("Standard Edition (core installation)") - of PRODUCT_WEB_SERVER: - result.add("Web Server Edition") - else: - discard - # End of Windows 6.* - - if osvi.majorVersion == 5 and osvi.minorVersion == 2: - if getSystemMetrics(SM_SERVERR2) != 0: - result.add("Windows Server 2003 R2, ") - elif (osvi.SuiteMask and VER_SUITE_PERSONAL) != 0: # Not sure if this will work - result.add("Windows Storage Server 2003") - elif (osvi.SuiteMask and VER_SUITE_WH_SERVER) != 0: - result.add("Windows Home Server") - elif osvi.ProductType == VER_NT_WORKSTATION and - si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64: - result.add("Windows XP Professional x64 Edition") - else: - result.add("Windows Server 2003, ") - - # Test for the specific product - if osvi.ProductType != VER_NT_WORKSTATION: - if ze(si.wProcessorArchitecture) == PROCESSOR_ARCHITECTURE_IA64: - if (osvi.SuiteMask and VER_SUITE_DATACENTER) != 0: - result.add("Datacenter Edition for Itanium-based Systems") - elif (osvi.SuiteMask and VER_SUITE_ENTERPRISE) != 0: - result.add("Enterprise Edition for Itanium-based Systems") - elif ze(si.wProcessorArchitecture) == PROCESSOR_ARCHITECTURE_AMD64: - if (osvi.SuiteMask and VER_SUITE_DATACENTER) != 0: - result.add("Datacenter x64 Edition") - elif (osvi.SuiteMask and VER_SUITE_ENTERPRISE) != 0: - result.add("Enterprise x64 Edition") - else: - result.add("Standard x64 Edition") - else: - if (osvi.SuiteMask and VER_SUITE_COMPUTE_SERVER) != 0: - result.add("Compute Cluster Edition") - elif (osvi.SuiteMask and VER_SUITE_DATACENTER) != 0: - result.add("Datacenter Edition") - elif (osvi.SuiteMask and VER_SUITE_ENTERPRISE) != 0: - result.add("Enterprise Edition") - elif (osvi.SuiteMask and VER_SUITE_BLADE) != 0: - result.add("Web Edition") - else: - result.add("Standard Edition") - # End of 5.2 - - if osvi.majorVersion == 5 and osvi.minorVersion == 1: - result.add("Windows XP ") - if (osvi.SuiteMask and VER_SUITE_PERSONAL) != 0: - result.add("Home Edition") - else: - result.add("Professional") - # End of 5.1 - - if osvi.majorVersion == 5 and osvi.minorVersion == 0: - result.add("Windows 2000 ") - if osvi.ProductType == VER_NT_WORKSTATION: - result.add("Professional") - else: - if (osvi.SuiteMask and VER_SUITE_DATACENTER) != 0: - result.add("Datacenter Server") - elif (osvi.SuiteMask and VER_SUITE_ENTERPRISE) != 0: - result.add("Advanced Server") - else: - result.add("Server") - # End of 5.0 - - # Include service pack (if any) and build number. - if len(osvi.SPVersion) > 0: - result.add(" ") - result.add(osvi.SPVersion) - - result.add(" (build " & $osvi.buildNumber & ")") - - if osvi.majorVersion >= 6: - if ze(si.wProcessorArchitecture) == PROCESSOR_ARCHITECTURE_AMD64: - result.add(", 64-bit") - elif ze(si.wProcessorArchitecture) == PROCESSOR_ARCHITECTURE_INTEL: - result.add(", 32-bit") - - else: - # Windows 98 etc... - result = "Unknown version of windows[Kernel version <= 4]" - - -proc getFileSize*(file: string): BiggestInt = - var fileData: TWIN32_FIND_DATA - - when useWinUnicode: - var aa = newWideCString(file) - var hFile = findFirstFileW(aa, fileData) - else: - var hFile = findFirstFileA(file, fileData) - - if hFile == INVALID_HANDLE_VALUE: - raise newException(IOError, $getLastError()) - - return fileData.nFileSizeLow - -proc getDiskFreeSpaceEx*(lpDirectoryName: cstring, lpFreeBytesAvailableToCaller, - lpTotalNumberOfBytes, - lpTotalNumberOfFreeBytes: var TFiletime): WINBOOL{. - stdcall, dynlib: "kernel32", importc: "GetDiskFreeSpaceExA".} - -proc getPartitionInfo*(partition: string): TPartitionInfo = - ## Retrieves partition info, for example ``partition`` may be ``"C:\"`` - var freeBytes, totalBytes, totalFreeBytes: TFiletime - discard getDiskFreeSpaceEx(r"C:\", freeBytes, totalBytes, - totalFreeBytes) - return (freeBytes, totalBytes) - -when isMainModule: - var r = getMemoryInfo() - echo("Memory load: ", r.MemoryLoad, "%") - - var osvi = getVersionInfo() - - echo($osvi) - - echo(getFileSize(r"lib\impure\osinfo_win.nim") div 1024, " KB") - - echo(rdFileTime(getPartitionInfo(r"C:\")[0])) +# +# +# Nim's Runtime Library +# (c) Copyright 2015 Dominik Picheta +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +{.error: "This module has been moved to the 'osinfo' nimble package.".} diff --git a/lib/impure/rdstdin.nim b/lib/impure/rdstdin.nim index aaf2ed1ca..55f8c5d32 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/nimbase.h b/lib/nimbase.h index a1d4d1e27..e9dad0bb7 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -406,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/pure/algorithm.nim b/lib/pure/algorithm.nim index 20976e788..08d224dfd 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -11,15 +11,15 @@ type SortOrder* = enum ## sort order - Descending, Ascending + Descending, Ascending {.deprecated: [TSortOrder: SortOrder].} -proc `*`*(x: int, order: SortOrder): int {.inline.} = +proc `*`*(x: int, order: SortOrder): int {.inline.} = ## flips `x` if ``order == Descending``; ## if ``order == Ascending`` then `x` is returned. - ## `x` is supposed to be the result of a comparator, ie ``< 0`` for + ## `x` is supposed to be the result of a comparator, ie ``< 0`` for ## *less than*, ``== 0`` for *equal*, ``> 0`` for *greater than*. var y = order.ord - 1 result = (x xor y) - y @@ -73,14 +73,15 @@ const onlySafeCode = true proc lowerBound*[T](a: openArray[T], key: T, cmp: proc(x,y: T): int {.closure.}): int = - ## same as binarySearch except that if key is not in `a` then this + ## same as binarySearch except that if key is not in `a` then this ## returns the location where `key` would be if it were. In other - ## words if you have a sorted sequence and you call insert(thing, elm, lowerBound(thing, elm)) - ## the sequence will still be sorted + ## words if you have a sorted sequence and you call + ## insert(thing, elm, lowerBound(thing, elm)) + ## the sequence will still be sorted. + ## + ## `cmp` is the comparator function to use, the expected return values are + ## the same as that of system.cmp. ## - ## `cmp` is the comparator function to use, the expected return values are the same as - ## that of system.cmp - ## ## example:: ## ## var arr = @[1,2,3,5,6,7,8,9] @@ -102,9 +103,9 @@ proc lowerBound*[T](a: openArray[T], key: T, cmp: proc(x,y: T): int {.closure.}) count = step proc lowerBound*[T](a: openArray[T], key: T): int = lowerBound(a, key, cmp[T]) -proc merge[T](a, b: var openArray[T], lo, m, hi: int, +proc merge[T](a, b: var openArray[T], lo, m, hi: int, cmp: proc (x, y: T): int {.closure.}, order: SortOrder) = - template `<-` (a, b: expr) = + template `<-` (a, b: expr) = when false: a = b elif onlySafeCode: @@ -151,10 +152,10 @@ proc merge[T](a, b: var openArray[T], lo, m, hi: int, proc sort*[T](a: var openArray[T], cmp: proc (x, y: T): int {.closure.}, order = SortOrder.Ascending) = - ## Default Nim sort. The sorting is guaranteed to be stable and + ## Default Nim sort. The sorting is guaranteed to be stable and ## the worst case is guaranteed to be O(n log n). ## The current implementation uses an iterative - ## mergesort to achieve this. It uses a temporary sequence of + ## mergesort to achieve this. It uses a temporary sequence of ## length ``a.len div 2``. Currently Nim does not support a ## sensible default argument for ``cmp``, so you have to provide one ## of your own. However, the ``system.cmp`` procs can be used: @@ -187,14 +188,15 @@ 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] = +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 = +template sortedByIt*(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 @@ -202,10 +204,23 @@ template sortByIt*(seq1, op: expr): expr = ## ## .. code-block:: nim ## - ## var users: seq[tuple[id: int, name: string]] = - ## @[(0, "Smith"), (1, "Pratt"), (2, "Sparrow")] + ## type Person = tuple[name: string, age: int] + ## var + ## p1: Person = (name: "p1", age: 60) + ## p2: Person = (name: "p2", age: 20) + ## p3: Person = (name: "p3", age: 30) + ## p4: Person = (name: "p4", age: 30) + ## + ## people = @[p1,p2,p4,p3] + ## + ## echo people.sortedByIt(it.name) + ## + ## Because the underlying ``cmp()`` is defined for tuples you can do + ## a nested sort like in the following example: + ## + ## .. code-block:: nim ## - ## echo users.sortByIt(it.name) + ## echo people.sortedByIt((it.age, it.name)) ## var result {.gensym.} = sorted(seq1, proc(x, y: type(seq1[0])): int = var it {.inject.} = x diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index d6ed66030..a8caf809e 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,29 +1315,40 @@ 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.} = + # -> {.push warning[resultshadowed]: off.} # -> var result: T + # -> {.pop.} # -> <proc_body> # -> complete(retFuture, result) var iteratorNameSym = genSym(nskIterator, $prc[0].getName & "Iter") var procBody = prc[6].processBody(retFutureSym, subtypeIsVoid, nil) if not subtypeIsVoid: - procBody.insert(0, newNimNode(nnkVarSection, prc[6]).add( + procBody.insert(0, newNimNode(nnkPragma).add(newIdentNode("push"), + newNimNode(nnkExprColonExpr).add(newNimNode(nnkBracketExpr).add( + newIdentNode("warning"), newIdentNode("resultshadowed")), + newIdentNode("off")))) # -> {.push warning[resultshadowed]: off.} + + procBody.insert(1, newNimNode(nnkVarSection, prc[6]).add( newIdentDefs(newIdentNode("result"), returnType[1]))) # -> var result: T + + procBody.insert(2, newNimNode(nnkPragma).add( + newIdentNode("pop"))) # -> {.pop.}) + procBody.add( newCall(newIdentNode("complete"), retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result) 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 +1362,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 +1388,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 +1399,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..25e121183 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 @@ -24,17 +24,17 @@ import asyncdispatch, os -when defined(windows): +when defined(windows) or defined(nimdoc): import winlean else: import posix type - AsyncFile = ref object + AsyncFile* = ref object fd: TAsyncFd offset: int64 -when defined(windows): +when defined(windows) or defined(nimdoc): proc getDesiredAccess(mode: FileMode): int32 = case mode of fmRead: @@ -70,7 +70,7 @@ else: proc getFileSize(f: AsyncFile): int64 = ## Retrieves the specified file's size. - when defined(windows): + when defined(windows) or defined(nimdoc): var high: DWord let low = getFileSize(f.fd.THandle, addr high) if low == INVALID_FILE_SIZE: @@ -81,7 +81,7 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile = ## Opens a file specified by the path in ``filename`` using ## the specified ``mode`` asynchronously. new result - when defined(windows): + when defined(windows) or defined(nimdoc): let flags = FILE_FLAG_OVERLAPPED or FILE_ATTRIBUTE_NORMAL let desiredAccess = getDesiredAccess(mode) let creationDisposition = getCreationDisposition(mode, filename) @@ -120,7 +120,7 @@ proc read*(f: AsyncFile, size: int): Future[string] = ## returned. var retFuture = newFuture[string]("asyncfile.read") - when defined(windows): + when defined(windows) or defined(nimdoc): var buffer = alloc0(size) var ol = PCustomOverlapped() @@ -224,7 +224,7 @@ proc setFilePos*(f: AsyncFile, pos: int64) = ## Sets the position of the file pointer that is used for read/write ## operations. The file's first byte has the index zero. f.offset = pos - when not defined(windows): + when not defined(windows) and not defined(nimdoc): let ret = lseek(f.fd.cint, pos, SEEK_SET) if ret == -1: raiseOSError(osLastError()) @@ -245,7 +245,7 @@ proc write*(f: AsyncFile, data: string): Future[void] = ## specified file. var retFuture = newFuture[void]("asyncfile.write") var copy = data - when defined(windows): + when defined(windows) or defined(nimdoc): var buffer = alloc0(data.len) copyMem(buffer, addr copy[0], data.len) @@ -316,7 +316,7 @@ proc write*(f: AsyncFile, data: string): Future[void] = proc close*(f: AsyncFile) = ## Closes the file specified. - when defined(windows): + when defined(windows) or defined(nimdoc): if not closeHandle(f.fd.THandle).bool: raiseOSError(osLastError()) else: 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/logging.nim b/lib/pure/logging.nim index b64437c89..16c36e1f0 100644 --- a/lib/pure/logging.nim +++ b/lib/pure/logging.nim @@ -217,7 +217,7 @@ method log*(logger: RollingFileLogger, level: Level, logger.curLine = 0 logger.f = open(logger.baseName, logger.baseMode) - writeln(logger.f, LevelNames[level], " ", frmt % args) + writeln(logger.f, LevelNames[level], " ",substituteLog(logger.fmtStr), frmt % args) logger.curLine.inc # -------- diff --git a/lib/pure/md5.nim b/lib/pure/md5.nim index c6228af50..5ee301b15 100644 --- a/lib/pure/md5.nim +++ b/lib/pure/md5.nim @@ -213,13 +213,13 @@ proc toMD5*(s: string): MD5Digest = md5Update(c, cstring(s), len(s)) md5Final(c, result) -proc `$`*(D: MD5Digest): string = +proc `$`*(d: MD5Digest): string = ## converts a MD5Digest value into its string representation const digits = "0123456789abcdef" result = "" for i in 0..15: - add(result, digits[(D[i] shr 4) and 0xF]) - add(result, digits[D[i] and 0xF]) + add(result, digits[(d[i] shr 4) and 0xF]) + add(result, digits[d[i] and 0xF]) proc getMD5*(s: string): string = ## computes an MD5 value of `s` and returns its string representation diff --git a/lib/pure/net.nim b/lib/pure/net.nim index bed751542..ffbc6e320 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -81,6 +81,23 @@ type TReadLineResult: ReadLineResult, TSOBool: SOBool, PSocket: Socket, TSocketImpl: SocketImpl].} +type + IpAddressFamily* {.pure.} = enum ## Describes the type of an IP address + IPv6, ## IPv6 address + IPv4 ## IPv4 address + + TIpAddress* = object ## stores an arbitrary IP address + case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6) + of IpAddressFamily.IPv6: + address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in + ## case of IPv6 + of IpAddressFamily.IPv4: + address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in + ## case of IPv4 + +proc isIpAddress*(address_str: string): bool {.tags: [].} +proc parseIpAddress*(address_str: string): TIpAddress + proc isDisconnectionError*(flags: set[SocketFlag], lastError: OSErrorCode): bool = ## Determines whether ``lastError`` is a disconnection error. Only does this @@ -511,6 +528,12 @@ proc connect*(socket: Socket, address: string, port = Port(0), when defined(ssl): if socket.isSSL: + # RFC3546 for SNI specifies that IP addresses are not allowed. + if not isIpAddress(address): + # Discard result in case OpenSSL version doesn't support SNI, or we're + # not using TLSv1+ + discard SSL_set_tlsext_host_name(socket.sslHandle, address) + let ret = SSLConnect(socket.sslHandle) socketError(socket, ret) @@ -969,20 +992,6 @@ proc isSsl*(socket: Socket): bool = proc getFd*(socket: Socket): SocketHandle = return socket.fd ## Returns the socket's file descriptor -type - IpAddressFamily* {.pure.} = enum ## Describes the type of an IP address - IPv6, ## IPv6 address - IPv4 ## IPv4 address - - TIpAddress* = object ## stores an arbitrary IP address - case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6) - of IpAddressFamily.IPv6: - address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in - ## case of IPv6 - of IpAddressFamily.IPv4: - address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in - ## case of IPv4 - proc IPv4_any*(): TIpAddress = ## Returns the IPv4 any address, which can be used to listen on all available ## network adapters @@ -1241,7 +1250,7 @@ proc parseIPv6Address(address_str: string): TIpAddress = raise newException(ValueError, "Invalid IP Address. The address consists of too many groups") -proc parseIpAddress*(address_str: string): TIpAddress = +proc parseIpAddress(address_str: string): TIpAddress = ## Parses an IP address ## Raises EInvalidValue on error if address_str == nil: @@ -1250,3 +1259,13 @@ proc parseIpAddress*(address_str: string): TIpAddress = return parseIPv6Address(address_str) else: return parseIPv4Address(address_str) + + +proc isIpAddress(address_str: string): bool = + ## Checks if a string is an IP address + ## Returns true if it is, false otherwise + try: + discard parseIpAddress(address_str) + except ValueError: + return false + return true diff --git a/lib/pure/os.nim b/lib/pure/os.nim index bf581667b..d2e112c18 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1997,6 +1997,8 @@ proc getFileInfo*(handle: FileHandle): FileInfo = rawToFormalFileInfo(rawInfo, result) proc getFileInfo*(file: File): FileInfo = + if file.isNil: + raise newException(IOError, "File is nil") result = getFileInfo(file.getFileHandle()) proc getFileInfo*(path: string, followSymlink = true): FileInfo = diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim index b957c0cf7..2663c5b2f 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 = nil 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. @@ -621,7 +622,7 @@ proc next*(my: var XmlParser) = of stateEmptyElementTag: my.state = stateNormal my.kind = xmlElementEnd - if not isNil(my.c): + if not my.c.isNil: my.a = my.c of stateError: my.kind = xmlError 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/rawsockets.pretty.nim b/lib/pure/rawsockets.pretty.nim deleted file mode 100644 index 726c07301..000000000 --- a/lib/pure/rawsockets.pretty.nim +++ /dev/null @@ -1,426 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2015 Dominik Picheta -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This module implements a low-level cross-platform sockets interface. Look -## at the ``net`` module for the higher-level version. - -# TODO: Clean up the exports a bit and everything else in general. - -import unsigned, os - -when hostOS == "solaris": - {.passl: "-lsocket -lnsl".} - -const useWinVersion = defined(Windows) or defined(nimdoc) - -when useWinVersion: - import winlean - export WSAEWOULDBLOCK, WSAECONNRESET, WSAECONNABORTED, WSAENETRESET, - WSAEDISCON, ERROR_NETNAME_DELETED -else: - import posix - export fcntl, F_GETFL, O_NONBLOCK, F_SETFL, EAGAIN, EWOULDBLOCK, MSG_NOSIGNAL, - EINTR, EINPROGRESS, ECONNRESET, EPIPE, ENETRESET - -export SocketHandle, Sockaddr_in, Addrinfo, INADDR_ANY, SockAddr, SockLen, - inet_ntoa, recv, `==`, connect, send, accept, recvfrom, sendto - -export - SO_ERROR, - SOL_SOCKET, - SOMAXCONN, - SO_ACCEPTCONN, SO_BROADCAST, SO_DEBUG, SO_DONTROUTE, - SO_KEEPALIVE, SO_OOBINLINE, SO_REUSEADDR, - MSG_PEEK - -type - Port* = distinct uint16 ## port type - - Domain* = enum ## domain, which specifies the protocol family of the - ## created socket. Other domains than those that are listed - ## here are unsupported. - AF_UNIX, ## for local socket (using a file). Unsupported on Windows. - AF_INET = 2, ## for network protocol IPv4 or - AF_INET6 = 23 ## for network protocol IPv6. - - SockType* = enum ## second argument to `socket` proc - SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets - SOCK_DGRAM = 2, ## datagram service or Datagram Sockets - SOCK_RAW = 3, ## raw protocols atop the network layer. - SOCK_SEQPACKET = 5 ## reliable sequenced packet service - - Protocol* = enum ## third argument to `socket` proc - IPPROTO_TCP = 6, ## Transmission control protocol. - IPPROTO_UDP = 17, ## User datagram protocol. - IPPROTO_IP, ## Internet protocol. Unsupported on Windows. - IPPROTO_IPV6, ## Internet Protocol Version 6. Unsupported on Windows. - IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows. - IPPROTO_ICMP ## Control message protocol. Unsupported on Windows. - - Servent* = object ## information about a service - name*: string - aliases*: seq[string] - port*: Port - proto*: string - - Hostent* = object ## information about a given host - name*: string - aliases*: seq[string] - addrtype*: Domain - length*: int - addrList*: seq[string] - -{.deprecated: [TPort: Port, TDomain: Domain, TType: SockType, - TProtocol: Protocol, TServent: Servent, THostent: Hostent].} - -when useWinVersion: - let - osInvalidSocket* = winlean.INVALID_SOCKET - - const - IOCPARM_MASK* = 127 - IOC_IN* = int(-2147483648) - FIONBIO* = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or - (102 shl 8) or 126 - - proc ioctlsocket*(s: SocketHandle, cmd: clong, - argptr: ptr clong): cint {. - stdcall, importc: "ioctlsocket", dynlib: "ws2_32.dll".} -else: - let - osInvalidSocket* = posix.INVALID_SOCKET - -proc `==`*(a, b: Port): bool {.borrow.} - ## ``==`` for ports. - -proc `$`*(p: Port): string {.borrow.} - ## returns the port number as a string - -proc toInt*(domain: Domain): cint - ## Converts the TDomain enum to a platform-dependent ``cint``. - -proc toInt*(typ: SockType): cint - ## Converts the TType enum to a platform-dependent ``cint``. - -proc toInt*(p: Protocol): cint - ## Converts the TProtocol enum to a platform-dependent ``cint``. - -when not useWinVersion: - proc toInt(domain: Domain): cint = - case domain - of AF_UNIX: result = posix.AF_UNIX - of AF_INET: result = posix.AF_INET - of AF_INET6: result = posix.AF_INET6 - else: discard - - proc toInt(typ: SockType): cint = - case typ - of SOCK_STREAM: result = posix.SOCK_STREAM - of SOCK_DGRAM: result = posix.SOCK_DGRAM - of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET - of SOCK_RAW: result = posix.SOCK_RAW - else: discard - - proc toInt(p: Protocol): cint = - case p - of IPPROTO_TCP: result = posix.IPPROTO_TCP - of IPPROTO_UDP: result = posix.IPPROTO_UDP - of IPPROTO_IP: result = posix.IPPROTO_IP - of IPPROTO_IPV6: result = posix.IPPROTO_IPV6 - of IPPROTO_RAW: result = posix.IPPROTO_RAW - of IPPROTO_ICMP: result = posix.IPPROTO_ICMP - else: discard - -else: - proc toInt(domain: Domain): cint = - result = toU16(ord(domain)) - - proc toInt(typ: SockType): cint = - result = cint(ord(typ)) - - proc toInt(p: Protocol): cint = - result = cint(ord(p)) - - -proc newRawSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM, - protocol: Protocol = IPPROTO_TCP): SocketHandle = - ## Creates a new socket; returns `InvalidSocket` if an error occurs. - socket(toInt(domain), toInt(typ), toInt(protocol)) - -proc close*(socket: SocketHandle) = - ## closes a socket. - when useWinVersion: - discard winlean.closesocket(socket) - else: - discard posix.close(socket) - # TODO: These values should not be discarded. An EOS should be raised. - # http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times - -proc bindAddr*(socket: SocketHandle, name: ptr SockAddr, namelen: SockLen): cint = - result = bindSocket(socket, name, namelen) - -proc listen*(socket: SocketHandle, backlog = SOMAXCONN): cint {.tags: [ReadIOEffect].} = - ## Marks ``socket`` as accepting connections. - ## ``Backlog`` specifies the maximum length of the - ## queue of pending connections. - when useWinVersion: - result = winlean.listen(socket, cint(backlog)) - else: - result = posix.listen(socket, cint(backlog)) - -proc getAddrInfo*(address: string, port: Port, af: Domain = AF_INET, typ: SockType = SOCK_STREAM, - prot: Protocol = IPPROTO_TCP): ptr AddrInfo = - ## - ## - ## **Warning**: The resulting ``ptr TAddrInfo`` must be freed using ``dealloc``! - var hints: AddrInfo - result = nil - hints.ai_family = toInt(af) - hints.ai_socktype = toInt(typ) - hints.ai_protocol = toInt(prot) - var gaiResult = getaddrinfo(address, $port, addr(hints), result) - if gaiResult != 0'i32: - when useWinVersion: - raiseOSError(osLastError()) - else: - raise newException(OSError, $gai_strerror(gaiResult)) - -proc dealloc*(ai: ptr AddrInfo) = - freeaddrinfo(ai) - -proc ntohl*(x: int32): int32 = - ## Converts 32-bit integers from network to host byte order. - ## On machines where the host byte order is the same as network byte order, - ## this is a no-op; otherwise, it performs a 4-byte swap operation. - when cpuEndian == bigEndian: result = x - else: result = (x shr 24'i32) or - (x shr 8'i32 and 0xff00'i32) or - (x shl 8'i32 and 0xff0000'i32) or - (x shl 24'i32) - -proc ntohs*(x: int16): int16 = - ## Converts 16-bit integers from network to host byte order. On machines - ## where the host byte order is the same as network byte order, this is - ## a no-op; otherwise, it performs a 2-byte swap operation. - when cpuEndian == bigEndian: result = x - else: result = (x shr 8'i16) or (x shl 8'i16) - -proc htonl*(x: int32): int32 = - ## Converts 32-bit integers from host to network byte order. On machines - ## where the host byte order is the same as network byte order, this is - ## a no-op; otherwise, it performs a 4-byte swap operation. - result = rawsockets.ntohl(x) - -proc htons*(x: int16): int16 = - ## Converts 16-bit positive integers from host to network byte order. - ## On machines where the host byte order is the same as network byte - ## order, this is a no-op; otherwise, it performs a 2-byte swap operation. - result = rawsockets.ntohs(x) - -proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} = - ## Searches the database from the beginning and finds the first entry for - ## which the service name specified by ``name`` matches the s_name member - ## and the protocol name specified by ``proto`` matches the s_proto member. - ## - ## On posix this will search through the ``/etc/services`` file. - when useWinVersion: - var s = winlean.getservbyname(name, proto) - else: - var s = posix.getservbyname(name, proto) - if s == nil: raise newException(OSError, "Service not found.") - result.name = $s.s_name - result.aliases = cstringArrayToSeq(s.s_aliases) - result.port = Port(s.s_port) - result.proto = $s.s_proto - -proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} = - ## Searches the database from the beginning and finds the first entry for - ## which the port specified by ``port`` matches the s_port member and the - ## protocol name specified by ``proto`` matches the s_proto member. - ## - ## On posix this will search through the ``/etc/services`` file. - when useWinVersion: - var s = winlean.getservbyport(ze(int16(port)).cint, proto) - else: - var s = posix.getservbyport(ze(int16(port)).cint, proto) - if s == nil: raise newException(OSError, "Service not found.") - result.name = $s.s_name - result.aliases = cstringArrayToSeq(s.s_aliases) - result.port = Port(s.s_port) - result.proto = $s.s_proto - -proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} = - ## This function will lookup the hostname of an IP Address. - var myaddr: InAddr - myaddr.s_addr = inet_addr(ip) - - when useWinVersion: - var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint, - cint(rawsockets.AF_INET)) - if s == nil: raiseOSError(osLastError()) - else: - var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).Socklen, - cint(posix.AF_INET)) - if s == nil: - raise newException(OSError, $hstrerror(h_errno)) - - result.name = $s.h_name - result.aliases = cstringArrayToSeq(s.h_aliases) - when useWinVersion: - result.addrtype = Domain(s.h_addrtype) - else: - if s.h_addrtype == posix.AF_INET: - result.addrtype = AF_INET - elif s.h_addrtype == posix.AF_INET6: - result.addrtype = AF_INET6 - else: - raise newException(OSError, "unknown h_addrtype") - result.addrList = cstringArrayToSeq(s.h_addr_list) - result.length = int(s.h_length) - -proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} = - ## This function will lookup the IP address of a hostname. - when useWinVersion: - var s = winlean.gethostbyname(name) - else: - var s = posix.gethostbyname(name) - if s == nil: raiseOSError(osLastError()) - result.name = $s.h_name - result.aliases = cstringArrayToSeq(s.h_aliases) - when useWinVersion: - result.addrtype = Domain(s.h_addrtype) - else: - if s.h_addrtype == posix.AF_INET: - result.addrtype = AF_INET - elif s.h_addrtype == posix.AF_INET6: - result.addrtype = AF_INET6 - else: - raise newException(OSError, "unknown h_addrtype") - result.addrList = cstringArrayToSeq(s.h_addr_list) - result.length = int(s.h_length) - -proc getSockName*(socket: SocketHandle): Port = - ## returns the socket's associated port number. - var name: Sockaddr_in - when useWinVersion: - name.sin_family = int16(ord(AF_INET)) - else: - name.sin_family = posix.AF_INET - #name.sin_port = htons(cint16(port)) - #name.sin_addr.s_addr = htonl(INADDR_ANY) - var namelen = sizeof(name).SockLen - if getsockname(socket, cast[ptr SockAddr](addr(name)), - addr(namelen)) == -1'i32: - raiseOSError(osLastError()) - result = Port(rawsockets.ntohs(name.sin_port)) - -proc getSockOptInt*(socket: SocketHandle, level, optname: int): int {. - tags: [ReadIOEffect].} = - ## getsockopt for integer options. - var res: cint - var size = sizeof(res).SockLen - if getsockopt(socket, cint(level), cint(optname), - addr(res), addr(size)) < 0'i32: - raiseOSError(osLastError()) - result = int(res) - -proc setSockOptInt*(socket: SocketHandle, level, optname, optval: int) {. - tags: [WriteIOEffect].} = - ## setsockopt for integer options. - var value = cint(optval) - if setsockopt(socket, cint(level), cint(optname), addr(value), - sizeof(value).SockLen) < 0'i32: - raiseOSError(osLastError()) - -proc setBlocking*(s: SocketHandle, blocking: bool) = - ## Sets blocking mode on socket. - ## - ## Raises EOS on error. - when useWinVersion: - var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking - if ioctlsocket(s, FIONBIO, addr(mode)) == -1: - raiseOSError(osLastError()) - else: # BSD sockets - var x: int = fcntl(s, F_GETFL, 0) - if x == -1: - raiseOSError(osLastError()) - else: - var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK - if fcntl(s, F_SETFL, mode) == -1: - raiseOSError(osLastError()) - -proc timeValFromMilliseconds(timeout = 500): Timeval = - if timeout != -1: - var seconds = timeout div 1000 - result.tv_sec = seconds.int32 - result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 - -proc createFdSet(fd: var FdSet, s: seq[SocketHandle], m: var int) = - FD_ZERO(fd) - for i in items(s): - m = max(m, int(i)) - fdSet(i, fd) - -proc pruneSocketSet(s: var seq[SocketHandle], fd: var FdSet) = - var i = 0 - var L = s.len - while i < L: - if FD_ISSET(s[i], fd) == 0'i32: - s[i] = s[L-1] - dec(L) - else: - inc(i) - setLen(s, L) - -proc select*(readfds: var seq[SocketHandle], timeout = 500): int = - ## Traditional select function. This function will return the number of - ## sockets that are ready to be read from, written to, or which have errors. - ## If there are none; 0 is returned. - ## ``Timeout`` is in miliseconds and -1 can be specified for no timeout. - ## - ## A socket is removed from the specific ``seq`` when it has data waiting to - ## be read/written to or has errors (``exceptfds``). - var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout) - - var rd: FdSet - var m = 0 - createFdSet((rd), readfds, m) - - if timeout != -1: - result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv))) - else: - result = int(select(cint(m+1), addr(rd), nil, nil, nil)) - - pruneSocketSet(readfds, (rd)) - -proc selectWrite*(writefds: var seq[SocketHandle], - timeout = 500): int {.tags: [ReadIOEffect].} = - ## When a socket in ``writefds`` is ready to be written to then a non-zero - ## value will be returned specifying the count of the sockets which can be - ## written to. The sockets which can be written to will also be removed - ## from ``writefds``. - ## - ## ``timeout`` is specified in miliseconds and ``-1`` can be specified for - ## an unlimited time. - var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout) - - var wr: FdSet - var m = 0 - createFdSet((wr), writefds, m) - - if timeout != -1: - result = int(select(cint(m+1), nil, addr(wr), nil, addr(tv))) - else: - result = int(select(cint(m+1), nil, addr(wr), nil, nil)) - - pruneSocketSet(writefds, (wr)) - -when defined(Windows): - var wsa: WSAData - if wsaStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError()) 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/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 c39667611..25f6b85f6 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -26,9 +26,10 @@ type WeekDay* = enum ## represents a weekday dMon, dTue, dWed, dThu, dFri, dSat, dSun -var - timezone {.importc, header: "<time.h>".}: int - tzname {.importc, header: "<time.h>" .}: array[0..1, cstring] +when not defined(JS): + var + timezone {.importc, header: "<time.h>".}: int + tzname {.importc, header: "<time.h>" .}: array[0..1, cstring] when defined(posix) and not defined(JS): type @@ -48,6 +49,11 @@ when defined(posix) and not defined(JS): proc posix_gettimeofday(tp: var Timeval, unused: pointer = nil) {. importc: "gettimeofday", header: "<sys/time.h>".} + # we also need tzset() to make sure that tzname is initialized + proc tzset() {.importc, header: "<sys/time.h>".} + # calling tzset() implicitly to initialize tzname data. + tzset() + elif defined(windows): import winlean @@ -482,7 +488,7 @@ elif defined(JS): return newDate() const - weekDays: array [0..6, TWeekDay] = [ + weekDays: array [0..6, WeekDay] = [ dSun, dMon, dTue, dWed, dThu, dFri, dSat] proc getLocalTime(t: Time): TimeInfo = @@ -490,7 +496,7 @@ elif defined(JS): result.minute = t.getMinutes() result.hour = t.getHours() result.monthday = t.getDate() - result.month = TMonth(t.getMonth()) + result.month = Month(t.getMonth()) result.year = t.getFullYear() result.weekday = weekDays[t.getDay()] result.yearday = 0 @@ -500,7 +506,7 @@ elif defined(JS): result.minute = t.getUTCMinutes() result.hour = t.getUTCHours() result.monthday = t.getUTCDate() - result.month = TMonth(t.getUTCMonth()) + result.month = Month(t.getUTCMonth()) result.year = t.getUTCFullYear() result.weekday = weekDays[t.getUTCDay()] result.yearday = 0 @@ -554,7 +560,7 @@ proc `$`*(day: WeekDay): string = return lookup[day] proc `$`*(m: Month): string = - ## stingify operator for ``TMonth``. + ## stingify operator for ``Month``. const lookup: array[Month, string] = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] @@ -1104,4 +1110,4 @@ when isMainModule: # Kitchen = "3:04PM" s = "3:04PM" f = "h:mmtt" - echo "Kitchen: " & $s.parse(f) \ No newline at end of file + echo "Kitchen: " & $s.parse(f) 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/system.nim b/lib/system.nim index abf31c821..ea35bd54a 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: @@ -1556,30 +1556,51 @@ when not defined(nimrodVM) and hostOS != "standalone": ## returns the number of bytes on the shared heap that are owned by the ## process. This is only available when threads are enabled. +when sizeof(int) <= 2: + type IntLikeForCount = int|int8|int16|char|bool|uint8 +else: + type IntLikeForCount = int|int8|int16|int32|char|bool|uint8|uint16 + iterator countdown*[T](a, b: T, step = 1): T {.inline.} = ## Counts from ordinal value `a` down to `b` with the given ## step count. `T` may be any ordinal type, `step` may only - ## be positive. - var res = a - while res >= b: - yield res - dec(res, step) + ## be positive. **Note**: This fails to count to ``low(int)`` if T = int for + ## efficiency reasons. + when T is IntLikeForCount: + var res = int(a) + while res >= int(b): + yield T(res) + dec(res, step) + else: + var res = a + while res >= b: + yield res + dec(res, step) + +template countupImpl(incr: stmt) {.immediate, dirty.} = + when T is IntLikeForCount: + var res = int(a) + while res <= int(b): + yield T(res) + incr + else: + var res: T = T(a) + while res <= b: + yield res + incr iterator countup*[S, T](a: S, b: T, step = 1): T {.inline.} = ## Counts from ordinal value `a` up to `b` with the given ## step count. `S`, `T` may be any ordinal type, `step` may only - ## be positive. - var res: T = T(a) - while res <= b: - yield res + ## be positive. **Note**: This fails to count to ``high(int)`` if T = int for + ## efficiency reasons. + countupImpl: inc(res, step) iterator `..`*[S, T](a: S, b: T): T {.inline.} = ## An alias for `countup`. - var res: T = T(a) - while res <= b: - yield res - inc res + countupImpl: + inc(res) iterator `||`*[S, T](a: S, b: T, annotation=""): T {. inline, magic: "OmpParFor", sideEffect.} = @@ -1805,7 +1826,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 +1874,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 +1900,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 +1962,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 +2003,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 +2023,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 +2032,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 +2042,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 +2058,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 +2066,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 +2077,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 +2119,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 +2162,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 +2231,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 +2283,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 +2339,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 +2396,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 +2408,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 +2419,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 +2432,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 +2461,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 +2471,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 +2565,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 +2583,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 +2597,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 +2608,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 +2629,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 +2749,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 +2763,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 +2795,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 +2818,7 @@ elif defined(JS): if x == y: return 0 if x < y: return -1 return 1 - + when defined(nimffi): include "system/sysio" @@ -2831,14 +2852,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 +2901,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 +2911,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 +2958,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 +3000,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 +3111,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 +3132,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 +3140,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 +3162,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..c4374d00c 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% @@ -461,6 +461,10 @@ proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap): pointer = {.pop.} +proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} = + result = rawNewObj(typ, size, gch) + when defined(memProfiler): nimProfile(size) + proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} = result = rawNewObj(typ, size, gch) zeroMem(result, size) @@ -481,7 +485,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 +514,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 +526,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 +540,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 +584,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 +771,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 +798,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 +937,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 +950,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 +966,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 +987,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 +998,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 +1021,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 +1040,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 +1054,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 +1078,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 +1086,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..4e3dee51c 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") @@ -634,8 +634,6 @@ proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap, rc1: bool): pointer = gcTrace(res, csAllocated) release(gch) result = cellToUsr(res) - zeroMem(result, size) - when defined(memProfiler): nimProfile(size) sysAssert(allocInv(gch.region), "rawNewObj end") {.pop.} @@ -670,23 +668,31 @@ template trimAt(roots: var TCellSeq, at: int): stmt = proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} = setStackTop(gch) result = rawNewObj(typ, size, gch, false) - + zeroMem(result, size) + when defined(memProfiler): nimProfile(size) + +proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} = + setStackTop(gch) + result = rawNewObj(typ, size, gch, false) + when defined(memProfiler): nimProfile(size) + proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} = setStackTop(gch) - # `rawNewObj` already uses locks, so no need for them here. + # `newObj` already uses locks, so no need for them here. let size = addInt(mulInt(len, typ.base.size), GenericSeqSize) - result = rawNewObj(typ, size, gch, false) + result = newObj(typ, size) cast[PGenericSeq](result).len = len cast[PGenericSeq](result).reserved = len proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} = setStackTop(gch) result = rawNewObj(typ, size, gch, true) + when defined(memProfiler): nimProfile(size) proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} = setStackTop(gch) let size = addInt(mulInt(len, typ.base.size), GenericSeqSize) - result = rawNewObj(typ, size, gch, true) + result = newObjRC1(typ, size) cast[PGenericSeq](result).len = len cast[PGenericSeq](result).reserved = len diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index 6fbe94239..12eb97b1e 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -7,13 +7,13 @@ # distribution, for details about the copyright. # -# A simple mark&sweep garbage collector for Nim. Define the +# A simple mark&sweep garbage collector for Nim. Define the # symbol ``gcUseBitvectors`` to generate a variant of this GC. {.push profiler:off.} const InitialThreshold = 4*1024*1024 # X MB because marking&sweeping is slow - withBitvectors = defined(gcUseBitvectors) + withBitvectors = defined(gcUseBitvectors) # bitvectors are significantly faster for GC-bench, but slower for # bootstrapping and use more memory rcWhite = 0 @@ -29,21 +29,21 @@ type TWalkOp = enum waMarkGlobal, # we need to mark conservatively for global marker procs # as these may refer to a global var and not to a thread - # local + # local waMarkPrecise # fast precise marking TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.} # A ref type can have a finalizer that is called before the object's # storage is freed. - + TGlobalMarkerProc = proc () {.nimcall, benign.} TGcStat = object collections: int # number of performed full collections maxThreshold: int # max threshold that has been set maxStackSize: int # max stack size - freedObjects: int # max entries in cycle table - + freedObjects: int # max entries in cycle table + TGcHeap = object # this contains the zero count and # non-zero count table stackBottom: pointer @@ -64,11 +64,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) @@ -134,7 +134,7 @@ proc prepareDealloc(cell: PCell) = (cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell)) dec(gch.recGcLock) -proc nimGCref(p: pointer) {.compilerProc.} = +proc nimGCref(p: pointer) {.compilerProc.} = # we keep it from being collected by pretending it's not even allocated: when false: when withBitvectors: excl(gch.allocated, usrToCell(p)) @@ -261,6 +261,10 @@ proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} = zeroMem(result, size) when defined(memProfiler): nimProfile(size) +proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} = + result = rawNewObj(typ, size, gch) + when defined(memProfiler): nimProfile(size) + proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} = # `newObj` already uses locks, so no need for them here. let size = addInt(mulInt(len, typ.base.size), GenericSeqSize) @@ -273,25 +277,25 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} = result = rawNewObj(typ, size, gch) zeroMem(result, size) when defined(memProfiler): nimProfile(size) - + proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} = let size = addInt(mulInt(len, typ.base.size), GenericSeqSize) result = newObj(typ, size) 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) var ol = usrToCell(old) sysAssert(ol.typ != nil, "growObj: 1") gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2") - + 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)), @@ -401,7 +405,7 @@ proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} = var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell)) if objStart != nil: mark(gch, objStart) - + # ----------------- stack management -------------------------------------- # inspired from Smart Eiffel @@ -536,7 +540,7 @@ proc collectCTBody(gch: var TGcHeap) = markStackAndRegisters(gch) markGlobals(gch) sweep(gch) - + inc(gch.stat.collections) when withBitvectors: deinit(gch.marked) @@ -544,19 +548,19 @@ proc collectCTBody(gch: var TGcHeap) = gch.cycleThreshold = max(InitialThreshold, getOccupiedMem().mulThreshold) gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold) sysAssert(allocInv(gch.region), "collectCT: end") - + proc collectCT(gch: var TGcHeap) = if getOccupiedMem(gch.region) >= gch.cycleThreshold and gch.recGcLock == 0: collectCTBody(gch) when not defined(useNimRtl): - proc GC_disable() = + proc GC_disable() = when hasThreadSupport and hasSharedHeap: atomicInc(gch.recGcLock, 1) else: inc(gch.recGcLock) proc GC_enable() = - if gch.recGcLock > 0: + if gch.recGcLock > 0: when hasThreadSupport and hasSharedHeap: atomicDec(gch.recGcLock, 1) else: diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index a1a0353ca..84e532049 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.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. @@ -23,7 +23,7 @@ const leakDetector = false overwriteFree = false trackAllocationSource = leakDetector - + cycleGC = true # (de)activate the cycle GC reallyDealloc = true # for debugging purposes this can be set to false reallyOsDealloc = true @@ -71,13 +71,13 @@ when defined(boehmgc): const boehmLib = "libgc.dylib" else: const boehmLib = "/usr/lib/libgc.so.1" - + proc boehmGCinit {.importc: "GC_init", dynlib: boehmLib.} - proc boehmGC_disable {.importc: "GC_disable", dynlib: boehmLib.} - proc boehmGC_enable {.importc: "GC_enable", dynlib: boehmLib.} + proc boehmGC_disable {.importc: "GC_disable", dynlib: boehmLib.} + proc boehmGC_enable {.importc: "GC_enable", dynlib: boehmLib.} proc boehmGCincremental {. - importc: "GC_enable_incremental", dynlib: boehmLib.} - proc boehmGCfullCollect {.importc: "GC_gcollect", dynlib: boehmLib.} + importc: "GC_enable_incremental", dynlib: boehmLib.} + proc boehmGCfullCollect {.importc: "GC_gcollect", dynlib: boehmLib.} proc boehmAlloc(size: int): pointer {. importc: "GC_malloc", dynlib: boehmLib.} proc boehmAllocAtomic(size: int): pointer {. @@ -85,7 +85,7 @@ when defined(boehmgc): proc boehmRealloc(p: pointer, size: int): pointer {. importc: "GC_realloc", dynlib: boehmLib.} proc boehmDealloc(p: pointer) {.importc: "GC_free", dynlib: boehmLib.} - + proc boehmGetHeapSize: int {.importc: "GC_get_heap_size", dynlib: boehmLib.} ## Return the number of bytes in the heap. Excludes collector private ## data structures. Includes empty blocks and fragmentation loss. @@ -108,7 +108,7 @@ when defined(boehmgc): zeroMem(result, size) when not defined(useNimRtl): - + proc alloc(size: int): pointer = result = boehmAlloc(size) if result == nil: raiseOutOfMem() @@ -119,7 +119,7 @@ when defined(boehmgc): result = boehmRealloc(p, newsize) if result == nil: raiseOutOfMem() proc dealloc(p: pointer) = boehmDealloc(p) - + proc allocShared(size: int): pointer = result = boehmAlloc(size) if result == nil: raiseOutOfMem() @@ -148,14 +148,14 @@ when defined(boehmgc): proc GC_enableMarkAndSweep() = discard proc GC_disableMarkAndSweep() = discard proc GC_getStatistics(): string = return "" - + proc getOccupiedMem(): int = return boehmGetHeapSize()-boehmGetFreeBytes() proc getFreeMem(): int = return boehmGetFreeBytes() proc getTotalMem(): int = return boehmGetHeapSize() proc setStackBottom(theStackBottom: pointer) = discard - proc initGC() = + proc initGC() = when defined(macosx): boehmGCinit() proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} = @@ -171,7 +171,7 @@ when defined(boehmgc): proc nimGCref(p: pointer) {.compilerproc, inline.} = discard proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard - + proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = dest[] = src proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = @@ -181,20 +181,20 @@ when defined(boehmgc): type TMemRegion = object {.final, pure.} - + proc alloc(r: var TMemRegion, size: int): pointer = result = boehmAlloc(size) if result == nil: raiseOutOfMem() proc alloc0(r: var TMemRegion, size: int): pointer = result = alloc(size) zeroMem(result, size) - proc dealloc(r: var TMemRegion, p: pointer) = boehmDealloc(p) + proc dealloc(r: var TMemRegion, p: pointer) = boehmDealloc(p) proc deallocOsPages(r: var TMemRegion) {.inline.} = discard proc deallocOsPages() {.inline.} = discard include "system/cellsets" elif defined(nogc) and defined(useMalloc): - + when not defined(useNimRtl): proc alloc(size: int): pointer = result = cmalloc(size) @@ -206,7 +206,7 @@ elif defined(nogc) and defined(useMalloc): result = crealloc(p, newsize) if result == nil: raiseOutOfMem() proc dealloc(p: pointer) = cfree(p) - + proc allocShared(size: int): pointer = result = cmalloc(size) if result == nil: raiseOutOfMem() @@ -225,11 +225,11 @@ elif defined(nogc) and defined(useMalloc): proc GC_enableMarkAndSweep() = discard proc GC_disableMarkAndSweep() = discard proc GC_getStatistics(): string = return "" - + proc getOccupiedMem(): int = discard proc getFreeMem(): int = discard proc getTotalMem(): int = discard - + proc setStackBottom(theStackBottom: pointer) = discard proc initGC() = discard @@ -240,13 +240,15 @@ elif defined(nogc) and defined(useMalloc): result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize)) cast[PGenericSeq](result).len = len cast[PGenericSeq](result).reserved = len + proc newObjNoInit(typ: PNimType, size: int): pointer = + result = alloc(size) proc growObj(old: pointer, newsize: int): pointer = result = realloc(old, newsize) proc nimGCref(p: pointer) {.compilerproc, inline.} = discard proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard - + proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = dest[] = src proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = @@ -256,7 +258,7 @@ elif defined(nogc) and defined(useMalloc): type TMemRegion = object {.final, pure.} - + proc alloc(r: var TMemRegion, size: int): pointer = result = alloc(size) proc alloc0(r: var TMemRegion, size: int): pointer = @@ -272,9 +274,9 @@ elif defined(nogc): # object, because C does not support this operation... Even though every # possible implementation has to have a way to determine the object's size. # C just sucks. - when appType == "lib": + when appType == "lib": {.warning: "nogc in a library context may not work".} - + include "system/alloc" proc initGC() = discard @@ -285,10 +287,14 @@ elif defined(nogc): proc GC_enableMarkAndSweep() = discard proc GC_disableMarkAndSweep() = discard proc GC_getStatistics(): string = return "" - - + + proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} = result = alloc0(size) + + proc newObjNoInit(typ: PNimType, size: int): pointer = + result = alloc(size) + proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} = result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize)) cast[PGenericSeq](result).len = len @@ -299,7 +305,7 @@ elif defined(nogc): proc setStackBottom(theStackBottom: pointer) = discard proc nimGCref(p: pointer) {.compilerproc, inline.} = discard proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard - + proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = dest[] = src proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = @@ -327,6 +333,6 @@ else: include "system/gc" else: include "system/gc" - + {.pop.} diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim index 48adb895d..468af1713 100644 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -154,7 +154,7 @@ proc readAll(file: File): TaintedString = proc readFile(filename: string): TaintedString = var f = open(filename) try: - result = readAllFile(f).TaintedString + result = readAll(f).TaintedString finally: close(f) diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index cfbc24f0c..5b4020c8c 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](newObjNoInit(addr(strDesc), size)) + +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.} = @@ -203,7 +215,7 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {. GenericSeqSize)) elif newLen < result.len: # we need to decref here, otherwise the GC leaks! - when not defined(boehmGC) and not defined(nogc) and + when not defined(boehmGC) and not defined(nogc) and not defined(gcMarkAndSweep): when compileOption("gc", "v2"): for i in newLen..result.len-1: @@ -220,7 +232,7 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {. forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +% GenericSeqSize +% (i*%elemSize)), extGetCellType(result).base, waZctDecRef) - + # XXX: zeroing out the memory can still result in crashes if a wiped-out # cell is aliased by another pointer (ie proc parameter or a let variable). # This is a tought problem, because even if we don't zeroMem here, in the @@ -258,7 +270,7 @@ proc nimFloatToStr(f: float): string {.compilerproc.} = if buf[i] == ',': buf[i] = '.' hasDot = true - elif buf[i] in {'a'..'z', 'A'..'Z', '.'}: + elif buf[i] in {'a'..'z', 'A'..'Z', '.'}: hasDot = true if not hasDot: buf[n] = '.' @@ -309,7 +321,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat, template addToBuf(c) = if ti < t.high: t[ti] = c; inc(ti) - + # Sign? if s[i] == '+' or s[i] == '-': if s[i] == '-': @@ -330,7 +342,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat, if s[i] == 'I' or s[i] == 'i': if s[i+1] == 'N' or s[i+1] == 'n': if s[i+2] == 'F' or s[i+2] == 'f': - if s[i+3] notin IdentChars: + if s[i+3] notin IdentChars: number = Inf*sign return i+3 - start return 0 diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index 29fe3a921..03729dbab 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -50,7 +50,7 @@ when useWinVersion: from winlean import SocketHandle else: const - versions = "(.10|.1.0.1|.1.0.0|.0.9.9|.0.9.8|.0.9.7|.0.9.6|.0.9.5|.0.9.4)" + versions = "(.10|.1.0.1|.1.0.0|.0.9.9|.0.9.8)" when defined(macosx): const DLLSSLName = "libssl" & versions & ".dylib" @@ -141,6 +141,14 @@ const SSL_CTRL_GET_MAX_CERT_LIST* = 50 SSL_CTRL_SET_MAX_CERT_LIST* = 51 #* Allow SSL_write(..., n) to return r with 0 < r < n (i.e. report success # * when just a single record has been written): * + SSL_CTRL_SET_TLSEXT_SERVERNAME_CB = 53 + SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG = 54 + SSL_CTRL_SET_TLSEXT_HOSTNAME = 55 + TLSEXT_NAMETYPE_host_name* = 0 + SSL_TLSEXT_ERR_OK* = 0 + SSL_TLSEXT_ERR_ALERT_WARNING* = 1 + SSL_TLSEXT_ERR_ALERT_FATAL* = 2 + SSL_TLSEXT_ERR_NOACK* = 3 SSL_MODE_ENABLE_PARTIAL_WRITE* = 1 #* Make it possible to retry SSL_write() with changed buffer location # * (buffer contents must stay the same!); this is not the default to avoid # * the misconception that non-blocking SSL_write() behaves like @@ -290,15 +298,47 @@ when not useWinVersion: if p != nil: dealloc(p) proc CRYPTO_malloc_init*() = - when not useWinVersion: + when not useWinVersion and not defined(macosx): CRYPTO_set_mem_functions(allocWrapper, reallocWrapper, deallocWrapper) proc SSL_CTX_ctrl*(ctx: SslCtx, cmd: cInt, larg: int, parg: pointer): int{. cdecl, dynlib: DLLSSLName, importc.} +proc SSL_CTX_callback_ctrl(ctx: SslCtx, typ: cInt, fp: PFunction): int{. + cdecl, dynlib: DLLSSLName, importc.} + proc SSLCTXSetMode*(ctx: SslCtx, mode: int): int = result = SSL_CTX_ctrl(ctx, SSL_CTRL_MODE, mode, nil) +proc SSL_ctrl*(ssl: SslPtr, cmd: cInt, larg: int, parg: pointer): int{. + cdecl, dynlib: DLLSSLName, importc.} + +proc SSL_set_tlsext_host_name*(ssl: SslPtr, name: cstring): int = + result = SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, name) + ## Set the SNI server name extension to be used in a client hello. + ## Returns 1 if SNI was set, 0 if current SSL configuration doesn't support SNI. + + +proc SSL_get_servername*(ssl: SslPtr, typ: cInt = TLSEXT_NAMETYPE_host_name): cstring {.cdecl, dynlib: DLLSSLName, importc.} + ## Retrieve the server name requested in the client hello. This can be used + ## in the callback set in `SSL_CTX_set_tlsext_servername_callback` to + ## implement virtual hosting. May return `nil`. + +proc SSL_CTX_set_tlsext_servername_callback*(ctx: SslCtx, cb: proc(ssl: SslPtr, cb_id: int, arg: pointer): int {.cdecl.}): int = + ## Set the callback to be used on listening SSL connections when the client hello is received. + ## + ## The callback should return one of: + ## * SSL_TLSEXT_ERR_OK + ## * SSL_TLSEXT_ERR_ALERT_WARNING + ## * SSL_TLSEXT_ERR_ALERT_FATAL + ## * SSL_TLSEXT_ERR_NOACK + result = SSL_CTX_callback_ctrl(ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, cast[PFunction](cb)) + +proc SSL_CTX_set_tlsext_servername_arg*(ctx: SslCtx, arg: pointer): int = + ## Set the pointer to be used in the callback registered to ``SSL_CTX_set_tlsext_servername_callback``. + result = SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG, 0, arg) + + proc bioNew*(b: PBIO_METHOD): BIO{.cdecl, dynlib: DLLUtilName, importc: "BIO_new".} proc bioFreeAll*(b: BIO){.cdecl, dynlib: DLLUtilName, importc: "BIO_free_all".} proc bioSMem*(): PBIO_METHOD{.cdecl, dynlib: DLLUtilName, importc: "BIO_s_mem".} @@ -341,8 +381,6 @@ else: dynlib: DLLSSLName, importc.} proc SslSetFd*(s: PSSL, fd: cInt): cInt{.cdecl, dynlib: DLLSSLName, importc.} - proc SslCtrl*(ssl: PSSL, cmd: cInt, larg: int, parg: Pointer): int{.cdecl, - dynlib: DLLSSLName, importc.} proc SslCTXCtrl*(ctx: PSSL_CTX, cmd: cInt, larg: int, parg: Pointer): int{. cdecl, dynlib: DLLSSLName, importc.} diff --git a/readme.md b/readme.md index e716c0d0d..740296f4f 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,5 @@ # Nim Compiler -This repo contains the Nim compiler, Nim's stdlib, tools and +This repo contains the Nim compiler, Nim's stdlib, tools and documentation. ## Compiling @@ -8,17 +8,13 @@ the Nim compiler itself is written in the Nim programming language the C source of an older version of the compiler are needed to bootstrap the latest version. The C sources are available in a separate repo [here](http://github.com/nim-lang/csources). -Pre-compiled snapshots of the compiler are also available on -[Nimbuild](http://build.nim-lang.org/). Your platform however may not -currently be built for. - -The compiler currently supports the following platform and architecture +The compiler currently supports the following platform and architecture combinations: - + * Windows (Windows XP or greater) - x86 and x86_64 * Linux (most, if not all, distributions) - x86, x86_64, ppc64 and armv6l * Mac OS X 10.04 or higher - x86, x86_64 and ppc64 - + In reality a lot more are supported, however they are not tested regularly. To build from source you will need: @@ -54,9 +50,9 @@ questions, and you can also get help in the IRC channel on tag](http://stackoverflow.com/questions/tagged/nim). ## License -The compiler and the standard library are licensed under the MIT license, -except for some modules where the documentation suggests otherwise. This means -that you can use any license for your own programs developed with Nim, +The compiler and the standard library are licensed under the MIT license, +except for some modules where the documentation suggests otherwise. This means +that you can use any license for your own programs developed with Nim, allowing you to create commercial applications. Read copying.txt for more details. diff --git a/tests/assert/tfailedassert.nim b/tests/assert/tfailedassert.nim index 16769f529..729cdcac7 100644 --- a/tests/assert/tfailedassert.nim +++ b/tests/assert/tfailedassert.nim @@ -3,7 +3,7 @@ discard """ WARNING: false first assertion from bar ERROR: false second assertion from bar -1 -tfailedassert.nim:27 false assertion from foo +tests/assert/tfailedassert.nim:27 false assertion from foo ''' """ diff --git a/tests/ccgbugs/tstringslice.nim b/tests/ccgbugs/tstringslice.nim new file mode 100644 index 000000000..00c1adf74 --- /dev/null +++ b/tests/ccgbugs/tstringslice.nim @@ -0,0 +1,24 @@ +discard """ + output: '''1 +1234 +1234 +2 +234 +234 +3 +34 +34 +4 +4 +4''' +""" + +# 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/deprecated/tdeprecated.nim b/tests/deprecated/tdeprecated.nim index ed3d2733a..955a7f6ad 100644 --- a/tests/deprecated/tdeprecated.nim +++ b/tests/deprecated/tdeprecated.nim @@ -1,9 +1,9 @@ discard """ - nimout: "'a' is deprecated [Deprecated]" + nimout: "a is deprecated [Deprecated]" """ var a {.deprecated.}: array[0..11, int] - + a[8] = 1 diff --git a/tests/enum/tenumitems.nim b/tests/enum/tenumitems.nim index b92cff6bf..04737fa9e 100644 --- a/tests/enum/tenumitems.nim +++ b/tests/enum/tenumitems.nim @@ -1,6 +1,6 @@ discard """ line: 7 - errormsg: "expression 'items' cannot be called" + errormsg: "undeclared identifier: 'items'" """ type a = enum b,c,d diff --git a/tests/exprs/tresultwarning.nim b/tests/exprs/tresultwarning.nim new file mode 100644 index 000000000..32934408e --- /dev/null +++ b/tests/exprs/tresultwarning.nim @@ -0,0 +1,6 @@ +discard """ + nimout: "Special variable 'result' is shadowed. [ResultShadowed]" +""" + +proc test(): string = + var result = "foo" diff --git a/tests/generics/tunique_type.nim b/tests/generics/tunique_type.nim index f8901d3bd..29367181c 100644 --- a/tests/generics/tunique_type.nim +++ b/tests/generics/tunique_type.nim @@ -1,5 +1,11 @@ # Bug #2022 +discard """ + output: '''@[97, 45] +@[true, false] +@[false, false]''' +""" + ## The goal of this snippet is to provide and test a construct for general- ## purpose, random-access mapping. I use an AST-manipulation-based approach ## because it's more efficient than using procedure pointers and less @@ -31,6 +37,7 @@ type Mapped[Input; predicate: static[string]] = object input: Input macro map(input, predicate: expr): expr = + let predicate = callsite()[2] newNimNode(nnkObjConstr).add( newNimNode(nnkBracketExpr).add( ident"Mapped", diff --git a/tests/init/tuninit2.nim b/tests/init/tuninit2.nim new file mode 100644 index 000000000..950895c02 --- /dev/null +++ b/tests/init/tuninit2.nim @@ -0,0 +1,54 @@ +# bug #2316 + +type + EventType = enum + QuitEvent = 5 + AppMain* = ref object of RootObj + width: int + height: int + title: string + running: bool + event_type: EventType + App* = ref object of AppMain + draw_proc: proc(app: AppMain): void {.closure.} + events_proc: proc(app: AppMain): void {.closure.} + update_proc: proc(app: AppMain, dt: float): void {.closure.} + load_proc: proc(app: AppMain): void {.closure.} + + +proc initApp*(t: string, w, h: int): App = + App(width: w, height: h, title: t, event_type: EventType.QuitEvent) + + +method getTitle*(self: AppMain): string = self.title +method getWidth*(self: AppMain): int = self.width +method getHeight*(self: AppMain): int = self.height + + +method draw*(self: App, draw: proc(app: AppMain)): void = + self.draw_proc = draw + +method load*(self: App, load: proc(a: AppMain)): void = + self.load_proc = load + +method events*(self: App, events: proc(app: AppMain)): void = + self.events_proc = events + +method update*(self: App, update: proc(app: AppMain, delta: float)): void = + self.update_proc = update + +method run*(self: App): void = discard + +var mygame = initApp("Example", 800, 600) + +mygame.load(proc(app: AppMain): void = + echo app.getTitle() + echo app.getWidth() + echo app.getHeight() +) + +mygame.events(proc(app: AppMain): void = + discard +) + +mygame.run() diff --git a/tests/manyloc/argument_parser/ex_wget.nim b/tests/manyloc/argument_parser/ex_wget.nim index d36947b34..625a6f595 100644 --- a/tests/manyloc/argument_parser/ex_wget.nim +++ b/tests/manyloc/argument_parser/ex_wget.nim @@ -8,8 +8,8 @@ const PARAM_BACKGROUND = @["-b", "--background"] PARAM_OUTPUT = @["-o", "--output"] PARAM_NO_CLOBBER = @["-nc", "--no-clobber"] - PARAM_PROGRESS = "--progress" - PARAM_NO_PROXY = "--no-proxy" + PARAM_PROGRESS = @["--progress"] + PARAM_NO_PROXY = @["--no-proxy"] template P(tnames: varargs[string], thelp: string, ttype = PK_EMPTY, @@ -77,8 +77,8 @@ proc process_commandline(): Tcommandline_results = quit() echo "Will download to $1" % [result.options[PARAM_OUTPUT[0]].str_val] - if result.options.hasKey(PARAM_PROGRESS): - echo "Will use progress type $1" % [result.options[PARAM_PROGRESS].str_val] + if result.options.hasKey(PARAM_PROGRESS[0]): + echo "Will use progress type $1" % [result.options[PARAM_PROGRESS[0]].str_val] when isMainModule: 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/tissue710.nim b/tests/misc/tissue710.nim index 9e8735eb3..ecfdf653e 100644 --- a/tests/misc/tissue710.nim +++ b/tests/misc/tissue710.nim @@ -1,7 +1,7 @@ discard """ file: "tissue710.nim" line: 8 - errorMsg: "expression '||' cannot be called" + errorMsg: "undeclared identifier: '||'" """ var sum = 0 for x in 3..1000: diff --git a/tests/misc/tnoop.nim b/tests/misc/tnoop.nim index c79403e11..10c2eb2ec 100644 --- a/tests/misc/tnoop.nim +++ b/tests/misc/tnoop.nim @@ -1,12 +1,11 @@ discard """ file: "tnoop.nim" line: 11 - errormsg: "expression \'a()\' cannot be called" + errormsg: "undeclared identifier: 'a'" """ -# Tests the new check in the semantic pass + var a: int -a() #ERROR_MSG expression 'a()' cannot be called - +a() 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/modules/trecinca.nim b/tests/modules/trecinca.nim index bedea8d7e..14a91ba5c 100644 --- a/tests/modules/trecinca.nim +++ b/tests/modules/trecinca.nim @@ -1,11 +1,11 @@ discard """ file: "tests/reject/trecincb.nim" line: 9 - errormsg: "recursive dependency: 'trecincb.nim'" + errormsg: "recursive dependency: 'tests/modules/trecincb.nim'" """ # Test recursive includes -include trecincb #ERROR_MSG recursive dependency: 'tests/trecincb.nim' +include trecincb echo "trecina" diff --git a/tests/modules/trecincb.nim b/tests/modules/trecincb.nim index eb0f72db0..299a242e1 100644 --- a/tests/modules/trecincb.nim +++ b/tests/modules/trecincb.nim @@ -1,12 +1,12 @@ discard """ file: "trecincb.nim" line: 9 - errormsg: "recursive dependency: 'trecincb.nim'" + errormsg: "recursive dependency: 'tests/modules/trecincb.nim'" """ # Test recursive includes -include trecincb #ERROR_MSG recursive dependency: 'tests/trecincb.nim' +include trecincb echo "trecinb" diff --git a/tests/objects/tobjconstr.nim b/tests/objects/tobjconstr.nim index 3bd785728..226fe98f7 100644 --- a/tests/objects/tobjconstr.nim +++ b/tests/objects/tobjconstr.nim @@ -1,14 +1,14 @@ discard """ - output: '''(k: kindA, a: (x: abc, z: [1, 1, 3]), empty: ()) -(k: kindA, a: (x: abc, z: [1, 2, 3]), empty: ()) -(k: kindA, a: (x: abc, z: [1, 3, 3]), empty: ()) -(k: kindA, a: (x: abc, z: [1, 4, 3]), empty: ()) -(k: kindA, a: (x: abc, z: [1, 5, 3]), empty: ()) -(k: kindA, a: (x: abc, z: [1, 6, 3]), empty: ()) -(k: kindA, a: (x: abc, z: [1, 7, 3]), empty: ()) -(k: kindA, a: (x: abc, z: [1, 8, 3]), empty: ()) -(k: kindA, a: (x: abc, z: [1, 9, 3]), empty: ()) -(k: kindA, a: (x: abc, z: [1, 10, 3]), empty: ())''' + output: '''(k: kindA, a: (x: abc, z: [1, 1, 3]), method: ()) +(k: kindA, a: (x: abc, z: [1, 2, 3]), method: ()) +(k: kindA, a: (x: abc, z: [1, 3, 3]), method: ()) +(k: kindA, a: (x: abc, z: [1, 4, 3]), method: ()) +(k: kindA, a: (x: abc, z: [1, 5, 3]), method: ()) +(k: kindA, a: (x: abc, z: [1, 6, 3]), method: ()) +(k: kindA, a: (x: abc, z: [1, 7, 3]), method: ()) +(k: kindA, a: (x: abc, z: [1, 8, 3]), method: ()) +(k: kindA, a: (x: abc, z: [1, 9, 3]), method: ()) +(k: kindA, a: (x: abc, z: [1, 10, 3]), method: ())''' """ type @@ -20,9 +20,9 @@ type TDummy = ref object case k: TKind of kindXY: x, y: int - of kindA: + of kindA: a: TArg - empty: TEmpty + `method`: TEmpty # bug #1791 proc `$`[T](s: seq[T]): string = # XXX why is that not in the stdlib? @@ -34,7 +34,7 @@ proc `$`[T](s: seq[T]): string = proc main() = for i in 1..10: - let d = TDummy(k: kindA, a: TArg(x: "abc", z: @[1,i,3]), empty: TEmpty()) + let d = TDummy(k: kindA, a: TArg(x: "abc", z: @[1,i,3]), `method`: TEmpty()) echo d[] main() diff --git a/tests/overload/toverprc.nim b/tests/overload/toverprc.nim index 22b64ed48..78831f744 100644 --- a/tests/overload/toverprc.nim +++ b/tests/overload/toverprc.nim @@ -1,14 +1,19 @@ +discard """ + output: '''another number: 123 +yay''' +""" + # Test overloading of procs when used as function pointers import strutils -proc parseInt(x: float): int {.noSideEffect.} = nil -proc parseInt(x: bool): int {.noSideEffect.} = nil -proc parseInt(x: float32): int {.noSideEffect.} = nil -proc parseInt(x: int8): int {.noSideEffect.} = nil -proc parseInt(x: TFile): int {.noSideEffect.} = nil -proc parseInt(x: char): int {.noSideEffect.} = nil -proc parseInt(x: int16): int {.noSideEffect.} = nil +proc parseInt(x: float): int {.noSideEffect.} = discard +proc parseInt(x: bool): int {.noSideEffect.} = discard +proc parseInt(x: float32): int {.noSideEffect.} = discard +proc parseInt(x: int8): int {.noSideEffect.} = discard +proc parseInt(x: TFile): int {.noSideEffect.} = discard +proc parseInt(x: char): int {.noSideEffect.} = discard +proc parseInt(x: int16): int {.noSideEffect.} = discard proc parseInt[T](x: T): int = echo x; 34 @@ -19,12 +24,13 @@ var q = TParseInt(parseInt) p: TParseInt = parseInt -proc takeParseInt(x: proc (y: string): int {.noSideEffect.}): int = +proc takeParseInt(x: proc (y: string): int {.noSideEffect.}): int = result = x("123") - -echo "Give a list of numbers (separated by spaces): " -var x = stdin.readline.split.map(parseInt).max -echo x, " is the maximum!" + +if false: + echo "Give a list of numbers (separated by spaces): " + var x = stdin.readline.split.map(parseInt).max + echo x, " is the maximum!" echo "another number: ", takeParseInt(parseInt) diff --git a/tests/overload/tprefer_specialized_generic.nim b/tests/overload/tprefer_specialized_generic.nim new file mode 100644 index 000000000..2b41502d1 --- /dev/null +++ b/tests/overload/tprefer_specialized_generic.nim @@ -0,0 +1,22 @@ +discard """ + output: '''ref ref T ptr S''' +""" + +proc foo[T](x: T) = + echo "only T" + +proc foo[T](x: ref T) = + echo "ref T" + +proc foo[T, S](x: ref ref T; y: ptr S) = + echo "ref ref T ptr S" + +proc foo[T, S](x: ref T; y: ptr S) = + echo "ref T ptr S" + +proc foo[T](x: ref T; default = 0) = + echo "ref T; default" + +var x: ref ref int +var y: ptr ptr int +foo(x, y) diff --git a/tests/overload/tprefer_tygenericinst.nim b/tests/overload/tprefer_tygenericinst.nim index 2700bed5e..9787af06b 100644 --- a/tests/overload/tprefer_tygenericinst.nim +++ b/tests/overload/tprefer_tygenericinst.nim @@ -1,17 +1,42 @@ discard """ - output: "Version 2 was called." - disabled: true + output: '''Version 2 was called. +This has the highest precedence. +This has the second-highest precedence. +This has the lowest precedence.''' """ # bug #2220 +when true: + type A[T] = object + type B = A[int] -type A[T] = object -type B = A[int] + proc q[X](x: X) = + echo "Version 1 was called." -proc p[X](x: X) = - echo "Version 1 was called." + proc q(x: B) = + echo "Version 2 was called." -proc p(x: B) = - echo "Version 2 was called." + q(B()) # This call reported as ambiguous. -p(B()) # This call reported as ambiguous. +# bug #2219 +template testPred(a: expr) = + block: + type A = object of RootObj + type B = object of A + type SomeA = A|A # A hack to make "A" a typeclass. + + when a >= 3: + proc p[X](x: X) = + echo "This has the highest precedence." + when a >= 2: + proc p[X: A](x: X) = + echo "This has the second-highest precedence." + when a >= 1: + proc p[X: SomeA](x: X) = + echo "This has the lowest precedence." + + p(B()) + +testPred(3) +testPred(2) +testPred(1) 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/overload/tsystemcmp.nim b/tests/overload/tsystemcmp.nim index 54dff0c46..9bfca35d7 100644 --- a/tests/overload/tsystemcmp.nim +++ b/tests/overload/tsystemcmp.nim @@ -7,3 +7,12 @@ import algorithm # bug #1657 var modules = @["hi", "ho", "ha", "huu"] sort(modules, system.cmp) + +type + MyType = object + x: string + +proc cmp(a, b: MyType): int = cmp(a.x, b.x) + +var modulesB = @[MyType(x: "ho"), MyType(x: "ha")] +sort(modulesB, cmp) diff --git a/tests/parallel/tconvexhull.nim b/tests/parallel/tconvexhull.nim index d7e4f7716..dffe5339b 100644 --- a/tests/parallel/tconvexhull.nim +++ b/tests/parallel/tconvexhull.nim @@ -6,7 +6,7 @@ true true true''' -ccodeCheck: "!'deepcopy('" +ccodeCheck: "\\i ! @'deepCopy(' .*" """ # parallel convex hull for Nim bigbreak diff --git a/tests/parallel/tmissing_deepcopy.nim b/tests/parallel/tmissing_deepcopy.nim new file mode 100644 index 000000000..53481e4df --- /dev/null +++ b/tests/parallel/tmissing_deepcopy.nim @@ -0,0 +1,40 @@ +discard """ + ccodeCheck: "\\i @'deepCopy(' .*" +""" + +# bug #2286 + +import threadPool + +type + Person = ref object + name: string + friend: Person + +var + people: seq[Person] = @[] + +proc newPerson(name:string): Person = + result.new() + result.name = name + +proc greet(p:Person) = + p.friend.name &= "-MUT" # this line crashes the program + echo "Person {", + " name:", p.name, "(", cast[int](addr p.name),"),", + " friend:", p.friend.name, "(", cast[int](addr p.friend.name),") }" + +proc setup = + for i in 0 .. <20: + people.add newPerson("Person" & $(i + 1)) + for i in 0 .. <20: + people[i].friend = people[19-i] + +proc update = + parallel: + for i in 0 .. people.high: + spawn people[i].greet() + +when isMainModule: + setup() + update() diff --git a/tests/parallel/tsimple_array_checks.nim b/tests/parallel/tsimple_array_checks.nim new file mode 100644 index 000000000..9874d3299 --- /dev/null +++ b/tests/parallel/tsimple_array_checks.nim @@ -0,0 +1,41 @@ +# bug #2287 + +import threadPool + +# If `nums` is an array instead of seq, +# NONE of the iteration ways below work (including high / len-1) +let nums = @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + +proc log(n:int) = + echo n + +proc main = + parallel: + for n in nums: # Error: cannot prove: i <= len(nums) + -1 + spawn log(n) + #for i in 0 .. <nums.len: # Error: cannot prove: i <= len(nums) + -1 + #for i in 0 .. nums.len-1: # WORKS! + #for i in 0 .. <nums.len: # WORKS! + # spawn log(nums[i]) + +# Array needs explicit size to work, probably related to issue #2287 +#const a: array[0..5, int] = [1,2,3,4,5,6] + +#const a = [1,2,3,4,5,6] # Doesn't work +const a = @[1,2,3,4,5,6] # Doesn't work +proc f(n: int) = echo "Hello ", n + +proc maino = + parallel: + # while loop doesn't work: + var i = 0 + while i < a.high: + #for i in countup(0, a.high-1, 2): + spawn f(a[i]) + spawn f(a[i+1]) + i += 2 + +maino() # Doesn't work outside a proc + +when isMainModule: + main() diff --git a/tests/stdlib/tcount.nim b/tests/stdlib/tcount.nim new file mode 100644 index 000000000..ce1d14b6c --- /dev/null +++ b/tests/stdlib/tcount.nim @@ -0,0 +1,29 @@ +discard """ + output: '''1 +2 +3 +4 +5 +done''' +""" + +# bug #1845, #2224 + +var arr = [3,2,1,5,4] + +# bubble sort +for i in low(arr)..high(arr): + for j in i+1..high(arr): # Error: unhandled exception: value out of range: 5 [RangeError] + if arr[i] > arr[j]: + let tmp = arr[i] + arr[i] = arr[j] + arr[j] = tmp + +for i in low(arr)..high(arr): + echo arr[i] + +# check this terminates: +for x in countdown('\255', '\0'): + discard + +echo "done" diff --git a/tests/stdlib/tgetfileinfo.nim b/tests/stdlib/tgetfileinfo.nim index c8e496cc1..8a0538a5f 100644 --- a/tests/stdlib/tgetfileinfo.nim +++ b/tests/stdlib/tgetfileinfo.nim @@ -65,13 +65,13 @@ proc testGetFileInfo = try: discard getFileInfo(testFile) #echo("Handle : Valid File : Success") - except EIO: + except IOError: echo("Handle : Valid File : Failure") try: discard getFileInfo(testHandle) #echo("Handle : Valid File : Success") - except EIO: + except IOError: echo("Handle : Valid File : Failure") # Case 6 and 8 @@ -82,14 +82,14 @@ proc testGetFileInfo = try: discard getFileInfo(testFile) echo("Handle : Invalid File : Failure") - except EIO, EOS: + except IOError, OSError: discard #echo("Handle : Invalid File : Success") try: discard getFileInfo(testHandle) echo("Handle : Invalid File : Failure") - except EIO, EOS: + except IOError, OSError: discard #echo("Handle : Invalid File : Success") diff --git a/tests/stdlib/tnet.nim b/tests/stdlib/tnet.nim new file mode 100644 index 000000000..e8ada05e7 --- /dev/null +++ b/tests/stdlib/tnet.nim @@ -0,0 +1,47 @@ +import net +import unittest + +suite "isIpAddress tests": + test "127.0.0.1 is valid": + check isIpAddress("127.0.0.1") == true + + test "ipv6 localhost is valid": + check isIpAddress("::1") == true + + test "fqdn is not an ip address": + check isIpAddress("example.com") == false + + test "random string is not an ipaddress": + check isIpAddress("foo bar") == false + + test "5127.0.0.1 is invalid": + check isIpAddress("5127.0.0.1") == false + + test "ipv6 is valid": + check isIpAddress("2001:cdba:0000:0000:0000:0000:3257:9652") == true + + test "invalid ipv6": + check isIpAddress("gggg:cdba:0000:0000:0000:0000:3257:9652") == false + + +suite "parseIpAddress tests": + test "127.0.0.1 is valid": + discard parseIpAddress("127.0.0.1") + + test "ipv6 localhost is valid": + discard parseIpAddress("::1") + + test "fqdn is not an ip address": + expect(ValueError): + discard parseIpAddress("example.com") + + test "random string is not an ipaddress": + expect(ValueError): + discard parseIpAddress("foo bar") + + test "ipv6 is valid": + discard parseIpAddress("2001:cdba:0000:0000:0000:0000:3257:9652") + + test "invalid ipv6": + expect(ValueError): + discard parseIpAddress("gggg:cdba:0000:0000:0000:0000:3257:9652") 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/template/twrongmapit.nim b/tests/template/twrongmapit.nim index 4b3e1553f..bca1292b8 100644 --- a/tests/template/twrongmapit.nim +++ b/tests/template/twrongmapit.nim @@ -1,7 +1,7 @@ discard """ errormsg: "'" file: "sequtils.nim" - line: 416 + line: 435 """ # unfortunately our tester doesn't support multiple lines of compiler # error messages yet... diff --git a/tests/template/utemplates.nim b/tests/template/utemplates.nim index 38ad4f515..8b9ae5d26 100644 --- a/tests/template/utemplates.nim +++ b/tests/template/utemplates.nim @@ -12,7 +12,7 @@ test "previous definitions can be further overloaded or hidden in local scopes": check t(true) == "bool" check t(10) == "int" - + template t(a: int): expr = "inner int" check t(10) == "inner int" check t("test") == "string" @@ -21,12 +21,12 @@ test "templates can be redefined multiple times": template customAssert(cond: bool, msg: string): stmt {.immediate, dirty.} = if not cond: fail(msg) - template assertion_failed(body: stmt) {.immediate.} = + template assertion_failed(body: stmt) {.immediate, dirty.} = template fail(msg: string): stmt = body assertion_failed: check msg == "first fail path" customAssert false, "first fail path" - assertion_failed: check msg == "second fail path" + assertion_failed: check msg == "second fail path" customAssert false, "second fail path" diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index ab1e46d6f..c293be7e8 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -86,9 +86,9 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) = safeCopyFile("lib" / nimrtlDll, "tests/dll" / nimrtlDll) else: # posix relies on crappy LD_LIBRARY_PATH (ugh!): - var libpath = getenv"LD_LIBRARY_PATH".string - if peg"\i '/nim' (!'/')* '/lib'" notin libpath: - echo "[Warning] insufficient LD_LIBRARY_PATH" + var libpath = getEnv"LD_LIBRARY_PATH".string + # Temporarily add the lib directory to LD_LIBRARY_PATH: + putEnv("LD_LIBRARY_PATH", "lib:" & libpath) var serverDll = DynlibFormat % "server" safeCopyFile("tests/dll" / serverDll, "lib" / serverDll) @@ -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/htmlgen.nim b/tests/testament/htmlgen.nim index 04a1835d7..a9f739995 100644 --- a/tests/testament/htmlgen.nim +++ b/tests/testament/htmlgen.nim @@ -20,7 +20,7 @@ const <td>Success</td></tr>""" TableFooter = "</table>" HtmlBegin = """<html> - <head> + <head> <title>Test results</title> <style type="text/css"> <!--""" & slurp("css/boilerplate.css") & "\n" & @@ -28,13 +28,13 @@ const """ ul#tabs { list-style-type: none; margin: 30px 0 0 0; padding: 0 0 0.3em 0; } ul#tabs li { display: inline; } -ul#tabs li a { color: #42454a; background-color: #dedbde; - border: 1px solid #c9c3ba; border-bottom: none; +ul#tabs li a { color: #42454a; background-color: #dedbde; + border: 1px solid #c9c3ba; border-bottom: none; padding: 0.3em; text-decoration: none; } ul#tabs li a:hover { background-color: #f1f0ee; } -ul#tabs li a.selected { color: #000; background-color: #f1f0ee; +ul#tabs li a.selected { color: #000; background-color: #f1f0ee; font-weight: bold; padding: 0.7em 0.3em 0.38em 0.3em; } -div.tabContent { border: 1px solid #c9c3ba; +div.tabContent { border: 1px solid #c9c3ba; padding: 0.5em; background-color: #f1f0ee; } div.tabContent.hide { display: none; } --> @@ -43,7 +43,7 @@ div.tabContent.hide { display: none; } var tabLinks = new Array(); var contentDivs = new Array(); - + function init() { // Grab the tab links and content divs from the page var tabListItems = document.getElementById('tabs').childNodes; @@ -103,7 +103,7 @@ div.tabContent.hide { display: none; } </head> <body onload="init()">""" - + HtmlEnd = "</body></html>" proc td(s: string): string = @@ -115,8 +115,8 @@ proc getCommit(db: TDbConn, c: int): string = if commit == 0: result = thisCommit[0] inc commit -proc generateHtml*(filename: string, commit: int) = - const selRow = """select name, category, target, action, +proc generateHtml*(filename: string, commit: int; onlyFailing: bool) = + const selRow = """select name, category, target, action, expected, given, result from TestResult where [commit] = ? and machine = ? @@ -140,17 +140,20 @@ proc generateHtml*(filename: string, commit: int) = for m in db.rows(sql"select id, name, os, cpu from Machine order by id"): outfile.writeln """<li><a href="#$#">$#: $#, $#</a></li>""" % m outfile.write("</ul>") - + for currentMachine in db.rows(sql"select id from Machine order by id"): let m = currentMachine[0] outfile.write("""<div class="tabContent" id="$#">""" % m) outfile.write(TableHeader) for row in db.rows(sql(selRow), lastCommit, m): - outfile.write("<tr>") - for x in row: - outfile.write(x.td) - outfile.write("</tr>") + if onlyFailing and row.len > 0 and row[row.high] == "reSuccess": + discard + else: + outfile.write("<tr>") + for x in row: + outfile.write(x.td) + outfile.write("</tr>") outfile.write(TableFooter) outfile.write("</div>") @@ -161,7 +164,7 @@ proc generateHtml*(filename: string, commit: int) = proc generateJson*(filename: string, commit: int) = const selRow = """select count(*), - sum(result = 'reSuccess'), + sum(result = 'reSuccess'), sum(result = 'reIgnored') from TestResult where [commit] = ? and machine = ? @@ -174,9 +177,9 @@ proc generateJson*(filename: string, commit: int) = on A.name = B.name and A.category = B.category where A.[commit] = ? and B.[commit] = ? and A.machine = ? and A.result != B.result""" - selResults = """select - category || '/' || target || '/' || name, - category, target, action, result, expected, given + selResults = """select + category || '/' || target || '/' || name, + category, target, action, result, expected, given from TestResult where [commit] = ?""" var db = open(connection="testament.db", user="testament", password="", diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index 881a41ce6..7cf902704 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -30,6 +30,7 @@ Arguments: arguments are passed to the compiler Options: --print also print results to the console + --failing only show failing/ignored tests """ % resultsFile type @@ -48,7 +49,7 @@ type # ---------------------------------------------------------------------------- let - pegLineError = + pegLineError = peg"{[^(]*} '(' {\d+} ', ' \d+ ') ' ('Error') ':' \s* {.*}" pegOtherError = peg"'Error:' \s* {.*}" pegSuccess = peg"'Hint: operation successful'.*" @@ -107,7 +108,7 @@ proc addResult(r: var TResults, test: TTest, expected, given: string, success: TResultEnum) = let name = test.name.extractFilename & test.options backend.writeTestResult(name = name, - category = test.cat.string, + category = test.cat.string, target = $test.target, action = $test.action, result = $success, @@ -142,31 +143,50 @@ proc generatedFile(path, name: string, target: TTarget): string = name.changeFileExt(ext) proc codegenCheck(test: TTest, check: string, given: var TSpec) = - if check.len > 0: - try: - let (path, name, ext2) = test.name.splitFile - let genFile = generatedFile(path, name, test.target) - echo genFile - let contents = readFile(genFile).string - if contents.find(check.peg) < 0: + try: + let (path, name, ext2) = test.name.splitFile + let genFile = generatedFile(path, name, test.target) + echo genFile + let contents = readFile(genFile).string + if check[0] == '\\': + # little hack to get 'match' support: + if not contents.match(check.peg): given.err = reCodegenFailure - except ValueError: - given.err = reInvalidPeg - except IOError: - given.err = reCodeNotFound + elif contents.find(check.peg) < 0: + given.err = reCodegenFailure + except ValueError: + given.err = reInvalidPeg + echo getCurrentExceptionMsg() + except IOError: + given.err = reCodeNotFound proc nimoutCheck(test: TTest; expectedNimout: string; given: var TSpec) = - 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 + let exp = expectedNimout.strip.replace("\C\L", "\L") + let giv = given.nimout.strip.replace("\C\L", "\L") + if exp notin giv: + given.err = reMsgsDiffer 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) = + var expectedmsg: string = "" + var givenmsg: string = "" + if given.err == reSuccess: + if expected.ccodeCheck.len > 0: + codegenCheck(test, expected.ccodeCheck, given) + expectedmsg = expected.ccodeCheck + givenmsg = given.msg + if expected.nimout.len > 0: + expectedmsg = expected.nimout + givenmsg = given.nimout.strip + nimoutCheck(test, expectedmsg, given) + if given.err == reSuccess: inc(r.passed) + r.addResult(test, expectedmsg, givenmsg, given.err) + proc testSpec(r: var TResults, test: TTest) = # major entry point for a single test let tname = test.name.addFileExt(".nim") @@ -179,13 +199,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 +231,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: @@ -255,11 +267,13 @@ proc main() = backend.open() var optPrintResults = false + var optFailing = false var p = initOptParser() p.next() - if p.kind == cmdLongoption: + while p.kind == cmdLongoption: case p.key.string.normalize of "print", "verbose": optPrintResults = true + of "failing": optFailing = true else: quit Usage p.next() if p.kind != cmdArgument: quit Usage @@ -283,7 +297,7 @@ proc main() = of "html": var commit = 0 discard parseInt(p.cmdLineRest.string, commit) - generateHtml(resultsFile, commit) + generateHtml(resultsFile, commit, optFailing) generateJson(jsonFile, commit) else: quit Usage diff --git a/todo.txt b/todo.txt index 1d180f737..31c3a46db 100644 --- a/todo.txt +++ b/todo.txt @@ -4,7 +4,6 @@ version 0.10.4 - improve GC-unsafety warnings - make 'nil' work for 'add' and 'len' - get rid of 'mget'; aka priority of 'var' needs to be 'var{lvalue}' -- 'result' shadowing warning - disallow negative indexing - improve the parser; deal with echo $foo gotcha @@ -53,8 +52,8 @@ 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 ... +- ConcreteTypes in a 'case' means we don't check for duplicated case branches version 0.9.x 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/web/question.txt b/web/question.txt index 28c7ece14..0733a2455 100644 --- a/web/question.txt +++ b/web/question.txt @@ -105,12 +105,13 @@ General FAQ -------------------------- - Nim IDE: https://github.com/nim-lang/Aporia - - Emacs: https://github.com/Tass/nim-mode + - Emacs: https://github.com/reactormonk/nim-mode - Vim: https://github.com/zah/nimrod.vim/ - Scite: Included - - Gedit: The `Aporia .lang file <https://github.com/nim-lang/Aporia/blob/master/share/gtksourceview-2.0/language-specs/nimrod.lang>`_ + - Gedit: The `Aporia .lang file <https://github.com/nim-lang/Aporia/blob/master/share/gtksourceview-2.0/language-specs/nim.lang>`_ - jEdit: https://github.com/exhu/nimrod-misc/tree/master/jedit - TextMate: Available in bundle installer (`Repository <https://github.com/textmate/nim.tmbundle>`_) + - Sublime Text: Available via Package Control (`Repository <https://github.com/Varriount/NimLime>`_) .. container:: standout 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" |