Merge remote-tracking branch 'upstream/devel' into devel

This commit is contained in:
Jonathan Edwards 2015-03-11 16:26:10 -04:00
commit 50ed39fd66
129 changed files with 3195 additions and 2204 deletions

View file

@ -9,12 +9,12 @@
# abstract syntax tree + symbol table # abstract syntax tree + symbol table
import import
msgs, hashes, nversion, options, strutils, crc, ropes, idents, lists, msgs, hashes, nversion, options, strutils, crc, ropes, idents, lists,
intsets, idgen intsets, idgen
type type
TCallingConvention* = enum TCallingConvention* = enum
ccDefault, # proc has no explicit calling convention ccDefault, # proc has no explicit calling convention
ccStdCall, # procedure is stdcall ccStdCall, # procedure is stdcall
ccCDecl, # cdecl ccCDecl, # cdecl
@ -26,12 +26,12 @@ type
ccClosure, # proc has a closure ccClosure, # proc has a closure
ccNoConvention # needed for generating proper C procs sometimes ccNoConvention # needed for generating proper C procs sometimes
const const
CallingConvToStr*: array[TCallingConvention, string] = ["", "stdcall", CallingConvToStr*: array[TCallingConvention, string] = ["", "stdcall",
"cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "cdecl", "safecall", "syscall", "inline", "noinline", "fastcall",
"closure", "noconv"] "closure", "noconv"]
type type
TNodeKind* = enum # order is extremely important, because ranges are used TNodeKind* = enum # order is extremely important, because ranges are used
# to check whether a node belongs to a certain class # to check whether a node belongs to a certain class
nkNone, # unknown node kind: indicates an error nkNone, # unknown node kind: indicates an error
@ -64,13 +64,13 @@ type
# end of atoms # end of atoms
nkMetaNode_Obsolete, # difficult to explain; represents itself nkMetaNode_Obsolete, # difficult to explain; represents itself
# (used for macros) # (used for macros)
nkDotCall, # used to temporarily flag a nkCall node; nkDotCall, # used to temporarily flag a nkCall node;
# this is used # this is used
# for transforming ``s.len`` to ``len(s)`` # for transforming ``s.len`` to ``len(s)``
nkCommand, # a call like ``p 2, 4`` without parenthesis nkCommand, # a call like ``p 2, 4`` without parenthesis
nkCall, # a call like p(x, y) or an operation like +(a, b) 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, nkRStrLit
# x"""abc""" has two sons: nkIdent, nkTripleStrLit # x"""abc""" has two sons: nkIdent, nkTripleStrLit
nkInfix, # a call like (a + b) nkInfix, # a call like (a + b)
@ -126,7 +126,7 @@ type
nkAsgn, # a = b nkAsgn, # a = b
nkFastAsgn, # internal node for a fast ``a = b`` nkFastAsgn, # internal node for a fast ``a = b``
# (no string copy) # (no string copy)
nkGenericParams, # generic parameters nkGenericParams, # generic parameters
nkFormalParams, # formal parameters nkFormalParams, # formal parameters
nkOfInherit, # inherited from symbol nkOfInherit, # inherited from symbol
@ -192,10 +192,11 @@ type
nkWith, # distinct with `foo` nkWith, # distinct with `foo`
nkWithout, # distinct without `foo` nkWithout, # distinct without `foo`
nkTypeOfExpr, # type(1+2) nkTypeOfExpr, # type(1+2)
nkObjectTy, # object body nkObjectTy, # object body
nkTupleTy, # tuple body nkTupleTy, # tuple body
nkTupleClassTy, # tuple type class
nkTypeClassTy, # user-defined type class nkTypeClassTy, # user-defined type class
nkStaticTy, # ``static[T]`` nkStaticTy, # ``static[T]``
nkRecList, # list of object parts nkRecList, # list of object parts
@ -226,7 +227,7 @@ type
TSymFlag* = enum # already 32 flags! TSymFlag* = enum # already 32 flags!
sfUsed, # read access of sym (for warnings) or simply used sfUsed, # read access of sym (for warnings) or simply used
sfExported, # symbol is exported from module 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 # for symbol file generation; such symbols should always
# be written into the ROD file # be written into the ROD file
sfGlobal, # symbol is at global scope sfGlobal, # symbol is at global scope
@ -284,9 +285,9 @@ const
sfAnon* = sfDiscardable sfAnon* = sfDiscardable
# symbol name that was generated by the compiler # 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. # in user messages.
sfNoForward* = sfRegister sfNoForward* = sfRegister
# forward declarations are not required (per module) # forward declarations are not required (per module)
@ -300,7 +301,7 @@ const
# getting ready for the future expr/stmt merge # getting ready for the future expr/stmt merge
nkWhen* = nkWhenStmt nkWhen* = nkWhenStmt
nkWhenExpr* = nkWhenStmt nkWhenExpr* = nkWhenStmt
nkEffectList* = nkArgList nkEffectList* = nkArgList
# hacks ahead: an nkEffectList is a node with 4 children: # hacks ahead: an nkEffectList is a node with 4 children:
exceptionEffects* = 0 # exceptions at position 0 exceptionEffects* = 0 # exceptions at position 0
usesEffects* = 1 # read effects at position 1 usesEffects* = 1 # read effects at position 1
@ -321,7 +322,7 @@ type
# unless this is an instance of a generic alias type. # unless this is an instance of a generic alias type.
# then realInstance will be the tyGenericInst of the # then realInstance will be the tyGenericInst of the
# completely (recursively) resolved alias. # completely (recursively) resolved alias.
tyGenericParam, # ``a`` in the above patterns tyGenericParam, # ``a`` in the above patterns
tyDistinct, tyDistinct,
tyEnum, tyEnum,
@ -340,14 +341,14 @@ type
tyInt, tyInt8, tyInt16, tyInt32, tyInt64, # signed integers tyInt, tyInt8, tyInt16, tyInt32, tyInt64, # signed integers
tyFloat, tyFloat32, tyFloat64, tyFloat128, tyFloat, tyFloat32, tyFloat64, tyFloat128,
tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64, tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64,
tyBigNum, tyBigNum,
tyConst, tyMutable, tyVarargs, tyConst, tyMutable, tyVarargs,
tyIter, # unused tyIter, # unused
tyProxy # used as errornous type (for idetools) tyProxy # used as errornous type (for idetools)
tyBuiltInTypeClass #\ tyBuiltInTypeClass #\
# Type such as the catch-all object, tuple, seq, etc # Type such as the catch-all object, tuple, seq, etc
tyUserTypeClass #\ tyUserTypeClass #\
# the body of a user-defined type class # the body of a user-defined type class
@ -357,24 +358,24 @@ type
# tyGenericInst represents concrete types, while # tyGenericInst represents concrete types, while
# this is still a "generic param" that will bind types # this is still a "generic param" that will bind types
# and resolves them during sigmatch and instantiation. # and resolves them during sigmatch and instantiation.
tyCompositeTypeClass #\ tyCompositeTypeClass #\
# Type such as seq[Number] # 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[0]: the original expression used by the user.
# sons[1]: fully expanded and instantiated meta type # sons[1]: fully expanded and instantiated meta type
# (potentially following aliases) # (potentially following aliases)
tyAnd, tyOr, tyNot #\ tyAnd, tyOr, tyNot #\
# boolean type classes such as `string|int`,`not seq`, # boolean type classes such as `string|int`,`not seq`,
# `Sortable and Enumable`, etc # `Sortable and Enumable`, etc
tyAnything #\ tyAnything #\
# a type class matching any type # a type class matching any type
tyStatic #\ tyStatic #\
# a value known at compile type (the underlying type is .base) # a value known at compile type (the underlying type is .base)
tyFromExpr #\ tyFromExpr #\
# This is a type representing an expression that depends # This is a type representing an expression that depends
# on generic parameters (the expression is stored in t.n) # on generic parameters (the expression is stored in t.n)
@ -390,7 +391,7 @@ type
# sons[0]: type of containing object or tuple # sons[0]: type of containing object or tuple
# sons[1]: field type # sons[1]: field type
# .n: nkDotExpr storing the field name # .n: nkDotExpr storing the field name
static: static:
# remind us when TTypeKind stops to fit in a single 64-bit word # remind us when TTypeKind stops to fit in a single 64-bit word
assert TTypeKind.high.ord <= 63 assert TTypeKind.high.ord <= 63
@ -408,7 +409,7 @@ const
tyAnd, tyOr, tyNot, tyAnything} tyAnd, tyOr, tyNot, tyAnything}
tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyExpr} + tyTypeClasses tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyExpr} + tyTypeClasses
type type
TTypeKinds* = set[TTypeKind] TTypeKinds* = set[TTypeKind]
@ -429,7 +430,7 @@ type
nfExprCall # this is an attempt to call a regular expression nfExprCall # this is an attempt to call a regular expression
nfIsRef # this node is a 'ref' node; used for the VM nfIsRef # this node is a 'ref' node; used for the VM
nfIsCursor # this node is attached a cursor; used for idetools nfIsCursor # this node is attached a cursor; used for idetools
TNodeFlags* = set[TNodeFlag] TNodeFlags* = set[TNodeFlag]
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 28) TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 28)
tfVarargs, # procedure has C styled varargs tfVarargs, # procedure has C styled varargs
@ -448,7 +449,7 @@ type
# proc foo(L: static[int]): array[L, int] # proc foo(L: static[int]): array[L, int]
# can be attached to ranges to indicate that the range # can be attached to ranges to indicate that the range
# depends on unresolved static params. # depends on unresolved static params.
tfRetType, # marks return types in proc (used to detect type classes tfRetType, # marks return types in proc (used to detect type classes
# used as return types for return type inference) # used as return types for return type inference)
tfCapturesEnv, # whether proc really captures some environment tfCapturesEnv, # whether proc really captures some environment
tfByCopy, # pass object/tuple by copy (C backend) tfByCopy, # pass object/tuple by copy (C backend)
@ -456,9 +457,9 @@ type
tfIterator, # type is really an iterator, not a tyProc tfIterator, # type is really an iterator, not a tyProc
tfShared, # type is 'shared' tfShared, # type is 'shared'
tfNotNil, # type cannot be 'nil' tfNotNil, # type cannot be 'nil'
tfNeedsInit, # type constains a "not nil" constraint somewhere or some 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 tfVarIsPtr, # 'var' type is translated like 'ptr' even in C++ mode
tfHasMeta, # type contains "wildcard" sub-types such as generic params tfHasMeta, # type contains "wildcard" sub-types such as generic params
# or other type classes # or other type classes
@ -520,7 +521,7 @@ const
tfGcSafe* = tfThread tfGcSafe* = tfThread
tfObjHasKids* = tfEnumHasHoles tfObjHasKids* = tfEnumHasHoles
skError* = skUnknown skError* = skUnknown
# type flags that are essential for type equality: # type flags that are essential for type equality:
eqTypeFlags* = {tfIterator, tfShared, tfNotNil, tfVarIsPtr} eqTypeFlags* = {tfIterator, tfShared, tfNotNil, tfVarIsPtr}
@ -531,29 +532,29 @@ type
mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf,
mEcho, mShallowCopy, mSlurp, mStaticExec, mEcho, mShallowCopy, mSlurp, mStaticExec,
mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst, mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
mUnaryLt, mSucc, mUnaryLt, mSucc,
mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray, mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray,
mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref, mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref,
mGCunref, mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64, mGCunref, mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64,
mDivI64, mModI64, mDivI64, mModI64,
mAddF64, mSubF64, mMulF64, mDivF64, 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, mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinI64, mMaxI64,
mMinF64, mMaxF64, mAddU, mSubU, mMulU, mMinF64, mMaxF64, mAddU, mSubU, mMulU,
mDivU, mModU, mEqI, mLeI, mDivU, mModU, mEqI, mLeI,
mLtI, mLtI,
mEqI64, mLeI64, mLtI64, mEqF64, mLeF64, mLtF64, mEqI64, mLeI64, mLtI64, mEqF64, mLeF64, mLtF64,
mLeU, mLtU, mLeU64, mLtU64, mLeU, mLtU, mLeU64, mLtU64,
mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef, mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef,
mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mEqProc, mUnaryMinusI, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mEqProc, mUnaryMinusI,
mUnaryMinusI64, mAbsI, mAbsI64, mNot, mUnaryMinusI64, mAbsI, mAbsI64, mNot,
mUnaryPlusI, mBitnotI, mUnaryPlusI64, mUnaryPlusI, mBitnotI, mUnaryPlusI64,
mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64, mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64,
mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32,
mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr, mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr,
mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr,
mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet, mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet,
mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mConArrArr, mConArrT, mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mConArrArr, mConArrT,
mConTArr, mConTT, mSlice, mConTArr, mConTT, mSlice,
mFields, mFieldPairs, mOmpParFor, mFields, mFieldPairs, mOmpParFor,
mAppendStrCh, mAppendStrStr, mAppendSeqElem, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
@ -584,36 +585,36 @@ type
# things that we can evaluate safely at compile time, even if not asked for it: # things that we can evaluate safely at compile time, even if not asked for it:
const const
ctfeWhitelist* = {mNone, mUnaryLt, mSucc, ctfeWhitelist* = {mNone, mUnaryLt, mSucc,
mPred, mInc, mDec, mOrd, mLengthOpenArray, mPred, mInc, mDec, mOrd, mLengthOpenArray,
mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr,
mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64, mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64,
mDivI64, mModI64, mAddF64, mSubF64, mMulF64, mDivF64, 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, mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinI64, mMaxI64,
mMinF64, mMaxF64, mAddU, mSubU, mMulU, mMinF64, mMaxF64, mAddU, mSubU, mMulU,
mDivU, mModU, mEqI, mLeI, mDivU, mModU, mEqI, mLeI,
mLtI, mLtI,
mEqI64, mLeI64, mLtI64, mEqF64, mLeF64, mLtF64, mEqI64, mLeI64, mLtI64, mEqF64, mLeF64, mLtF64,
mLeU, mLtU, mLeU64, mLtU64, mLeU, mLtU, mLeU64, mLtU64,
mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef, mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef,
mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI, mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI,
mUnaryMinusI64, mAbsI, mAbsI64, mNot, mUnaryMinusI64, mAbsI, mAbsI64, mNot,
mUnaryPlusI, mBitnotI, mUnaryPlusI64, mUnaryPlusI, mBitnotI, mUnaryPlusI64,
mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64, mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64,
mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32,
mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr, mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr,
mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr,
mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet, mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet,
mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mConArrArr, mConArrT, mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mConArrArr, mConArrT,
mConTArr, mConTT, mConTArr, mConTT,
mAppendStrCh, mAppendStrStr, mAppendSeqElem, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
mInRange, mInSet, mRepr, mInRange, mInSet, mRepr,
mCopyStr, mCopyStrLast} mCopyStr, mCopyStrLast}
# magics that require special semantic checking and # magics that require special semantic checking and
# thus cannot be overloaded (also documented in the spec!): # thus cannot be overloaded (also documented in the spec!):
SpecialSemMagics* = { SpecialSemMagics* = {
mDefined, mDefinedInScope, mCompiles, mLow, mHigh, mSizeOf, mIs, mOf, mDefined, mDefinedInScope, mCompiles, mLow, mHigh, mSizeOf, mIs, mOf,
mEcho, mShallowCopy, mExpandToAst, mParallel, mSpawn, mAstToStr} mEcho, mShallowCopy, mExpandToAst, mParallel, mSpawn, mAstToStr}
type type
@ -634,21 +635,21 @@ type
floatVal*: BiggestFloat floatVal*: BiggestFloat
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
strVal*: string strVal*: string
of nkSym: of nkSym:
sym*: PSym sym*: PSym
of nkIdent: of nkIdent:
ident*: PIdent ident*: PIdent
else: else:
sons*: TNodeSeq sons*: TNodeSeq
comment*: string comment*: string
TSymSeq* = seq[PSym] TSymSeq* = seq[PSym]
TStrTable* = object # a table[PIdent] of PSym TStrTable* = object # a table[PIdent] of PSym
counter*: int counter*: int
data*: TSymSeq data*: TSymSeq
# -------------- backend information ------------------------------- # -------------- backend information -------------------------------
TLocKind* = enum TLocKind* = enum
locNone, # no location locNone, # no location
locTemp, # temporary location locTemp, # temporary location
locLocalVar, # location is a local variable locLocalVar, # location is a local variable
@ -660,7 +661,7 @@ type
locData, # location is a constant locData, # location is a constant
locCall, # location is a call expression locCall, # location is a call expression
locOther # location is something other locOther # location is something other
TLocFlag* = enum TLocFlag* = enum
lfIndirect, # backend introduced a pointer lfIndirect, # backend introduced a pointer
lfParamCopy, # backend introduced a parameter copy (LLVM) lfParamCopy, # backend introduced a parameter copy (LLVM)
lfNoDeepCopy, # no need for a deep copy lfNoDeepCopy, # no need for a deep copy
@ -670,13 +671,13 @@ type
lfHeader, # include header file for symbol lfHeader, # include header file for symbol
lfImportCompilerProc, # ``importc`` of a compilerproc lfImportCompilerProc, # ``importc`` of a compilerproc
lfSingleUse # no location yet and will only be used once lfSingleUse # no location yet and will only be used once
TStorageLoc* = enum TStorageLoc* = enum
OnUnknown, # location is unknown (stack, heap or static) OnUnknown, # location is unknown (stack, heap or static)
OnStack, # location is on hardware stack OnStack, # location is on hardware stack
OnHeap # location is on heap or global OnHeap # location is on heap or global
# (reference counting needed) # (reference counting needed)
TLocFlags* = set[TLocFlag] TLocFlags* = set[TLocFlag]
TLoc* = object TLoc* = object
k*: TLocKind # kind of location k*: TLocKind # kind of location
s*: TStorageLoc s*: TStorageLoc
flags*: TLocFlags # location's flags flags*: TLocFlags # location's flags
@ -688,7 +689,7 @@ type
# ---------------- end of backend information ------------------------------ # ---------------- end of backend information ------------------------------
TLibKind* = enum TLibKind* = enum
libHeader, libDynamic libHeader, libDynamic
TLib* = object of lists.TListEntry # also misused for headers! TLib* = object of lists.TListEntry # also misused for headers!
kind*: TLibKind kind*: TLibKind
@ -696,17 +697,17 @@ type
isOverriden*: bool isOverriden*: bool
name*: PRope name*: PRope
path*: PNode # can be a string literal! path*: PNode # can be a string literal!
TInstantiation* = object TInstantiation* = object
sym*: PSym sym*: PSym
concreteTypes*: seq[PType] concreteTypes*: seq[PType]
usedBy*: seq[int32] # list of modules using the generic usedBy*: seq[int32] # list of modules using the generic
# needed in caas mode for purging the cache # 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 # simple ref count here
PInstantiation* = ref TInstantiation PInstantiation* = ref TInstantiation
TScope* = object TScope* = object
depthLevel*: int depthLevel*: int
symbols*: TStrTable symbols*: TStrTable
@ -773,7 +774,7 @@ type
constraint*: PNode # additional constraints like 'lit|result'; also constraint*: PNode # additional constraints like 'lit|result'; also
# misused for the codegenDecl pragma in the hope # misused for the codegenDecl pragma in the hope
# it won't cause problems # it won't cause problems
TTypeSeq* = seq[PType] TTypeSeq* = seq[PType]
TLockLevel* = distinct int16 TLockLevel* = distinct int16
TType* {.acyclic.} = object of TIdObj # \ TType* {.acyclic.} = object of TIdObj # \
@ -806,7 +807,7 @@ type
lockLevel*: TLockLevel # lock level as required for deadlock checking lockLevel*: TLockLevel # lock level as required for deadlock checking
loc*: TLoc loc*: TLoc
TPair*{.final.} = object TPair*{.final.} = object
key*, val*: RootRef key*, val*: RootRef
TPairSeq* = seq[TPair] TPairSeq* = seq[TPair]
@ -814,7 +815,7 @@ type
counter*: int counter*: int
data*: TPairSeq data*: TPairSeq
TIdPair*{.final.} = object TIdPair*{.final.} = object
key*: PIdObj key*: PIdObj
val*: RootRef val*: RootRef
@ -823,7 +824,7 @@ type
counter*: int counter*: int
data*: TIdPairSeq data*: TIdPairSeq
TIdNodePair*{.final.} = object TIdNodePair*{.final.} = object
key*: PIdObj key*: PIdObj
val*: PNode val*: PNode
@ -832,7 +833,7 @@ type
counter*: int counter*: int
data*: TIdNodePairSeq data*: TIdNodePairSeq
TNodePair*{.final.} = object TNodePair*{.final.} = object
h*: THash # because it is expensive to compute! h*: THash # because it is expensive to compute!
key*: PNode key*: PNode
val*: int val*: int
@ -844,7 +845,7 @@ type
data*: TNodePairSeq data*: TNodePairSeq
TObjectSeq* = seq[RootRef] TObjectSeq* = seq[RootRef]
TObjectSet*{.final.} = object TObjectSet*{.final.} = object
counter*: int counter*: int
data*: TObjectSeq data*: TObjectSeq
@ -855,27 +856,27 @@ type
# same name as an imported module. This is necessary because of # same name as an imported module. This is necessary because of
# the poor naming choices in the standard library. # the poor naming choices in the standard library.
const const
OverloadableSyms* = {skProc, skMethod, skIterator, skClosureIterator, OverloadableSyms* = {skProc, skMethod, skIterator, skClosureIterator,
skConverter, skModule, skTemplate, skMacro} skConverter, skModule, skTemplate, skMacro}
GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody, GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody,
tyGenericParam} tyGenericParam}
StructuralEquivTypes*: TTypeKinds = {tyArrayConstr, tyNil, tyTuple, tyArray, StructuralEquivTypes*: TTypeKinds = {tyArrayConstr, tyNil, tyTuple, tyArray,
tySet, tyRange, tyPtr, tyRef, tyVar, tySequence, tyProc, tyOpenArray, tySet, tyRange, tyPtr, tyRef, tyVar, tySequence, tyProc, tyOpenArray,
tyVarargs} tyVarargs}
ConcreteTypes*: TTypeKinds = { # types of the expr that may occur in:: ConcreteTypes*: TTypeKinds = { # types of the expr that may occur in::
# var x = expr # var x = expr
tyBool, tyChar, tyEnum, tyArray, tyObject, tyBool, tyChar, tyEnum, tyArray, tyObject,
tySet, tyTuple, tyRange, tyPtr, tyRef, tyVar, tySequence, tyProc, tySet, tyTuple, tyRange, tyPtr, tyRef, tyVar, tySequence, tyProc,
tyPointer, tyPointer,
tyOpenArray, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128, tyOpenArray, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
tyUInt..tyUInt64} tyUInt..tyUInt64}
IntegralTypes* = {tyBool, tyChar, tyEnum, tyInt..tyInt64, IntegralTypes* = {tyBool, tyChar, tyEnum, tyInt..tyInt64,
tyFloat..tyFloat128, tyUInt..tyUInt64} tyFloat..tyFloat128, tyUInt..tyUInt64}
ConstantDataTypes*: TTypeKinds = {tyArrayConstr, tyArray, tySet, ConstantDataTypes*: TTypeKinds = {tyArrayConstr, tyArray, tySet,
tyTuple, tySequence} tyTuple, tySequence}
NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence, NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence,
tyProc, tyString, tyError} tyProc, tyString, tyError}
@ -926,17 +927,17 @@ proc discardSons*(father: PNode)
proc len*(n: PNode): int {.inline.} = proc len*(n: PNode): int {.inline.} =
if isNil(n.sons): result = 0 if isNil(n.sons): result = 0
else: result = len(n.sons) else: result = len(n.sons)
proc safeLen*(n: PNode): int {.inline.} = proc safeLen*(n: PNode): int {.inline.} =
## works even for leaves. ## works even for leaves.
if n.kind in {nkNone..nkNilLit} or isNil(n.sons): result = 0 if n.kind in {nkNone..nkNilLit} or isNil(n.sons): result = 0
else: result = len(n.sons) else: result = len(n.sons)
proc add*(father, son: PNode) = proc add*(father, son: PNode) =
assert son != nil assert son != nil
if isNil(father.sons): father.sons = @[] if isNil(father.sons): father.sons = @[]
add(father.sons, son) add(father.sons, son)
proc `[]`*(n: PNode, i: int): PNode {.inline.} = proc `[]`*(n: PNode, i: int): PNode {.inline.} =
result = n.sons[i] result = n.sons[i]
@ -946,13 +947,10 @@ template `{}=`*(n: PNode, i: int, s: PNode): stmt =
n.sons[i -| n] = s n.sons[i -| n] = s
when defined(useNodeIds): when defined(useNodeIds):
const nodeIdToDebug* = -1 # 884953 # 612794 const nodeIdToDebug* = -1 # 299750 # 300761 #300863 # 300879
#612840 # 612905 # 614635 # 614637 # 614641
# 423408
#429107 # 430443 # 441048 # 441090 # 441153
var gNodeId: int var gNodeId: int
proc newNode*(kind: TNodeKind): PNode = proc newNode*(kind: TNodeKind): PNode =
new(result) new(result)
result.kind = kind result.kind = kind
#result.info = UnknownLineInfo() inlined: #result.info = UnknownLineInfo() inlined:
@ -966,24 +964,24 @@ proc newNode*(kind: TNodeKind): PNode =
writeStackTrace() writeStackTrace()
inc gNodeId inc gNodeId
proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode = proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
result = newNode(kind) result = newNode(kind)
result.intVal = intVal 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 = newIntNode(kind, intVal)
result.typ = typ result.typ = typ
proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode = proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode =
result = newNode(kind) result = newNode(kind)
result.floatVal = floatVal result.floatVal = floatVal
proc newStrNode*(kind: TNodeKind, strVal: string): PNode = proc newStrNode*(kind: TNodeKind, strVal: string): PNode =
result = newNode(kind) result = newNode(kind)
result.strVal = strVal result.strVal = strVal
proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym, proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
info: TLineInfo): PSym = info: TLineInfo): PSym =
# generates a symbol and initializes the hash field too # generates a symbol and initializes the hash field too
new(result) new(result)
result.name = name result.name = name
@ -994,7 +992,7 @@ proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
result.owner = owner result.owner = owner
result.offset = - 1 result.offset = - 1
result.id = getID() result.id = getID()
when debugIds: when debugIds:
registerId(result) registerId(result)
#if result.id < 2000: #if result.id < 2000:
# MessageOut(name.s & " has id: " & toString(result.id)) # MessageOut(name.s & " has id: " & toString(result.id))
@ -1036,54 +1034,54 @@ proc appendToModule*(m: PSym, n: PNode) =
else: else:
assert m.ast.kind == nkStmtList assert m.ast.kind == nkStmtList
m.ast.sons.add(n) m.ast.sons.add(n)
const # for all kind of hash tables: const # for all kind of hash tables:
GrowthFactor* = 2 # must be power of 2, > 0 GrowthFactor* = 2 # must be power of 2, > 0
StartSize* = 8 # 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 dest.counter = src.counter
if isNil(src.data): return if isNil(src.data): return
setLen(dest.data, len(src.data)) setLen(dest.data, len(src.data))
for i in countup(0, high(src.data)): dest.data[i] = src.data[i] 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 dest.counter = src.counter
if isNil(src.data): return if isNil(src.data): return
newSeq(dest.data, len(src.data)) newSeq(dest.data, len(src.data))
for i in countup(0, high(src.data)): dest.data[i] = src.data[i] 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 dest.counter = src.counter
if isNil(src.data): return if isNil(src.data): return
setLen(dest.data, len(src.data)) setLen(dest.data, len(src.data))
for i in countup(0, high(src.data)): dest.data[i] = src.data[i] 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 dest.counter = src.counter
if isNil(src.data): return if isNil(src.data): return
setLen(dest.data, len(src.data)) setLen(dest.data, len(src.data))
for i in countup(0, high(src.data)): dest.data[i] = src.data[i] 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 father.sons = nil
proc withInfo*(n: PNode, info: TLineInfo): PNode = proc withInfo*(n: PNode, info: TLineInfo): PNode =
n.info = info n.info = info
return n return n
proc newIdentNode*(ident: PIdent, info: TLineInfo): PNode = proc newIdentNode*(ident: PIdent, info: TLineInfo): PNode =
result = newNode(nkIdent) result = newNode(nkIdent)
result.ident = ident result.ident = ident
result.info = info result.info = info
proc newSymNode*(sym: PSym): PNode = proc newSymNode*(sym: PSym): PNode =
result = newNode(nkSym) result = newNode(nkSym)
result.sym = sym result.sym = sym
result.typ = sym.typ result.typ = sym.typ
result.info = sym.info result.info = sym.info
proc newSymNode*(sym: PSym, info: TLineInfo): PNode = proc newSymNode*(sym: PSym, info: TLineInfo): PNode =
result = newNode(nkSym) result = newNode(nkSym)
result.sym = sym result.sym = sym
result.typ = sym.typ result.typ = sym.typ
@ -1128,12 +1126,12 @@ proc newNode*(kind: TNodeKind, info: TLineInfo, sons: TNodeSeq = @[],
writeStackTrace() writeStackTrace()
inc gNodeId inc gNodeId
proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode = proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
result = newNode(kind) result = newNode(kind)
result.info = info result.info = info
result.typ = typ result.typ = typ
proc addSon*(father, son: PNode) = proc addSon*(father, son: PNode) =
assert son != nil assert son != nil
if isNil(father.sons): father.sons = @[] if isNil(father.sons): father.sons = @[]
add(father.sons, son) add(father.sons, son)
@ -1159,7 +1157,7 @@ proc `$`*(x: TLockLevel): string =
elif x.ord == UnknownLockLevel.ord: result = "<unknown>" elif x.ord == UnknownLockLevel.ord: result = "<unknown>"
else: result = $int16(x) else: result = $int16(x)
proc newType*(kind: TTypeKind, owner: PSym): PType = proc newType*(kind: TTypeKind, owner: PSym): PType =
new(result) new(result)
result.kind = kind result.kind = kind
result.owner = owner result.owner = owner
@ -1171,7 +1169,7 @@ proc newType*(kind: TTypeKind, owner: PSym): PType =
registerId(result) registerId(result)
#if result.id < 2000: #if result.id < 2000:
# messageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id)) # messageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id))
proc mergeLoc(a: var TLoc, b: TLoc) = proc mergeLoc(a: var TLoc, b: TLoc) =
if a.k == low(a.k): a.k = b.k if a.k == low(a.k): a.k = b.k
if a.s == low(a.s): a.s = b.s 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.r == nil: a.r = b.r
#if a.a == 0: a.a = b.a #if a.a == 0: a.a = b.a
proc newSons*(father: PNode, length: int) = proc newSons*(father: PNode, length: int) =
if isNil(father.sons): if isNil(father.sons):
newSeq(father.sons, length) newSeq(father.sons, length)
else: else:
setLen(father.sons, length) setLen(father.sons, length)
proc newSons*(father: PType, length: int) = proc newSons*(father: PType, length: int) =
if isNil(father.sons): if isNil(father.sons):
newSeq(father.sons, length) newSeq(father.sons, length)
else: else:
setLen(father.sons, length) setLen(father.sons, length)
proc sonsLen*(n: PType): int = proc sonsLen*(n: PType): int =
if isNil(n.sons): result = 0 if isNil(n.sons): result = 0
else: result = len(n.sons) else: result = len(n.sons)
proc len*(n: PType): int = proc len*(n: PType): int =
if isNil(n.sons): result = 0 if isNil(n.sons): result = 0
else: result = len(n.sons) else: result = len(n.sons)
proc sonsLen*(n: PNode): int = proc sonsLen*(n: PNode): int =
if isNil(n.sons): result = 0 if isNil(n.sons): result = 0
else: result = len(n.sons) else: result = len(n.sons)
proc lastSon*(n: PNode): PNode = proc lastSon*(n: PNode): PNode =
result = n.sons[sonsLen(n) - 1] result = n.sons[sonsLen(n) - 1]
proc lastSon*(n: PType): PType = proc lastSon*(n: PType): PType =
result = n.sons[sonsLen(n) - 1] result = n.sons[sonsLen(n) - 1]
proc assignType*(dest, src: PType) = proc assignType*(dest, src: PType) =
dest.kind = src.kind dest.kind = src.kind
dest.flags = src.flags dest.flags = src.flags
dest.callConv = src.callConv dest.callConv = src.callConv
@ -1230,23 +1228,23 @@ proc assignType*(dest, src: PType) =
dest.sym = src.sym dest.sym = src.sym
newSons(dest, sonsLen(src)) newSons(dest, sonsLen(src))
for i in countup(0, sonsLen(src) - 1): dest.sons[i] = src.sons[i] 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) result = newType(t.kind, owner)
assignType(result, t) assignType(result, t)
if keepId: if keepId:
result.id = t.id result.id = t.id
else: else:
when debugIds: registerId(result) when debugIds: registerId(result)
result.sym = t.sym # backend-info should not be copied 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 = newSym(s.kind, s.name, s.owner, s.info)
#result.ast = nil # BUGFIX; was: s.ast which made problems #result.ast = nil # BUGFIX; was: s.ast which made problems
result.typ = s.typ result.typ = s.typ
if keepId: if keepId:
result.id = s.id result.id = s.id
else: else:
result.id = getID() result.id = getID()
when debugIds: registerId(result) when debugIds: registerId(result)
result.flags = s.flags result.flags = s.flags
@ -1273,19 +1271,19 @@ proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo): PSym =
result.annex = s.annex result.annex = s.annex
# XXX once usedGenerics is used, ensure module aliases keep working! # XXX once usedGenerics is used, ensure module aliases keep working!
assert s.usedGenerics == nil assert s.usedGenerics == nil
proc initStrTable*(x: var TStrTable) = proc initStrTable*(x: var TStrTable) =
x.counter = 0 x.counter = 0
newSeq(x.data, StartSize) newSeq(x.data, StartSize)
proc newStrTable*: TStrTable = proc newStrTable*: TStrTable =
initStrTable(result) initStrTable(result)
proc initTable(x: var TTable) = proc initTable(x: var TTable) =
x.counter = 0 x.counter = 0
newSeq(x.data, StartSize) newSeq(x.data, StartSize)
proc initIdTable*(x: var TIdTable) = proc initIdTable*(x: var TIdTable) =
x.counter = 0 x.counter = 0
newSeq(x.data, StartSize) newSeq(x.data, StartSize)
@ -1295,15 +1293,15 @@ proc resetIdTable*(x: var TIdTable) =
setLen(x.data, 0) setLen(x.data, 0)
setLen(x.data, StartSize) setLen(x.data, StartSize)
proc initObjectSet*(x: var TObjectSet) = proc initObjectSet*(x: var TObjectSet) =
x.counter = 0 x.counter = 0
newSeq(x.data, StartSize) newSeq(x.data, StartSize)
proc initIdNodeTable*(x: var TIdNodeTable) = proc initIdNodeTable*(x: var TIdNodeTable) =
x.counter = 0 x.counter = 0
newSeq(x.data, StartSize) newSeq(x.data, StartSize)
proc initNodeTable*(x: var TNodeTable) = proc initNodeTable*(x: var TNodeTable) =
x.counter = 0 x.counter = 0
newSeq(x.data, StartSize) newSeq(x.data, StartSize)
@ -1320,18 +1318,18 @@ proc isGCedMem*(t: PType): bool {.inline.} =
t.kind == tyProc and t.callConv == ccClosure t.kind == tyProc and t.callConv == ccClosure
proc propagateToOwner*(owner, elem: PType) = proc propagateToOwner*(owner, elem: PType) =
const HaveTheirOwnEmpty = {tySequence, tySet} const HaveTheirOwnEmpty = {tySequence, tySet, tyPtr, tyRef, tyProc}
owner.flags = owner.flags + (elem.flags * {tfHasMeta}) owner.flags = owner.flags + (elem.flags * {tfHasMeta})
if tfNotNil in elem.flags: if tfNotNil in elem.flags:
if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvocation}: if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvocation}:
owner.flags.incl tfNotNil owner.flags.incl tfNotNil
elif owner.kind notin HaveTheirOwnEmpty: elif owner.kind notin HaveTheirOwnEmpty:
owner.flags.incl tfNeedsInit owner.flags.incl tfNeedsInit
if tfNeedsInit in elem.flags: if tfNeedsInit in elem.flags:
if owner.kind in HaveTheirOwnEmpty: discard if owner.kind in HaveTheirOwnEmpty: discard
else: owner.flags.incl tfNeedsInit else: owner.flags.incl tfNeedsInit
if elem.isMetaType: if elem.isMetaType:
owner.flags.incl tfHasMeta owner.flags.incl tfHasMeta
@ -1348,15 +1346,15 @@ proc addSonNilAllowed*(father, son: PNode) =
if isNil(father.sons): father.sons = @[] if isNil(father.sons): father.sons = @[]
add(father.sons, son) add(father.sons, son)
proc delSon*(father: PNode, idx: int) = proc delSon*(father: PNode, idx: int) =
if isNil(father.sons): return if isNil(father.sons): return
var length = sonsLen(father) var length = sonsLen(father)
for i in countup(idx, length - 2): father.sons[i] = father.sons[i + 1] for i in countup(idx, length - 2): father.sons[i] = father.sons[i + 1]
setLen(father.sons, length - 1) setLen(father.sons, length - 1)
proc copyNode*(src: PNode): PNode = proc copyNode*(src: PNode): PNode =
# does not copy its sons! # does not copy its sons!
if src == nil: if src == nil:
return nil return nil
result = newNode(src.kind) result = newNode(src.kind)
result.info = src.info result.info = src.info
@ -1373,7 +1371,7 @@ proc copyNode*(src: PNode): PNode =
of nkStrLit..nkTripleStrLit: result.strVal = src.strVal of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
else: discard else: discard
proc shallowCopy*(src: PNode): PNode = proc shallowCopy*(src: PNode): PNode =
# does not copy its sons, but provides space for them: # does not copy its sons, but provides space for them:
if src == nil: return nil if src == nil: return nil
result = newNode(src.kind) result = newNode(src.kind)
@ -1391,9 +1389,9 @@ proc shallowCopy*(src: PNode): PNode =
of nkStrLit..nkTripleStrLit: result.strVal = src.strVal of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
else: newSeq(result.sons, sonsLen(src)) else: newSeq(result.sons, sonsLen(src))
proc copyTree*(src: PNode): PNode = proc copyTree*(src: PNode): PNode =
# copy a whole syntax tree; performs deep copying # copy a whole syntax tree; performs deep copying
if src == nil: if src == nil:
return nil return nil
result = newNode(src.kind) result = newNode(src.kind)
result.info = src.info result.info = src.info
@ -1408,20 +1406,20 @@ proc copyTree*(src: PNode): PNode =
of nkSym: result.sym = src.sym of nkSym: result.sym = src.sym
of nkIdent: result.ident = src.ident of nkIdent: result.ident = src.ident
of nkStrLit..nkTripleStrLit: result.strVal = src.strVal of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
else: else:
newSeq(result.sons, sonsLen(src)) 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]) result.sons[i] = copyTree(src.sons[i])
proc hasSonWith*(n: PNode, kind: TNodeKind): bool = proc hasSonWith*(n: PNode, kind: TNodeKind): bool =
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if n.sons[i].kind == kind: if n.sons[i].kind == kind:
return true return true
result = false result = false
proc hasNilSon*(n: PNode): bool = proc hasNilSon*(n: PNode): bool =
for i in countup(0, safeLen(n) - 1): for i in countup(0, safeLen(n) - 1):
if n.sons[i] == nil: if n.sons[i] == nil:
return true return true
elif hasNilSon(n.sons[i]): elif hasNilSon(n.sons[i]):
return true return true
@ -1435,47 +1433,47 @@ proc containsNode*(n: PNode, kinds: TNodeKinds): bool =
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if n.kind in kinds or containsNode(n.sons[i], kinds): return true 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 case n.kind
of nkEmpty..nkNilLit: result = n.kind == kind of nkEmpty..nkNilLit: result = n.kind == kind
else: else:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if (n.sons[i].kind == kind) or hasSubnodeWith(n.sons[i], kind): if (n.sons[i].kind == kind) or hasSubnodeWith(n.sons[i], kind):
return true return true
result = false result = false
proc replaceSons(n: PNode, oldKind, newKind: TNodeKind) = proc replaceSons(n: PNode, oldKind, newKind: TNodeKind) =
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if n.sons[i].kind == oldKind: n.sons[i].kind = newKind if n.sons[i].kind == oldKind: n.sons[i].kind = newKind
proc sonsNotNil(n: PNode): bool = proc sonsNotNil(n: PNode): bool =
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if n.sons[i] == nil: if n.sons[i] == nil:
return false return false
result = true result = true
proc getInt*(a: PNode): BiggestInt = proc getInt*(a: PNode): BiggestInt =
case a.kind case a.kind
of nkIntLit..nkUInt64Lit: result = a.intVal of nkIntLit..nkUInt64Lit: result = a.intVal
else: else:
internalError(a.info, "getInt") internalError(a.info, "getInt")
result = 0 result = 0
proc getFloat*(a: PNode): BiggestFloat = proc getFloat*(a: PNode): BiggestFloat =
case a.kind case a.kind
of nkFloatLit..nkFloat128Lit: result = a.floatVal of nkFloatLit..nkFloat128Lit: result = a.floatVal
else: else:
internalError(a.info, "getFloat") internalError(a.info, "getFloat")
result = 0.0 result = 0.0
proc getStr*(a: PNode): string = proc getStr*(a: PNode): string =
case a.kind case a.kind
of nkStrLit..nkTripleStrLit: result = a.strVal of nkStrLit..nkTripleStrLit: result = a.strVal
else: else:
internalError(a.info, "getStr") internalError(a.info, "getStr")
result = "" result = ""
proc getStrOrChar*(a: PNode): string = proc getStrOrChar*(a: PNode): string =
case a.kind case a.kind
of nkStrLit..nkTripleStrLit: result = a.strVal of nkStrLit..nkTripleStrLit: result = a.strVal
of nkCharLit..nkUInt64Lit: result = $chr(int(a.intVal)) of nkCharLit..nkUInt64Lit: result = $chr(int(a.intVal))
@ -1483,7 +1481,7 @@ proc getStrOrChar*(a: PNode): string =
internalError(a.info, "getStrOrChar") internalError(a.info, "getStrOrChar")
result = "" result = ""
proc isGenericRoutine*(s: PSym): bool = proc isGenericRoutine*(s: PSym): bool =
case s.kind case s.kind
of skProcKinds: of skProcKinds:
result = sfFromGeneric in s.flags or result = sfFromGeneric in s.flags or

View file

@ -203,9 +203,9 @@ proc mustRehash(length, counter: int): bool =
assert(length > counter) assert(length > counter)
result = (length * 2 < counter * 3) or (length - counter < 4) result = (length * 2 < counter * 3) or (length - counter < 4)
proc spaces(x: int): PRope = proc rspaces(x: int): PRope =
# returns x spaces # returns x spaces
result = toRope(repeatChar(x)) result = toRope(spaces(x))
proc toYamlChar(c: char): string = proc toYamlChar(c: char): string =
case c case c
@ -253,7 +253,7 @@ proc typeToYamlAux(n: PType, marker: var IntSet,
indent, maxRecDepth: int): PRope indent, maxRecDepth: int): PRope
proc strTableToYaml(n: TStrTable, marker: var IntSet, indent: int, proc strTableToYaml(n: TStrTable, marker: var IntSet, indent: int,
maxRecDepth: int): PRope = maxRecDepth: int): PRope =
var istr = spaces(indent + 2) var istr = rspaces(indent + 2)
result = toRope("[") result = toRope("[")
var mycount = 0 var mycount = 0
for i in countup(0, high(n.data)): for i in countup(0, high(n.data)):
@ -262,20 +262,20 @@ proc strTableToYaml(n: TStrTable, marker: var IntSet, indent: int,
appf(result, "$N$1$2", appf(result, "$N$1$2",
[istr, symToYamlAux(n.data[i], marker, indent + 2, maxRecDepth - 1)]) [istr, symToYamlAux(n.data[i], marker, indent + 2, maxRecDepth - 1)])
inc(mycount) inc(mycount)
if mycount > 0: appf(result, "$N$1", [spaces(indent)]) if mycount > 0: appf(result, "$N$1", [rspaces(indent)])
app(result, "]") app(result, "]")
assert(mycount == n.counter) assert(mycount == n.counter)
proc ropeConstr(indent: int, c: openArray[PRope]): PRope = proc ropeConstr(indent: int, c: openArray[PRope]): PRope =
# array of (name, value) pairs # array of (name, value) pairs
var istr = spaces(indent + 2) var istr = rspaces(indent + 2)
result = toRope("{") result = toRope("{")
var i = 0 var i = 0
while i <= high(c): while i <= high(c):
if i > 0: app(result, ",") if i > 0: app(result, ",")
appf(result, "$N$1\"$2\": $3", [istr, c[i], c[i + 1]]) appf(result, "$N$1\"$2\": $3", [istr, c[i], c[i + 1]])
inc(i, 2) inc(i, 2)
appf(result, "$N$1}", [spaces(indent)]) appf(result, "$N$1}", [rspaces(indent)])
proc symToYamlAux(n: PSym, marker: var IntSet, indent: int, proc symToYamlAux(n: PSym, marker: var IntSet, indent: int,
maxRecDepth: int): PRope = maxRecDepth: int): PRope =
@ -310,9 +310,9 @@ proc typeToYamlAux(n: PType, marker: var IntSet, indent: int,
result = toRope("[") result = toRope("[")
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ",") if i > 0: app(result, ",")
appf(result, "$N$1$2", [spaces(indent + 4), typeToYamlAux(n.sons[i], appf(result, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(n.sons[i],
marker, indent + 4, maxRecDepth - 1)]) marker, indent + 4, maxRecDepth - 1)])
appf(result, "$N$1]", [spaces(indent + 2)]) appf(result, "$N$1]", [rspaces(indent + 2)])
else: else:
result = toRope("null") result = toRope("null")
result = ropeConstr(indent, [toRope("kind"), result = ropeConstr(indent, [toRope("kind"),
@ -331,7 +331,7 @@ proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int,
if n == nil: if n == nil:
result = toRope("null") result = toRope("null")
else: else:
var istr = spaces(indent + 2) var istr = rspaces(indent + 2)
result = ropef("{$N$1\"kind\": $2", [istr, makeYamlString($n.kind)]) result = ropef("{$N$1\"kind\": $2", [istr, makeYamlString($n.kind)])
if maxRecDepth != 0: if maxRecDepth != 0:
appf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)]) appf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)])
@ -359,12 +359,12 @@ proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int,
appf(result, ",$N$1\"sons\": [", [istr]) appf(result, ",$N$1\"sons\": [", [istr])
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ",") if i > 0: app(result, ",")
appf(result, "$N$1$2", [spaces(indent + 4), treeToYamlAux(n.sons[i], appf(result, "$N$1$2", [rspaces(indent + 4), treeToYamlAux(n.sons[i],
marker, indent + 4, maxRecDepth - 1)]) marker, indent + 4, maxRecDepth - 1)])
appf(result, "$N$1]", [istr]) appf(result, "$N$1]", [istr])
appf(result, ",$N$1\"typ\": $2", appf(result, ",$N$1\"typ\": $2",
[istr, typeToYamlAux(n.typ, marker, indent + 2, maxRecDepth)]) [istr, typeToYamlAux(n.typ, marker, indent + 2, maxRecDepth)])
appf(result, "$N$1}", [spaces(indent)]) appf(result, "$N$1}", [rspaces(indent)])
proc treeToYaml(n: PNode, indent: int = 0, maxRecDepth: int = - 1): PRope = proc treeToYaml(n: PNode, indent: int = 0, maxRecDepth: int = - 1): PRope =
var marker = initIntSet() var marker = initIntSet()
@ -408,7 +408,7 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int;
if n == nil: if n == nil:
result = toRope("null") result = toRope("null")
else: else:
var istr = spaces(indent + 2) var istr = rspaces(indent + 2)
result = ropef("{$N$1\"kind\": $2", result = ropef("{$N$1\"kind\": $2",
[istr, makeYamlString($n.kind)]) [istr, makeYamlString($n.kind)])
if maxRecDepth != 0: if maxRecDepth != 0:
@ -440,11 +440,11 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int;
appf(result, ",$N$1\"sons\": [", [istr]) appf(result, ",$N$1\"sons\": [", [istr])
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ",") if i > 0: app(result, ",")
appf(result, "$N$1$2", [spaces(indent + 4), debugTree(n.sons[i], appf(result, "$N$1$2", [rspaces(indent + 4), debugTree(n.sons[i],
indent + 4, maxRecDepth - 1, renderType)]) indent + 4, maxRecDepth - 1, renderType)])
appf(result, "$N$1]", [istr]) appf(result, "$N$1]", [istr])
appf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)]) appf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)])
appf(result, "$N$1}", [spaces(indent)]) appf(result, "$N$1}", [rspaces(indent)])
proc debug(n: PSym) = proc debug(n: PSym) =
if n == nil: if n == nil:
@ -681,9 +681,8 @@ proc initIdentIter(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym =
else: result = nextIdentIter(ti, tab) else: result = nextIdentIter(ti, tab)
proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym = proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym =
var h, start: THash var h = ti.h and high(tab.data)
h = ti.h and high(tab.data) var start = h
start = h
result = tab.data[h] result = tab.data[h]
while result != nil: while result != nil:
if result.name.id == ti.name.id: break if result.name.id == ti.name.id: break

View file

@ -135,14 +135,14 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): PRope =
elif ccgIntroducedPtr(param): elif ccgIntroducedPtr(param):
initLocExpr(p, n, a) initLocExpr(p, n, a)
result = addrLoc(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: n.kind == nkHiddenAddr:
initLocExprSingleUse(p, n.sons[0], a) initLocExprSingleUse(p, n.sons[0], a)
# if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still # 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. # means '*T'. See posix.nim for lots of examples that do that in the wild.
let callee = call.sons[0] let callee = call.sons[0]
if callee.kind == nkSym and 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 != {}: {lfHeader, lfNoDecl} * callee.sym.loc.flags != {}:
result = addrLoc(a) result = addrLoc(a)
else: else:
@ -192,7 +192,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
var op: TLoc var op: TLoc
initLocExpr(p, ri.sons[0], op) initLocExpr(p, ri.sons[0], op)
var pl: PRope var pl: PRope
var typ = skipTypes(ri.sons[0].typ, abstractInst) var typ = skipTypes(ri.sons[0].typ, abstractInst)
assert(typ.kind == tyProc) assert(typ.kind == tyProc)
var length = sonsLen(ri) var length = sonsLen(ri)
@ -204,7 +204,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
else: else:
app(pl, genArgNoParam(p, ri.sons[i])) app(pl, genArgNoParam(p, ri.sons[i]))
if i < length - 1: app(pl, ~", ") if i < length - 1: app(pl, ~", ")
template genCallPattern {.dirty.} = template genCallPattern {.dirty.} =
lineF(p, cpsStmts, callPattern & ";$n", op.r, pl, pl.addComma, rawProc) 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) let typ = skipTypes(ri.sons[0].typ, abstractInst)
if pat[i+1] == '+': result.app genArgNoParam(p, ri.sons[0]) if pat[i+1] == '+': result.app genArgNoParam(p, ri.sons[0])
result.app(~"(") 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: for k in j+1 .. < ri.len:
result.app(~", ") result.app(~", ")
result.app genOtherArg(p, ri, k, typ) result.app genOtherArg(p, ri, k, typ)

View file

@ -56,11 +56,6 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): PRope =
case skipTypes(ty, abstractVarRange).kind case skipTypes(ty, abstractVarRange).kind
of tyChar, tyNil: of tyChar, tyNil:
result = intLiteral(n.intVal) result = intLiteral(n.intVal)
of tyInt:
if n.intVal >= low(int32) and n.intVal <= high(int32):
result = int32Literal(int32(n.intVal))
else:
result = intLiteral(n.intVal)
of tyBool: of tyBool:
if n.intVal != 0: result = ~"NIM_TRUE" if n.intVal != 0: result = ~"NIM_TRUE"
else: result = ~"NIM_FALSE" else: result = ~"NIM_FALSE"
@ -89,7 +84,7 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): PRope =
var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId) var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId)
if id == gBackendId: if id == gBackendId:
# string literal not found in the cache: # string literal not found in the cache:
result = ropecg(p.module, "((#NimStringDesc*) &$1)", result = ropecg(p.module, "((#NimStringDesc*) &$1)",
[getStrLit(p.module, n.strVal)]) [getStrLit(p.module, n.strVal)])
else: else:
result = ropecg(p.module, "((#NimStringDesc*) &TMP$1)", [toRope(id)]) result = ropecg(p.module, "((#NimStringDesc*) &TMP$1)", [toRope(id)])
@ -158,7 +153,7 @@ proc getStorageLoc(n: PNode): TStorageLoc =
of skVar, skForVar, skResult, skLet: of skVar, skForVar, skResult, skLet:
if sfGlobal in n.sym.flags: result = OnHeap if sfGlobal in n.sym.flags: result = OnHeap
else: result = OnStack else: result = OnStack
of skConst: of skConst:
if sfGlobal in n.sym.flags: result = OnHeap if sfGlobal in n.sym.flags: result = OnHeap
else: result = OnUnknown else: result = OnUnknown
else: result = OnUnknown else: result = OnUnknown
@ -236,7 +231,7 @@ proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
for i in 0 .. <t.len: for i in 0 .. <t.len:
let t = t.sons[i] let t = t.sons[i]
let field = ropef("Field$1", i.toRope) let field = ropef("Field$1", i.toRope)
genAssignment(p, optAsgnLoc(dest, t, field), genAssignment(p, optAsgnLoc(dest, t, field),
optAsgnLoc(src, t, field), newflags) optAsgnLoc(src, t, field), newflags)
proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags, proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags,
@ -252,20 +247,20 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags,
case t.kind case t.kind
of nkSym: of nkSym:
let field = t.sym let field = t.sym
genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.r), genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.r),
optAsgnLoc(src, field.typ, field.loc.r), newflags) optAsgnLoc(src, field.typ, field.loc.r), newflags)
of nkRecList: of nkRecList:
for child in items(t): genOptAsgnObject(p, dest, src, newflags, child) for child in items(t): genOptAsgnObject(p, dest, src, newflags, child)
else: discard else: discard
proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
# Consider: # Consider:
# type TMyFastString {.shallow.} = string # type TMyFastString {.shallow.} = string
# Due to the implementation of pragmas this would end up to set the # Due to the implementation of pragmas this would end up to set the
# tfShallow flag for the built-in string type too! So we check only # tfShallow flag for the built-in string type too! So we check only
# here for this flag, where it is reasonably safe to do so # here for this flag, where it is reasonably safe to do so
# (for objects, etc.): # (for objects, etc.):
if needToCopy notin flags or if needToCopy notin flags or
tfShallow in skipTypes(dest.t, abstractVarRange).flags: tfShallow in skipTypes(dest.t, abstractVarRange).flags:
if dest.s == OnStack or not usesNativeGC(): if dest.s == OnStack or not usesNativeGC():
useStringh(p.module) useStringh(p.module)
@ -510,7 +505,7 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
var storage: PRope var storage: PRope
var size = getSize(t) var size = getSize(t)
if size < platform.intSize: if size < platform.intSize:
storage = toRope("NI") storage = toRope("NI")
else: else:
storage = getTypeDesc(p.module, t) storage = getTypeDesc(p.module, t)
var tmp = getTempName() var tmp = getTempName()
@ -547,7 +542,7 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
"(($4)($1) - ($4)($2))", # SubF64 "(($4)($1) - ($4)($2))", # SubF64
"(($4)($1) * ($4)($2))", # MulF64 "(($4)($1) * ($4)($2))", # MulF64
"(($4)($1) / ($4)($2))", # DivF64 "(($4)($1) / ($4)($2))", # DivF64
"($4)((NU$3)($1) >> (NU$3)($2))", # ShrI "($4)((NU$3)($1) >> (NU$3)($2))", # ShrI
"($4)((NU$3)($1) << (NU$3)($2))", # ShlI "($4)((NU$3)($1) << (NU$3)($2))", # ShlI
"($4)($1 & $2)", # BitandI "($4)($1 & $2)", # BitandI
@ -617,7 +612,7 @@ proc genEqProc(p: BProc, e: PNode, d: var TLoc) =
initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], b) initLocExpr(p, e.sons[2], b)
if a.t.callConv == ccClosure: if a.t.callConv == ccClosure:
putIntoDest(p, d, e.typ, putIntoDest(p, d, e.typ,
ropef("($1.ClPrc == $2.ClPrc && $1.ClEnv == $2.ClEnv)", [ ropef("($1.ClPrc == $2.ClPrc && $1.ClEnv == $2.ClEnv)", [
rdLoc(a), rdLoc(b)])) rdLoc(a), rdLoc(b)]))
else: else:
@ -721,7 +716,7 @@ template inheritLocation(d: var TLoc, a: TLoc) =
if d.k == locNone: d.s = a.s if d.k == locNone: d.s = a.s
if d.heapRoot == nil: if d.heapRoot == nil:
d.heapRoot = if a.heapRoot != nil: a.heapRoot else: a.r d.heapRoot = if a.heapRoot != nil: a.heapRoot else: a.r
proc genRecordFieldAux(p: BProc, e: PNode, d, a: var TLoc): PType = proc genRecordFieldAux(p: BProc, e: PNode, d, a: var TLoc): PType =
initLocExpr(p, e.sons[0], a) initLocExpr(p, e.sons[0], a)
if e.sons[1].kind != nkSym: internalError(e.info, "genRecordFieldAux") if e.sons[1].kind != nkSym: internalError(e.info, "genRecordFieldAux")
@ -954,7 +949,7 @@ proc genEcho(p: BProc, n: PNode) =
initLocExpr(p, n.sons[i], a) initLocExpr(p, n.sons[i], a)
appf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)]) appf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)])
linefmt(p, cpsStmts, "printf($1$2);$n", linefmt(p, cpsStmts, "printf($1$2);$n",
makeCString(repeatStr(n.len, "%s") & tnl), args) makeCString(repeat("%s", n.len) & tnl), args)
proc gcUsage(n: PNode) = proc gcUsage(n: PNode) =
if gSelectedGC == gcNone: message(n.info, warnGcMem, n.renderTree) if gSelectedGC == gcNone: message(n.info, warnGcMem, n.renderTree)
@ -1061,7 +1056,7 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
genAssignment(p, dest, b, {needToCopy, afDestIsNil}) genAssignment(p, dest, b, {needToCopy, afDestIsNil})
gcUsage(e) gcUsage(e)
proc genReset(p: BProc, n: PNode) = proc genReset(p: BProc, n: PNode) =
var a: TLoc var a: TLoc
initLocExpr(p, n.sons[1], a) initLocExpr(p, n.sons[1], a)
linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n", linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
@ -1120,14 +1115,14 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: PRope) =
else: else:
call.r = ropecg(p.module, "($1) #newSeq($2, $3)", args) call.r = ropecg(p.module, "($1) #newSeq($2, $3)", args)
genAssignment(p, dest, call, {needToKeepAlive}) genAssignment(p, dest, call, {needToKeepAlive})
proc genNewSeq(p: BProc, e: PNode) = proc genNewSeq(p: BProc, e: PNode) =
var a, b: TLoc var a, b: TLoc
initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], b) initLocExpr(p, e.sons[2], b)
genNewSeqAux(p, a, b.rdLoc) genNewSeqAux(p, a, b.rdLoc)
gcUsage(e) gcUsage(e)
proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
var tmp: TLoc var tmp: TLoc
var t = e.typ.skipTypes(abstractInst) var t = e.typ.skipTypes(abstractInst)
@ -1168,7 +1163,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
d = tmp d = tmp
else: else:
genAssignment(p, d, tmp, {}) genAssignment(p, d, tmp, {})
proc genSeqConstr(p: BProc, t: PNode, d: var TLoc) = proc genSeqConstr(p: BProc, t: PNode, d: var TLoc) =
var arr: TLoc var arr: TLoc
if d.k == locNone: if d.k == locNone:
@ -1192,7 +1187,7 @@ proc genArrToSeq(p: BProc, t: PNode, d: var TLoc) =
getTemp(p, t.typ, d) getTemp(p, t.typ, d)
# generate call to newSeq before adding the elements per hand: # generate call to newSeq before adding the elements per hand:
var L = int(lengthOrd(t.sons[1].typ)) var L = int(lengthOrd(t.sons[1].typ))
genNewSeqAux(p, d, intLiteral(L)) genNewSeqAux(p, d, intLiteral(L))
initLocExpr(p, t.sons[1], a) initLocExpr(p, t.sons[1], a)
for i in countup(0, L - 1): for i in countup(0, L - 1):
@ -1202,7 +1197,7 @@ proc genArrToSeq(p: BProc, t: PNode, d: var TLoc) =
initLoc(arr, locExpr, elemType(skipTypes(t.sons[1].typ, abstractInst)), a.s) initLoc(arr, locExpr, elemType(skipTypes(t.sons[1].typ, abstractInst)), a.s)
arr.r = rfmt(nil, "$1[$2]", rdLoc(a), intLiteral(i)) arr.r = rfmt(nil, "$1[$2]", rdLoc(a), intLiteral(i))
genAssignment(p, elem, arr, {afDestIsNil, needToCopy}) genAssignment(p, elem, arr, {afDestIsNil, needToCopy})
proc genNewFinalize(p: BProc, e: PNode) = proc genNewFinalize(p: BProc, e: PNode) =
var var
a, b, f: TLoc a, b, f: TLoc
@ -1258,7 +1253,7 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
app(r, ~".Sup") app(r, ~".Sup")
t = skipTypes(t.sons[0], typedescInst) t = skipTypes(t.sons[0], typedescInst)
if isObjLackingTypeField(t): if isObjLackingTypeField(t):
globalError(x.info, errGenerated, globalError(x.info, errGenerated,
"no 'of' operator available for pure objects") "no 'of' operator available for pure objects")
if nilCheck != nil: if nilCheck != nil:
r = rfmt(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r)) r = rfmt(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r))
@ -1275,7 +1270,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
var t = skipTypes(e.sons[1].typ, abstractVarRange) var t = skipTypes(e.sons[1].typ, abstractVarRange)
case t.kind case t.kind
of tyInt..tyInt64, tyUInt..tyUInt64: of tyInt..tyInt64, tyUInt..tyUInt64:
putIntoDest(p, d, e.typ, putIntoDest(p, d, e.typ,
ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)])) ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)]))
of tyFloat..tyFloat128: of tyFloat..tyFloat128:
putIntoDest(p, d, e.typ, ropecg(p.module, "#reprFloat($1)", [rdLoc(a)])) putIntoDest(p, d, e.typ, ropecg(p.module, "#reprFloat($1)", [rdLoc(a)]))
@ -1298,13 +1293,13 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
putIntoDest(p, b, e.typ, ropef("$1, $1Len0", [rdLoc(a)])) putIntoDest(p, b, e.typ, ropef("$1, $1Len0", [rdLoc(a)]))
of tyString, tySequence: of tyString, tySequence:
putIntoDest(p, b, e.typ, putIntoDest(p, b, e.typ,
ropef("$1->data, $1->$2", [rdLoc(a), lenField(p)])) ropef("$1->data, $1->$2", [rdLoc(a), lenField(p)]))
of tyArray, tyArrayConstr: of tyArray, tyArrayConstr:
putIntoDest(p, b, e.typ, putIntoDest(p, b, e.typ,
ropef("$1, $2", [rdLoc(a), toRope(lengthOrd(a.t))])) ropef("$1, $2", [rdLoc(a), toRope(lengthOrd(a.t))]))
else: internalError(e.sons[0].info, "genRepr()") else: internalError(e.sons[0].info, "genRepr()")
putIntoDest(p, d, e.typ, putIntoDest(p, d, e.typ,
ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b), ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b),
genTypeInfo(p.module, elemType(t))])) genTypeInfo(p.module, elemType(t))]))
of tyCString, tyArray, tyArrayConstr, tyRef, tyPtr, tyPointer, tyNil, of tyCString, tyArray, tyArrayConstr, tyRef, tyPtr, tyPointer, tyNil,
@ -1433,7 +1428,7 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) =
# do not emit the set, but generate a bunch of comparisons; and if we do # do not emit the set, but generate a bunch of comparisons; and if we do
# so, we skip the unnecessary range check: This is a semantical extension # so, we skip the unnecessary range check: This is a semantical extension
# that code now relies on. :-/ XXX # that code now relies on. :-/ XXX
let ea = if e.sons[2].kind in {nkChckRange, nkChckRange64}: let ea = if e.sons[2].kind in {nkChckRange, nkChckRange64}:
e.sons[2].sons[0] e.sons[2].sons[0]
else: else:
e.sons[2] e.sons[2]
@ -1518,7 +1513,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
initLocExpr(p, e.sons[2], b) initLocExpr(p, e.sons[2], b)
if d.k == locNone: getTemp(p, a.t, d) if d.k == locNone: getTemp(p, a.t, d)
lineF(p, cpsStmts, lineF(p, cpsStmts,
"for ($1 = 0; $1 < $2; $1++) $n" & "for ($1 = 0; $1 < $2; $1++) $n" &
" $3[$1] = $4[$1] $6 $5[$1];$n", [ " $3[$1] = $4[$1] $6 $5[$1];$n", [
rdLoc(i), toRope(size), rdLoc(d), rdLoc(a), rdLoc(b), rdLoc(i), toRope(size), rdLoc(d), rdLoc(a), rdLoc(b),
toRope(lookupOpr[op])]) toRope(lookupOpr[op])])
@ -1549,7 +1544,7 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
proc genCast(p: BProc, e: PNode, d: var TLoc) = proc genCast(p: BProc, e: PNode, d: var TLoc) =
const floatTypes = {tyFloat..tyFloat128} const floatTypes = {tyFloat..tyFloat128}
let let
destt = skipTypes(e.typ, abstractRange) destt = skipTypes(e.typ, abstractRange)
srct = skipTypes(e.sons[1].typ, abstractRange) srct = skipTypes(e.sons[1].typ, abstractRange)
if destt.kind in floatTypes or srct.kind in floatTypes: if destt.kind in floatTypes or srct.kind in floatTypes:
@ -1656,7 +1651,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
of mRepr: genRepr(p, e, d) of mRepr: genRepr(p, e, d)
of mGetTypeInfo: genGetTypeInfo(p, e, d) of mGetTypeInfo: genGetTypeInfo(p, e, d)
of mSwap: genSwap(p, e, d) of mSwap: genSwap(p, e, d)
of mUnaryLt: of mUnaryLt:
if optOverflowCheck notin p.options: unaryExpr(p, e, d, "($1 - 1)") if optOverflowCheck notin p.options: unaryExpr(p, e, d, "($1 - 1)")
else: unaryExpr(p, e, d, "#subInt($1, 1)") else: unaryExpr(p, e, d, "#subInt($1, 1)")
of mPred: of mPred:
@ -1830,10 +1825,10 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
proc isConstClosure(n: PNode): bool {.inline.} = proc isConstClosure(n: PNode): bool {.inline.} =
result = n.sons[0].kind == nkSym and isRoutine(n.sons[0].sym) and result = n.sons[0].kind == nkSym and isRoutine(n.sons[0].sym) and
n.sons[1].kind == nkNilLit n.sons[1].kind == nkNilLit
proc genClosure(p: BProc, n: PNode, d: var TLoc) = proc genClosure(p: BProc, n: PNode, d: var TLoc) =
assert n.kind == nkClosure assert n.kind == nkClosure
if isConstClosure(n): if isConstClosure(n):
inc(p.labels) inc(p.labels)
var tmp = con("LOC", toRope(p.labels)) var tmp = con("LOC", toRope(p.labels))
@ -1920,7 +1915,7 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) =
if isRef: if isRef:
# it can happen that we end up generating '&&x->Sup' here, so we pack # it can happen that we end up generating '&&x->Sup' here, so we pack
# the '&x->Sup' into a temporary and then those address is taken # the '&x->Sup' into a temporary and then those address is taken
# (see bug #837). However sometimes using a temporary is not correct: # (see bug #837). However sometimes using a temporary is not correct:
# init(TFigure(my)) # where it is passed to a 'var TFigure'. We test # init(TFigure(my)) # where it is passed to a 'var TFigure'. We test
# this by ensuring the destination is also a pointer: # this by ensuring the destination is also a pointer:
if d.k == locNone and skipTypes(n.typ, abstractInst).kind in {tyRef, tyPtr, tyVar}: if d.k == locNone and skipTypes(n.typ, abstractInst).kind in {tyRef, tyPtr, tyVar}:
@ -1937,13 +1932,13 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
discard getTypeDesc(p.module, t) # so that any fields are initialized discard getTypeDesc(p.module, t) # so that any fields are initialized
var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId) var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId)
var tmp = con("TMP", toRope(id)) var tmp = con("TMP", toRope(id))
if id == gBackendId: if id == gBackendId:
# expression not found in the cache: # expression not found in the cache:
inc(gBackendId) inc(gBackendId)
appf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", appf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n",
[getTypeDesc(p.module, t), tmp, genConstExpr(p, n)]) [getTypeDesc(p.module, t), tmp, genConstExpr(p, n)])
if d.k == locNone: if d.k == locNone:
fillLoc(d, locData, t, tmp, OnHeap) fillLoc(d, locData, t, tmp, OnHeap)
else: else:
@ -1984,7 +1979,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
internalError n.info, "expr: var not init " & sym.name.s & "_" & $sym.id internalError n.info, "expr: var not init " & sym.name.s & "_" & $sym.id
if sfThread in sym.flags: if sfThread in sym.flags:
accessThreadLocalVar(p, sym) accessThreadLocalVar(p, sym)
if emulatedThreadVars(): if emulatedThreadVars():
putIntoDest(p, d, sym.loc.t, con("NimTV->", sym.loc.r)) putIntoDest(p, d, sym.loc.t, con("NimTV->", sym.loc.r))
else: else:
putLocIntoDest(p, d, sym.loc) putLocIntoDest(p, d, sym.loc)
@ -2001,7 +1996,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
#echo "FAILED FOR PRCO ", p.prc.name.s #echo "FAILED FOR PRCO ", p.prc.name.s
#debug p.prc.typ.n #debug p.prc.typ.n
#echo renderTree(p.prc.ast, {renderIds}) #echo renderTree(p.prc.ast, {renderIds})
internalError(n.info, "expr: param not init " & sym.name.s & "_" & $sym.id) internalError(n.info, "expr: param not init " & sym.name.s & "_" & $sym.id)
putLocIntoDest(p, d, sym.loc) putLocIntoDest(p, d, sym.loc)
else: internalError(n.info, "expr(" & $sym.kind & "); unknown symbol") else: internalError(n.info, "expr(" & $sym.kind & "); unknown symbol")
of nkNilLit: of nkNilLit:
@ -2101,9 +2096,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
# we have to emit the type information for object types here to support # we have to emit the type information for object types here to support
# separate compilation: # separate compilation:
genTypeSection(p.module, n) genTypeSection(p.module, n)
of nkCommentStmt, nkIteratorDef, nkIncludeStmt, of nkCommentStmt, nkIteratorDef, nkIncludeStmt,
nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
nkFromStmt, nkTemplateDef, nkMacroDef: nkFromStmt, nkTemplateDef, nkMacroDef:
discard discard
of nkPragma: genPragma(p, n) of nkPragma: genPragma(p, n)
of nkPragmaBlock: expr(p, n.lastSon, d) of nkPragmaBlock: expr(p, n.lastSon, d)
@ -2118,8 +2113,8 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
sfDeadCodeElim notin getModule(prc).flags) or sfDeadCodeElim notin getModule(prc).flags) or
({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or
(sfExportc in prc.flags and lfExportLib in prc.loc.flags) or (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or
(prc.kind == skMethod): (prc.kind == skMethod):
# we have not only the header: # we have not only the header:
if prc.getBody.kind != nkEmpty or lfDynamicLib in prc.loc.flags: if prc.getBody.kind != nkEmpty or lfDynamicLib in prc.loc.flags:
genProc(p.module, prc) genProc(p.module, prc)
of nkParForStmt: genParForStmt(p, n) of nkParForStmt: genParForStmt(p, n)
@ -2142,7 +2137,7 @@ proc genConstSimpleList(p: BProc, n: PNode): PRope =
proc genConstSeq(p: BProc, n: PNode, t: PType): PRope = proc genConstSeq(p: BProc, n: PNode, t: PType): PRope =
var data = ropef("{{$1, $1}", n.len.toRope) var data = ropef("{{$1, $1}", n.len.toRope)
if n.len > 0: if n.len > 0:
# array part needs extra curlies: # array part needs extra curlies:
data.app(", {") data.app(", {")
for i in countup(0, n.len - 1): for i in countup(0, n.len - 1):
@ -2150,14 +2145,14 @@ proc genConstSeq(p: BProc, n: PNode, t: PType): PRope =
data.app genConstExpr(p, n.sons[i]) data.app genConstExpr(p, n.sons[i])
data.app("}") data.app("}")
data.app("}") data.app("}")
inc(gBackendId) inc(gBackendId)
result = con("CNSTSEQ", gBackendId.toRope) result = con("CNSTSEQ", gBackendId.toRope)
appcg(p.module, cfsData, appcg(p.module, cfsData,
"NIM_CONST struct {$n" & "NIM_CONST struct {$n" &
" #TGenericSeq Sup;$n" & " #TGenericSeq Sup;$n" &
" $1 data[$2];$n" & " $1 data[$2];$n" &
"} $3 = $4;$n", [ "} $3 = $4;$n", [
getTypeDesc(p.module, t.sons[0]), n.len.toRope, result, data]) getTypeDesc(p.module, t.sons[0]), n.len.toRope, result, data])

View file

@ -9,31 +9,31 @@
# This module declares some helpers for the C code generator. # This module declares some helpers for the C code generator.
import import
ast, astalgo, ropes, lists, hashes, strutils, types, msgs, wordrecg, ast, astalgo, ropes, lists, hashes, strutils, types, msgs, wordrecg,
platform, trees platform, trees
proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode = proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
case n.kind case n.kind
of nkStmtList: of nkStmtList:
for i in 0 .. < n.len: for i in 0 .. < n.len:
result = getPragmaStmt(n[i], w) result = getPragmaStmt(n[i], w)
if result != nil: break if result != nil: break
of nkPragma: of nkPragma:
for i in 0 .. < n.len: for i in 0 .. < n.len:
if whichPragma(n[i]) == w: return n[i] if whichPragma(n[i]) == w: return n[i]
else: discard else: discard
proc stmtsContainPragma*(n: PNode, w: TSpecialWord): bool = proc stmtsContainPragma*(n: PNode, w: TSpecialWord): bool =
result = getPragmaStmt(n, w) != nil result = getPragmaStmt(n, w) != nil
proc hashString*(s: string): BiggestInt = proc hashString*(s: string): BiggestInt =
# has to be the same algorithm as system.hashString! # has to be the same algorithm as system.hashString!
if CPU[targetCPU].bit == 64: if CPU[targetCPU].bit == 64:
# we have to use the same bitwidth # we have to use the same bitwidth
# as the target CPU # as the target CPU
var b = 0'i64 var b = 0'i64
for i in countup(0, len(s) - 1): for i in countup(0, len(s) - 1):
b = b +% ord(s[i]) b = b +% ord(s[i])
b = b +% `shl`(b, 10) b = b +% `shl`(b, 10)
b = b xor `shr`(b, 6) b = b xor `shr`(b, 6)
@ -41,9 +41,9 @@ proc hashString*(s: string): BiggestInt =
b = b xor `shr`(b, 11) b = b xor `shr`(b, 11)
b = b +% `shl`(b, 15) b = b +% `shl`(b, 15)
result = b result = b
else: else:
var a = 0'i32 var a = 0'i32
for i in countup(0, len(s) - 1): for i in countup(0, len(s) - 1):
a = a +% ord(s[i]).int32 a = a +% ord(s[i]).int32
a = a +% `shl`(a, 10'i32) a = a +% `shl`(a, 10'i32)
a = a xor `shr`(a, 6'i32) a = a xor `shr`(a, 6'i32)
@ -52,11 +52,11 @@ proc hashString*(s: string): BiggestInt =
a = a +% `shl`(a, 15'i32) a = a +% `shl`(a, 15'i32)
result = a result = a
var var
gTypeTable: array[TTypeKind, TIdTable] gTypeTable: array[TTypeKind, TIdTable]
gCanonicalTypes: array[TTypeKind, PType] gCanonicalTypes: array[TTypeKind, PType]
proc initTypeTables() = proc initTypeTables() =
for i in countup(low(TTypeKind), high(TTypeKind)): initIdTable(gTypeTable[i]) for i in countup(low(TTypeKind), high(TTypeKind)): initIdTable(gTypeTable[i])
proc resetCaches* = proc resetCaches* =
@ -67,7 +67,7 @@ proc resetCaches* =
when false: when false:
proc echoStats*() = proc echoStats*() =
for i in countup(low(TTypeKind), high(TTypeKind)): for i in countup(low(TTypeKind), high(TTypeKind)):
echo i, " ", gTypeTable[i].counter echo i, " ", gTypeTable[i].counter
proc slowSearch(key: PType; k: TTypeKind): PType = proc slowSearch(key: PType; k: TTypeKind): PType =
@ -78,28 +78,28 @@ proc slowSearch(key: PType; k: TTypeKind): PType =
if idTableHasObjectAsKey(gTypeTable[k], key): return key if idTableHasObjectAsKey(gTypeTable[k], key): return key
for h in countup(0, high(gTypeTable[k].data)): for h in countup(0, high(gTypeTable[k].data)):
var t = PType(gTypeTable[k].data[h].key) var t = PType(gTypeTable[k].data[h].key)
if t != nil and sameBackendType(t, key): if t != nil and sameBackendType(t, key):
return t return t
idTablePut(gTypeTable[k], key, key) idTablePut(gTypeTable[k], key, key)
result = key result = key
proc getUniqueType*(key: PType): PType = proc getUniqueType*(key: PType): PType =
# this is a hotspot in the compiler! # this is a hotspot in the compiler!
if key == nil: return if key == nil: return
var k = key.kind var k = key.kind
case k case k
of tyBool, tyChar, tyInt..tyUInt64: of tyBool, tyChar, tyInt..tyUInt64:
# no canonicalization for integral types, so that e.g. ``pid_t`` is # no canonicalization for integral types, so that e.g. ``pid_t`` is
# produced instead of ``NI``. # produced instead of ``NI``.
result = key result = key
of tyEmpty, tyNil, tyExpr, tyStmt, tyPointer, tyString, of tyEmpty, tyNil, tyExpr, tyStmt, tyPointer, tyString,
tyCString, tyNone, tyBigNum: tyCString, tyNone, tyBigNum:
result = gCanonicalTypes[k] result = gCanonicalTypes[k]
if result == nil: if result == nil:
gCanonicalTypes[k] = key gCanonicalTypes[k] = key
result = key result = key
of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr, tyFieldAccessor: of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr, tyFieldAccessor:
internalError("GetUniqueType") internalError("getUniqueType")
of tyDistinct: of tyDistinct:
if key.deepCopy != nil: result = key if key.deepCopy != nil: result = key
else: result = getUniqueType(lastSon(key)) else: result = getUniqueType(lastSon(key))
@ -127,21 +127,21 @@ proc getUniqueType*(key: PType): PType =
if tfFromGeneric notin key.flags: if tfFromGeneric notin key.flags:
# fast case; lookup per id suffices: # fast case; lookup per id suffices:
result = PType(idTableGet(gTypeTable[k], key)) result = PType(idTableGet(gTypeTable[k], key))
if result == nil: if result == nil:
idTablePut(gTypeTable[k], key, key) idTablePut(gTypeTable[k], key, key)
result = key result = key
else: else:
# ugly slow case: need to compare by structure # ugly slow case: need to compare by structure
if idTableHasObjectAsKey(gTypeTable[k], key): return key if idTableHasObjectAsKey(gTypeTable[k], key): return key
for h in countup(0, high(gTypeTable[k].data)): for h in countup(0, high(gTypeTable[k].data)):
var t = PType(gTypeTable[k].data[h].key) var t = PType(gTypeTable[k].data[h].key)
if t != nil and sameType(t, key): if t != nil and sameBackendType(t, key):
return t return t
idTablePut(gTypeTable[k], key, key) idTablePut(gTypeTable[k], key, key)
result = key result = key
of tyEnum: of tyEnum:
result = PType(idTableGet(gTypeTable[k], key)) result = PType(idTableGet(gTypeTable[k], key))
if result == nil: if result == nil:
idTablePut(gTypeTable[k], key, key) idTablePut(gTypeTable[k], key, key)
result = key result = key
of tyProc: of tyProc:
@ -151,16 +151,16 @@ proc getUniqueType*(key: PType): PType =
# ugh, we need the canon here: # ugh, we need the canon here:
result = slowSearch(key, k) result = slowSearch(key, k)
proc tableGetType*(tab: TIdTable, key: PType): RootRef = proc tableGetType*(tab: TIdTable, key: PType): RootRef =
# returns nil if we need to declare this type # returns nil if we need to declare this type
result = idTableGet(tab, key) result = idTableGet(tab, key)
if (result == nil) and (tab.counter > 0): if (result == nil) and (tab.counter > 0):
# we have to do a slow linear search because types may need # we have to do a slow linear search because types may need
# to be compared by their structure: # to be compared by their structure:
for h in countup(0, high(tab.data)): for h in countup(0, high(tab.data)):
var t = PType(tab.data[h].key) var t = PType(tab.data[h].key)
if t != nil: if t != nil:
if sameType(t, key): if sameType(t, key):
return tab.data[h].val return tab.data[h].val
proc makeSingleLineCString*(s: string): string = proc makeSingleLineCString*(s: string): string =
@ -193,16 +193,16 @@ proc mangle*(name: string): string =
else: else:
add(result, "HEX" & toHex(ord(c), 2)) add(result, "HEX" & toHex(ord(c), 2))
proc makeLLVMString*(s: string): PRope = proc makeLLVMString*(s: string): PRope =
const MaxLineLength = 64 const MaxLineLength = 64
result = nil result = nil
var res = "c\"" var res = "c\""
for i in countup(0, len(s) - 1): for i in countup(0, len(s) - 1):
if (i + 1) mod MaxLineLength == 0: if (i + 1) mod MaxLineLength == 0:
app(result, toRope(res)) app(result, toRope(res))
setLen(res, 0) setLen(res, 0)
case s[i] case s[i]
of '\0'..'\x1F', '\x80'..'\xFF', '\"', '\\': of '\0'..'\x1F', '\x80'..'\xFF', '\"', '\\':
add(res, '\\') add(res, '\\')
add(res, toHex(ord(s[i]), 2)) add(res, toHex(ord(s[i]), 2))
else: add(res, s[i]) else: add(res, s[i])

View file

@ -164,6 +164,7 @@ proc packObject(x: PNode, typ: PType, res: pointer) =
let field = getField(typ.n, i) let field = getField(typ.n, i)
pack(it, field.typ, res +! field.offset) pack(it, field.typ, res +! field.offset)
else: else:
# XXX: todo
globalError(x.info, "cannot pack unnamed tuple") globalError(x.info, "cannot pack unnamed tuple")
const maxPackDepth = 20 const maxPackDepth = 20

View file

@ -10,7 +10,7 @@
## Template evaluation engine. Now hygienic. ## Template evaluation engine. Now hygienic.
import import
strutils, options, ast, astalgo, msgs, os, idents, wordrecg, renderer, strutils, options, ast, astalgo, msgs, os, idents, wordrecg, renderer,
rodread rodread
type type
@ -49,7 +49,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
result.add copyNode(c, templ, actual) result.add copyNode(c, templ, actual)
else: else:
var res = copyNode(c, templ, actual) 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) evalTemplateAux(templ.sons[i], actual, c, res)
result.add res result.add res
@ -86,9 +86,9 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym): PNode =
ctx.owner = tmpl ctx.owner = tmpl
ctx.genSymOwner = genSymOwner ctx.genSymOwner = genSymOwner
initIdTable(ctx.mapping) initIdTable(ctx.mapping)
let body = tmpl.getBody let body = tmpl.getBody
if isAtom(body): if isAtom(body):
result = newNodeI(nkPar, body.info) result = newNodeI(nkPar, body.info)
evalTemplateAux(body, args, ctx, result) evalTemplateAux(body, args, ctx, result)
if result.len == 1: result = result.sons[0] 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 if ctx.instLines: result.info = n.info
for i in countup(0, safeLen(body) - 1): for i in countup(0, safeLen(body) - 1):
evalTemplateAux(body.sons[i], args, ctx, result) evalTemplateAux(body.sons[i], args, ctx, result)
dec(evalTemplateCounter) dec(evalTemplateCounter)

View file

@ -572,6 +572,9 @@ proc footprint(filename: string): TCrc32 =
getCompileCFileCmd(filename, true) getCompileCFileCmd(filename, true)
proc externalFileChanged(filename: string): bool = proc externalFileChanged(filename: string): bool =
if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
return false
var crcFile = toGeneratedFile(filename.withPackageName, "crc") var crcFile = toGeneratedFile(filename.withPackageName, "crc")
var currentCrc = int(footprint(filename)) var currentCrc = int(footprint(filename))
var f: File var f: File

View file

@ -37,11 +37,11 @@ const
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'} PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'}
proc newLine(p: var TTmplParser) = proc newLine(p: var TTmplParser) =
llStreamWrite(p.outp, repeatChar(p.emitPar, ')')) llStreamWrite(p.outp, repeat(')', p.emitPar))
p.emitPar = 0 p.emitPar = 0
if p.info.line > int16(1): llStreamWrite(p.outp, "\n") if p.info.line > int16(1): llStreamWrite(p.outp, "\n")
if p.pendingExprLine: if p.pendingExprLine:
llStreamWrite(p.outp, repeatChar(2)) llStreamWrite(p.outp, spaces(2))
p.pendingExprLine = false p.pendingExprLine = false
proc scanPar(p: var TTmplParser, d: int) = proc scanPar(p: var TTmplParser, d: int) =
@ -88,24 +88,24 @@ proc parseLine(p: var TTmplParser) =
else: else:
p.info.col = int16(j) p.info.col = int16(j)
localError(p.info, errXNotAllowedHere, "end") localError(p.info, errXNotAllowedHere, "end")
llStreamWrite(p.outp, repeatChar(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, "#end") llStreamWrite(p.outp, "#end")
of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator, of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator,
wConverter, wMacro, wTemplate, wMethod: wConverter, wMacro, wTemplate, wMethod:
llStreamWrite(p.outp, repeatChar(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, substr(p.x, d)) llStreamWrite(p.outp, substr(p.x, d))
inc(p.indent, 2) inc(p.indent, 2)
of wElif, wOf, wElse, wExcept, wFinally: of wElif, wOf, wElse, wExcept, wFinally:
llStreamWrite(p.outp, repeatChar(p.indent - 2)) llStreamWrite(p.outp, spaces(p.indent - 2))
llStreamWrite(p.outp, substr(p.x, d)) llStreamWrite(p.outp, substr(p.x, d))
of wLet, wVar, wConst, wType: of wLet, wVar, wConst, wType:
llStreamWrite(p.outp, repeatChar(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, substr(p.x, d)) llStreamWrite(p.outp, substr(p.x, d))
if not p.x.contains({':', '='}): if not p.x.contains({':', '='}):
# no inline element --> treat as block: # no inline element --> treat as block:
inc(p.indent, 2) inc(p.indent, 2)
else: else:
llStreamWrite(p.outp, repeatChar(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, substr(p.x, d)) llStreamWrite(p.outp, substr(p.x, d))
p.state = psDirective p.state = psDirective
else: else:
@ -120,11 +120,11 @@ proc parseLine(p: var TTmplParser) =
# next line of string literal: # next line of string literal:
llStreamWrite(p.outp, p.conc) llStreamWrite(p.outp, p.conc)
llStreamWrite(p.outp, "\n") llStreamWrite(p.outp, "\n")
llStreamWrite(p.outp, repeatChar(p.indent + 2)) llStreamWrite(p.outp, spaces(p.indent + 2))
llStreamWrite(p.outp, "\"") llStreamWrite(p.outp, "\"")
of psDirective: of psDirective:
newLine(p) newLine(p)
llStreamWrite(p.outp, repeatChar(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, p.emit) llStreamWrite(p.outp, p.emit)
llStreamWrite(p.outp, "(\"") llStreamWrite(p.outp, "(\"")
inc(p.emitPar) inc(p.emitPar)

View file

@ -27,7 +27,7 @@ proc getModuleName*(n: PNode): string =
result = n.ident.s result = n.ident.s
of nkSym: of nkSym:
result = n.sym.name.s 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: if n.sons[0].kind == nkIdent and n.sons[0].ident.id == getIdent("as").id:
# XXX hack ahead: # XXX hack ahead:
n.kind = nkImportAs n.kind = nkImportAs

View file

@ -780,7 +780,7 @@ proc genIf(p: PProc, n: PNode, r: var TCompRes) =
moveInto(p, stmt, r) moveInto(p, stmt, r)
appf(p.body, "}$n" | "end$n") appf(p.body, "}$n" | "end$n")
if p.target == targetJS: if p.target == targetJS:
app(p.body, repeatChar(toClose, '}') & tnl) app(p.body, repeat('}', toClose) & tnl)
else: else:
for i in 1..toClose: appf(p.body, "end$n") for i in 1..toClose: appf(p.body, "end$n")

View file

@ -35,7 +35,7 @@ proc llStreamOpen*(data: string): PLLStream =
result.s = data result.s = data
result.kind = llsString result.kind = llsString
proc llStreamOpen*(f: var File): PLLStream = proc llStreamOpen*(f: File): PLLStream =
new(result) new(result)
result.f = f result.f = f
result.kind = llsFile result.kind = llsFile

View file

@ -176,7 +176,7 @@ proc addInterfaceDeclAux(c: PContext, sym: PSym) =
if sfExported in sym.flags: if sfExported in sym.flags:
# add to interface: # add to interface:
if c.module != nil: strTableAdd(c.module.tab, sym) if c.module != nil: strTableAdd(c.module.tab, sym)
else: internalError(sym.info, "AddInterfaceDeclAux") else: internalError(sym.info, "addInterfaceDeclAux")
proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) = proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) =
addDeclAt(scope, sym) addDeclAt(scope, sym)

View file

@ -117,7 +117,8 @@ type
warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode, warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode,
warnEachIdentIsTuple, warnShadowIdent, warnEachIdentIsTuple, warnShadowIdent,
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnUser, warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
warnUser,
hintSuccess, hintSuccessX, hintSuccess, hintSuccessX,
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
@ -391,6 +392,7 @@ const
warnGcMem: "'$1' uses GC'ed memory [GcMem]", 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]", 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]", warnLockLevel: "$1 [LockLevel]",
warnResultShadowed: "Special variable 'result' is shadowed. [ResultShadowed]",
warnUser: "$1 [User]", warnUser: "$1 [User]",
hintSuccess: "operation successful [Success]", hintSuccess: "operation successful [Success]",
hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#) [SuccessX]", hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#) [SuccessX]",
@ -411,7 +413,7 @@ const
hintUser: "$1 [User]"] hintUser: "$1 [User]"]
const const
WarningsToStr*: array[0..29, string] = ["CannotOpenFile", "OctalEscape", WarningsToStr*: array[0..30, string] = ["CannotOpenFile", "OctalEscape",
"XIsNeverRead", "XmightNotBeenInit", "XIsNeverRead", "XmightNotBeenInit",
"Deprecated", "ConfigDeprecated", "Deprecated", "ConfigDeprecated",
"SmallLshouldNotBeUsed", "UnknownMagic", "SmallLshouldNotBeUsed", "UnknownMagic",
@ -421,7 +423,7 @@ const
"TypelessParam", "DifferentHeaps", "WriteToForeignHeap", "TypelessParam", "DifferentHeaps", "WriteToForeignHeap",
"UnsafeCode", "EachIdentIsTuple", "ShadowIdent", "UnsafeCode", "EachIdentIsTuple", "ShadowIdent",
"ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
"GcMem", "Destructor", "LockLevel", "User"] "GcMem", "Destructor", "LockLevel", "ResultShadowed", "User"]
HintsToStr*: array[0..16, string] = ["Success", "SuccessX", "LineTooLong", HintsToStr*: array[0..16, string] = ["Success", "SuccessX", "LineTooLong",
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
@ -784,7 +786,7 @@ proc rawMessage*(msg: TMsgKind, arg: string) =
proc writeSurroundingSrc(info: TLineInfo) = proc writeSurroundingSrc(info: TLineInfo) =
const indent = " " const indent = " "
msgWriteln(indent & info.sourceLine.ropeToStr) msgWriteln(indent & info.sourceLine.ropeToStr)
msgWriteln(indent & repeatChar(info.col, ' ') & '^') msgWriteln(indent & spaces(info.col) & '^')
proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string = proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
let frmt = case msg let frmt = case msg

View file

@ -18,3 +18,4 @@ define:useStdoutAsStdmsg
cs:partial cs:partial
#define:useNodeIds #define:useNodeIds
symbol:nimfix symbol:nimfix
#gc:markAndSweep

View file

@ -166,4 +166,4 @@ proc getCurrentLine(L: TBaseLexer, marker: bool = true): string =
inc(i) inc(i)
result.add("\n") result.add("\n")
if marker: if marker:
result.add(repeatChar(getColNumber(L, L.bufpos)) & '^' & "\n") result.add(spaces(getColNumber(L, L.bufpos)) & '^' & "\n")

View file

@ -865,6 +865,7 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode =
#| [' optInd (identColonEquals (comma/semicolon)?)* optPar ']' #| [' optInd (identColonEquals (comma/semicolon)?)* optPar ']'
#| extTupleDecl = 'tuple' #| extTupleDecl = 'tuple'
#| COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)? #| COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)?
#| tupleClass = 'tuple'
result = newNodeP(nkTupleTy, p) result = newNodeP(nkTupleTy, p)
getTok(p) getTok(p)
if p.tok.tokType == tkBracketLe: if p.tok.tokType == tkBracketLe:
@ -894,6 +895,8 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode =
parMessage(p, errIdentifierExpected, p.tok) parMessage(p, errIdentifierExpected, p.tok)
break break
if not sameInd(p): break if not sameInd(p): break
else:
result = newNodeP(nkTupleClassTy, p)
proc parseParamList(p: var TParser, retColon = true): PNode = proc parseParamList(p: var TParser, retColon = true): PNode =
#| paramList = '(' declColonEquals ^* (comma/semicolon) ')' #| paramList = '(' declColonEquals ^* (comma/semicolon) ')'

View file

@ -10,15 +10,15 @@
# This module implements the passes functionality. A pass must implement the # This module implements the passes functionality. A pass must implement the
# `TPass` interface. # `TPass` interface.
import import
strutils, lists, options, ast, astalgo, llstream, msgs, platform, os, strutils, lists, options, ast, astalgo, llstream, msgs, platform, os,
condsyms, idents, renderer, types, extccomp, math, magicsys, nversion, condsyms, idents, renderer, types, extccomp, math, magicsys, nversion,
nimsets, syntaxes, times, rodread, idgen nimsets, syntaxes, times, rodread, idgen
type type
TPassContext* = object of RootObj # the pass's context TPassContext* = object of RootObj # the pass's context
fromCache*: bool # true if created by "openCached" fromCache*: bool # true if created by "openCached"
PPassContext* = ref TPassContext PPassContext* = ref TPassContext
TPassOpen* = proc (module: PSym): PPassContext {.nimcall.} TPassOpen* = proc (module: PSym): PPassContext {.nimcall.}
@ -33,8 +33,8 @@ type
TPassData* = tuple[input: PNode, closeOutput: PNode] TPassData* = tuple[input: PNode, closeOutput: PNode]
TPasses* = openArray[TPass] TPasses* = openArray[TPass]
# a pass is a tuple of procedure vars ``TPass.close`` may produce additional # a pass is a tuple of procedure vars ``TPass.close`` may produce additional
# nodes. These are passed to the other close procedures. # nodes. These are passed to the other close procedures.
# This mechanism used to be used for the instantiation of generics. # This mechanism used to be used for the instantiation of generics.
proc makePass*(open: TPassOpen = nil, proc makePass*(open: TPassOpen = nil,
@ -53,46 +53,46 @@ proc makePass*(open: TPassOpen = nil,
proc processModule*(module: PSym, stream: PLLStream, rd: PRodReader) proc processModule*(module: PSym, stream: PLLStream, rd: PRodReader)
# the semantic checker needs these: # the semantic checker needs these:
var var
gImportModule*: proc (m: PSym, fileIdx: int32): PSym {.nimcall.} gImportModule*: proc (m: PSym, fileIdx: int32): PSym {.nimcall.}
gIncludeFile*: proc (m: PSym, fileIdx: int32): PNode {.nimcall.} gIncludeFile*: proc (m: PSym, fileIdx: int32): PNode {.nimcall.}
# implementation # implementation
proc skipCodegen*(n: PNode): bool {.inline.} = proc skipCodegen*(n: PNode): bool {.inline.} =
# can be used by codegen passes to determine whether they should do # can be used by codegen passes to determine whether they should do
# something with `n`. Currently, this ignores `n` and uses the global # something with `n`. Currently, this ignores `n` and uses the global
# error count instead. # error count instead.
result = msgs.gErrorCounter > 0 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 # 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 # needs to be stored. The passes manager frees s.sons[codePos] when
# appropriate to free the procedure body's memory. This is important # appropriate to free the procedure body's memory. This is important
# to keep memory usage down. # to keep memory usage down.
if (s.kind in {skMethod, skProc}) and if (s.kind in {skMethod, skProc}) and
({sfCompilerProc, sfCompileTime} * s.flags == {}) and ({sfCompilerProc, sfCompileTime} * s.flags == {}) and
(s.typ.callConv != ccInline) and (s.typ.callConv != ccInline) and
(s.ast.sons[genericParamsPos].kind == nkEmpty): (s.ast.sons[genericParamsPos].kind == nkEmpty):
result = false result = false
# XXX this doesn't really make sense with excessive CTFE # XXX this doesn't really make sense with excessive CTFE
else: else:
result = true result = true
const const
maxPasses = 10 maxPasses = 10
type type
TPassContextArray = array[0..maxPasses - 1, PPassContext] TPassContextArray = array[0..maxPasses - 1, PPassContext]
var var
gPasses: array[0..maxPasses - 1, TPass] gPasses: array[0..maxPasses - 1, TPass]
gPassesLen*: int gPassesLen*: int
proc clearPasses* = proc clearPasses* =
gPassesLen = 0 gPassesLen = 0
proc registerPass*(p: TPass) = proc registerPass*(p: TPass) =
gPasses[gPassesLen] = p gPasses[gPassesLen] = p
inc(gPassesLen) inc(gPassesLen)
@ -109,48 +109,48 @@ proc carryPasses*(nodes: PNode, module: PSym, passes: TPasses) =
passdata = carryPass(pass, module, passdata) passdata = carryPass(pass, module, passdata)
proc openPasses(a: var TPassContextArray, module: PSym) = proc openPasses(a: var TPassContextArray, module: PSym) =
for i in countup(0, gPassesLen - 1): for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].open): if not isNil(gPasses[i].open):
a[i] = gPasses[i].open(module) a[i] = gPasses[i].open(module)
else: a[i] = nil else: a[i] = nil
proc openPassesCached(a: var TPassContextArray, module: PSym, rd: PRodReader) = proc openPassesCached(a: var TPassContextArray, module: PSym, rd: PRodReader) =
for i in countup(0, gPassesLen - 1): for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].openCached): if not isNil(gPasses[i].openCached):
a[i] = gPasses[i].openCached(module, rd) a[i] = gPasses[i].openCached(module, rd)
if a[i] != nil: if a[i] != nil:
a[i].fromCache = true a[i].fromCache = true
else: else:
a[i] = nil a[i] = nil
proc closePasses(a: var TPassContextArray) = proc closePasses(a: var TPassContextArray) =
var m: PNode = nil 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) if not isNil(gPasses[i].close): m = gPasses[i].close(a[i], m)
a[i] = nil # free the memory here 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 # this implements the code transformation pipeline
var m = n var m = n
for i in countup(0, gPassesLen - 1): for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].process): if not isNil(gPasses[i].process):
m = gPasses[i].process(a[i], m) m = gPasses[i].process(a[i], m)
if isNil(m): return false if isNil(m): return false
result = true result = true
proc processTopLevelStmtCached(n: PNode, a: var TPassContextArray) = proc processTopLevelStmtCached(n: PNode, a: var TPassContextArray) =
# this implements the code transformation pipeline # this implements the code transformation pipeline
var m = n 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) 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 var m: PNode = nil
for i in countup(0, gPassesLen - 1): for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].openCached) and not isNil(gPasses[i].close): if not isNil(gPasses[i].openCached) and not isNil(gPasses[i].close):
m = gPasses[i].close(a[i], m) m = gPasses[i].close(a[i], m)
a[i] = nil # free the memory here a[i] = nil # free the memory here
proc processImplicits(implicits: seq[string], nodeKind: TNodeKind, proc processImplicits(implicits: seq[string], nodeKind: TNodeKind,
a: var TPassContextArray) = a: var TPassContextArray) =
for module in items(implicits): for module in items(implicits):
@ -159,45 +159,45 @@ proc processImplicits(implicits: seq[string], nodeKind: TNodeKind,
str.info = gCmdLineInfo str.info = gCmdLineInfo
importStmt.addSon str importStmt.addSon str
if not processTopLevelStmt(importStmt, a): break if not processTopLevelStmt(importStmt, a): break
proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) = proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) =
var var
p: TParsers p: TParsers
a: TPassContextArray a: TPassContextArray
s: PLLStream s: PLLStream
fileIdx = module.fileIdx fileIdx = module.fileIdx
if rd == nil: if rd == nil:
openPasses(a, module) openPasses(a, module)
if stream == nil: if stream == nil:
let filename = fileIdx.toFullPathConsiderDirty let filename = fileIdx.toFullPathConsiderDirty
if module.name.s == "-": if module.name.s == "-":
module.name.s = "stdinfile" module.name.s = "stdinfile"
s = llStreamOpenStdIn() s = llStreamOpen(stdin)
else: else:
s = llStreamOpen(filename, fmRead) s = llStreamOpen(filename, fmRead)
if s == nil: if s == nil:
rawMessage(errCannotOpenFile, filename) rawMessage(errCannotOpenFile, filename)
return return
else: else:
s = stream s = stream
while true: while true:
openParsers(p, fileIdx, s) openParsers(p, fileIdx, s)
if sfSystemModule notin module.flags: 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 # modules to include between compilation runs? we'd need to track that
# in ROD files. I think we should enable this feature only # in ROD files. I think we should enable this feature only
# for the interactive mode. # for the interactive mode.
processImplicits implicitImports, nkImportStmt, a processImplicits implicitImports, nkImportStmt, a
processImplicits implicitIncludes, nkIncludeStmt, a processImplicits implicitIncludes, nkIncludeStmt, a
while true: while true:
var n = parseTopLevelStmt(p) var n = parseTopLevelStmt(p)
if n.kind == nkEmpty: break if n.kind == nkEmpty: break
if not processTopLevelStmt(n, a): break if not processTopLevelStmt(n, a): break
closeParsers(p) closeParsers(p)
if s.kind != llsStdIn: break if s.kind != llsStdIn: break
closePasses(a) closePasses(a)
# id synchronization point for more consistent code generation: # id synchronization point for more consistent code generation:
idSynchronizationPoint(1000) idSynchronizationPoint(1000)

View file

@ -92,7 +92,7 @@ proc addTok(g: var TSrcGen, kind: TTokType, s: string) =
proc addPendingNL(g: var TSrcGen) = proc addPendingNL(g: var TSrcGen) =
if g.pendingNL >= 0: if g.pendingNL >= 0:
addTok(g, tkSpaces, "\n" & repeatChar(g.pendingNL)) addTok(g, tkSpaces, "\n" & spaces(g.pendingNL))
g.lineLen = g.pendingNL g.lineLen = g.pendingNL
g.pendingNL = - 1 g.pendingNL = - 1
@ -190,7 +190,7 @@ proc putComment(g: var TSrcGen, s: string) =
if not isCode and (g.lineLen + (j - i) > MaxLineLen): if not isCode and (g.lineLen + (j - i) > MaxLineLen):
put(g, tkComment, com) put(g, tkComment, com)
optNL(g, ind) optNL(g, ind)
com = '#' & repeatChar(comIndent) com = '#' & spaces(comIndent)
while s[i] > ' ': while s[i] > ' ':
add(com, s[i]) add(com, s[i])
inc(i) inc(i)
@ -280,7 +280,7 @@ proc gcom(g: var TSrcGen, n: PNode) =
(g.lineLen < LineCommentColumn): (g.lineLen < LineCommentColumn):
var ml = maxLineLength(n.comment) var ml = maxLineLength(n.comment)
if ml + LineCommentColumn <= MaxLineLen: if ml + LineCommentColumn <= MaxLineLen:
put(g, tkSpaces, repeatChar(LineCommentColumn - g.lineLen)) put(g, tkSpaces, spaces(LineCommentColumn - g.lineLen))
putComment(g, n.comment) #assert(g.comStack[high(g.comStack)] = n); putComment(g, n.comment) #assert(g.comStack[high(g.comStack)] = n);
proc gcoms(g: var TSrcGen) = proc gcoms(g: var TSrcGen) =
@ -395,6 +395,7 @@ proc lsub(n: PNode): int =
of nkClosedSymChoice, nkOpenSymChoice: of nkClosedSymChoice, nkOpenSymChoice:
result = lsons(n) + len("()") + sonsLen(n) - 1 result = lsons(n) + len("()") + sonsLen(n) - 1
of nkTupleTy: result = lcomma(n) + len("tuple[]") of nkTupleTy: result = lcomma(n) + len("tuple[]")
of nkTupleClassTy: result = len("tuple")
of nkDotExpr: result = lsons(n) + 1 of nkDotExpr: result = lsons(n) + 1
of nkBind: result = lsons(n) + len("bind_") of nkBind: result = lsons(n) + len("bind_")
of nkBindStmt: result = lcomma(n) + len("bind_") of nkBindStmt: result = lcomma(n) + len("bind_")
@ -1292,10 +1293,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
gsub(g, n.sons[0]) gsub(g, n.sons[0])
of nkTupleTy: of nkTupleTy:
put(g, tkTuple, "tuple") put(g, tkTuple, "tuple")
if sonsLen(n) > 0: put(g, tkBracketLe, "[")
put(g, tkBracketLe, "[") gcomma(g, n)
gcomma(g, n) put(g, tkBracketRi, "]")
put(g, tkBracketRi, "]") of nkTupleClassTy:
put(g, tkTuple, "tuple")
of nkMetaNode_Obsolete: of nkMetaNode_Obsolete:
put(g, tkParLe, "(META|") put(g, tkParLe, "(META|")
gsub(g, n.sons[0]) gsub(g, n.sons[0])

View file

@ -65,8 +65,8 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode;
echo "passing to safeSemExpr: ", renderTree(n) echo "passing to safeSemExpr: ", renderTree(n)
discard safeSemExpr(c, n) discard safeSemExpr(c, n)
proc typeMismatch(n: PNode, formal, actual: PType) = proc typeMismatch(n: PNode, formal, actual: PType) =
if formal.kind != tyError and actual.kind != tyError: if formal.kind != tyError and actual.kind != tyError:
localError(n.info, errGenerated, msgKindToString(errTypeMismatch) & localError(n.info, errGenerated, msgKindToString(errTypeMismatch) &
typeToString(actual) & ") " & typeToString(actual) & ") " &
`%`(msgKindToString(errButExpectedX), [typeToString(formal)])) `%`(msgKindToString(errButExpectedX), [typeToString(formal)]))
@ -113,7 +113,7 @@ proc commonType*(x, y: PType): PType =
else: else:
result = newType(tyTypeDesc, a.owner) result = newType(tyTypeDesc, a.owner)
rawAddSon(result, newType(tyNone, a.owner)) rawAddSon(result, newType(tyNone, a.owner))
elif b.kind in {tyArray, tyArrayConstr, tySet, tySequence} and elif b.kind in {tyArray, tyArrayConstr, tySet, tySequence} and
a.kind == b.kind: a.kind == b.kind:
# check for seq[empty] vs. seq[int] # check for seq[empty] vs. seq[int]
let idx = ord(b.kind in {tyArray, tyArrayConstr}) let idx = ord(b.kind in {tyArray, tyArrayConstr})
@ -163,7 +163,7 @@ proc commonType*(x, y: PType): PType =
result = newType(k, r.owner) result = newType(k, r.owner)
result.addSonSkipIntLit(r) result.addSonSkipIntLit(r)
proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym = proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info) result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info)
proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
@ -182,7 +182,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
allowed: TSymFlags): PSym allowed: TSymFlags): PSym
# identifier with visability # identifier with visability
proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
allowed: TSymFlags): PSym allowed: TSymFlags): PSym
proc semStmtScope(c: PContext, n: PNode): PNode proc semStmtScope(c: PContext, n: PNode): PNode
@ -190,7 +190,7 @@ proc typeAllowedCheck(info: TLineInfo; typ: PType; kind: TSymKind) =
let t = typeAllowed(typ, kind) let t = typeAllowed(typ, kind)
if t != nil: if t != nil:
if t == typ: localError(info, "invalid type: '" & typeToString(typ) & "'") if t == typ: localError(info, "invalid type: '" & typeToString(typ) & "'")
else: localError(info, "invalid type: '" & typeToString(t) & else: localError(info, "invalid type: '" & typeToString(t) &
"' in this context: '" & typeToString(typ) & "'") "' in this context: '" & typeToString(typ) & "'")
proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} = proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} =
@ -226,7 +226,7 @@ when false:
result = newSymNode(getSysSym"void") result = newSymNode(getSysSym"void")
else: else:
result.typ = makeTypeDesc(c, result.typ) result.typ = makeTypeDesc(c, result.typ)
result.handleIsOperator = proc (n: PNode): PNode = result.handleIsOperator = proc (n: PNode): PNode =
result = isOpImpl(c, n) result = isOpImpl(c, n)
@ -248,7 +248,7 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode =
if result == nil: if result == nil:
result = arg result = arg
# for 'tcnstseq' we support [] to become 'seq' # for 'tcnstseq' we support [] to become 'seq'
if eOrig.typ.skipTypes(abstractInst).kind == tySequence and if eOrig.typ.skipTypes(abstractInst).kind == tySequence and
arg.typ.skipTypes(abstractInst).kind == tyArrayConstr: arg.typ.skipTypes(abstractInst).kind == tyArrayConstr:
arg.typ = eOrig.typ arg.typ = eOrig.typ
@ -320,7 +320,7 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
else: else:
case s.typ.sons[0].kind case s.typ.sons[0].kind
of tyExpr: of tyExpr:
# BUGFIX: we cannot expect a type here, because module aliases would not # BUGFIX: we cannot expect a type here, because module aliases would not
# work then (see the ``tmodulealias`` test) # work then (see the ``tmodulealias`` test)
# semExprWithType(c, result) # semExprWithType(c, result)
result = semExpr(c, result, flags) result = semExpr(c, result, flags)
@ -355,18 +355,18 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
result = semAfterMacroCall(c, result, sym, flags) result = semAfterMacroCall(c, result, sym, flags)
popInfoContext() popInfoContext()
proc forceBool(c: PContext, n: PNode): PNode = proc forceBool(c: PContext, n: PNode): PNode =
result = fitNode(c, getSysType(tyBool), n) result = fitNode(c, getSysType(tyBool), n)
if result == nil: result = n if result == nil: result = n
proc semConstBoolExpr(c: PContext, n: PNode): PNode = proc semConstBoolExpr(c: PContext, n: PNode): PNode =
let nn = semExprWithType(c, n) let nn = semExprWithType(c, n)
result = fitNode(c, getSysType(tyBool), nn) result = fitNode(c, getSysType(tyBool), nn)
if result == nil: if result == nil:
localError(n.info, errConstExprExpected) localError(n.info, errConstExprExpected)
return nn return nn
result = getConstExpr(c.module, result) result = getConstExpr(c.module, result)
if result == nil: if result == nil:
localError(n.info, errConstExprExpected) localError(n.info, errConstExprExpected)
result = nn result = nn
@ -403,9 +403,9 @@ proc myOpen(module: PSym): PPassContext =
pushOwner(c.module) pushOwner(c.module)
c.importTable = openScope(c) c.importTable = openScope(c)
c.importTable.addSym(module) # a module knows itself c.importTable.addSym(module) # a module knows itself
if sfSystemModule in module.flags: if sfSystemModule in module.flags:
magicsys.systemModule = module # set global variable! magicsys.systemModule = module # set global variable!
else: else:
c.importTable.addSym magicsys.systemModule # import the "System" identifier c.importTable.addSym magicsys.systemModule # import the "System" identifier
importAllSymbols(c, magicsys.systemModule) importAllSymbols(c, magicsys.systemModule)
c.topLevelScope = openScope(c) c.topLevelScope = openScope(c)
@ -415,13 +415,13 @@ proc myOpenCached(module: PSym, rd: PRodReader): PPassContext =
result = myOpen(module) result = myOpen(module)
for m in items(rd.methods): methodDef(m, true) for m in items(rd.methods): methodDef(m, true)
proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode = proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
result = semStmt(c, n) result = semStmt(c, n)
# BUGFIX: process newly generated generics here, not at the end! # BUGFIX: process newly generated generics here, not at the end!
if c.lastGenericIdx < c.generics.len: if c.lastGenericIdx < c.generics.len:
var a = newNodeI(nkStmtList, n.info) var a = newNodeI(nkStmtList, n.info)
addCodeForGenerics(c, a) addCodeForGenerics(c, a)
if sonsLen(a) > 0: if sonsLen(a) > 0:
# a generic has been added to `a`: # a generic has been added to `a`:
if result.kind != nkEmpty: addSon(a, result) if result.kind != nkEmpty: addSon(a, result)
result = a result = a
@ -429,17 +429,17 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
if gCmd == cmdInteractive and not isEmptyType(result.typ): if gCmd == cmdInteractive and not isEmptyType(result.typ):
result = buildEchoStmt(c, result) result = buildEchoStmt(c, result)
result = transformStmt(c.module, result) result = transformStmt(c.module, result)
proc recoverContext(c: PContext) = proc recoverContext(c: PContext) =
# clean up in case of a semantic error: We clean up the stacks, etc. This is # clean up in case of a semantic error: We clean up the stacks, etc. This is
# faster than wrapping every stack operation in a 'try finally' block and # faster than wrapping every stack operation in a 'try finally' block and
# requires far less code. # requires far less code.
c.currentScope = c.topLevelScope c.currentScope = c.topLevelScope
while getCurrOwner().kind != skModule: popOwner() while getCurrOwner().kind != skModule: popOwner()
while c.p != nil and c.p.owner.kind != skModule: c.p = c.p.next while c.p != nil and c.p.owner.kind != skModule: c.p = c.p.next
proc myProcess(context: PPassContext, n: PNode): PNode = proc myProcess(context: PPassContext, n: PNode): PNode =
var c = PContext(context) var c = PContext(context)
# no need for an expensive 'try' if we stop after the first error anyway: # no need for an expensive 'try' if we stop after the first error anyway:
if msgs.gErrorMax <= 1: if msgs.gErrorMax <= 1:
result = semStmtAndGenerateGenerics(c, n) result = semStmtAndGenerateGenerics(c, n)
@ -455,8 +455,8 @@ proc myProcess(context: PPassContext, n: PNode): PNode =
if getCurrentException() of ESuggestDone: result = nil if getCurrentException() of ESuggestDone: result = nil
else: result = ast.emptyNode else: result = ast.emptyNode
#if gCmd == cmdIdeTools: findSuggest(c, n) #if gCmd == cmdIdeTools: findSuggest(c, n)
proc myClose(context: PPassContext, n: PNode): PNode = proc myClose(context: PPassContext, n: PNode): PNode =
var c = PContext(context) var c = PContext(context)
closeScope(c) # close module's scope closeScope(c) # close module's scope
rawCloseScope(c) # imported symbols; don't check for unused ones! rawCloseScope(c) # imported symbols; don't check for unused ones!

View file

@ -7,16 +7,16 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## This module implements semantic checking for calls. ## This module implements semantic checking for calls.
# included from sem.nim # included from sem.nim
proc sameMethodDispatcher(a, b: PSym): bool = proc sameMethodDispatcher(a, b: PSym): bool =
result = false result = false
if a.kind == skMethod and b.kind == skMethod: if a.kind == skMethod and b.kind == skMethod:
var aa = lastSon(a.ast) var aa = lastSon(a.ast)
var bb = lastSon(b.ast) var bb = lastSon(b.ast)
if aa.kind == nkSym and bb.kind == nkSym: if aa.kind == nkSym and bb.kind == nkSym:
if aa.sym == bb.sym: if aa.sym == bb.sym:
result = true result = true
else: else:
discard discard
@ -31,7 +31,7 @@ proc sameMethodDispatcher(a, b: PSym): bool =
# to avoid subtle problems, the call remains ambiguous and needs to # to avoid subtle problems, the call remains ambiguous and needs to
# be disambiguated by the programmer; this way the right generic is # be disambiguated by the programmer; this way the right generic is
# instantiated. # instantiated.
proc determineType(c: PContext, s: PSym) proc determineType(c: PContext, s: PSym)
proc pickBestCandidate(c: PContext, headSymbol: PNode, proc pickBestCandidate(c: PContext, headSymbol: PNode,
@ -41,73 +41,80 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
best, alt: var TCandidate, best, alt: var TCandidate,
errors: var CandidateErrors) = errors: var CandidateErrors) =
var o: TOverloadIter var o: TOverloadIter
var sym = initOverloadIter(o, c, headSymbol) # thanks to the lazy semchecking for operands, we need to iterate over the
var symScope = o.lastOverloadScope # symbol table *before* any call to 'initCandidate' which might invoke
# semExpr which might modify the symbol table in cases like
# 'init(a, 1, (var b = new(Type2); b))'.
var symx = initOverloadIter(o, c, headSymbol)
let symScope = o.lastOverloadScope
var syms: seq[tuple[a: PSym, b: int]] = @[]
while symx != nil:
if symx.kind in filter: syms.add((symx, o.lastOverloadScope))
symx = nextOverloadIter(o, c, headSymbol)
if syms.len == 0: return
var z: TCandidate var z: TCandidate
initCandidate(c, best, syms[0][0], initialBinding, symScope)
if sym == nil: return initCandidate(c, alt, syms[0][0], initialBinding, symScope)
initCandidate(c, best, sym, initialBinding, symScope)
initCandidate(c, alt, sym, initialBinding, symScope)
best.state = csNoMatch 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: for i in 0 .. <syms.len:
# gDebug = true let sym = syms[i][0]
matches(c, n, orig, z) determineType(c, sym)
if errors != nil: initCandidate(c, z, sym, initialBinding, syms[i][1])
errors.safeAdd(sym) z.calleeSym = sym
if z.errors != nil:
for err in z.errors: #if sym.name.s == "*" and (n.info ?? "temp5.nim") and n.info.line == 140:
errors.add(err) # gDebug = true
if z.state == csMatch: matches(c, n, orig, z)
# little hack so that iterators are preferred over everything else: if errors != nil:
if sym.kind in skIterators: inc(z.exactMatches, 200) errors.safeAdd(sym)
case best.state if z.errors != nil:
of csEmpty, csNoMatch: best = z for err in z.errors:
of csMatch: errors.add(err)
var cmp = cmpCandidates(best, z) if z.state == csMatch:
if cmp < 0: best = z # x is better than the best so far # little hack so that iterators are preferred over everything else:
elif cmp == 0: alt = z # x is as good as the best so far if sym.kind in skIterators: inc(z.exactMatches, 200)
else: discard case best.state
#if sym.name.s == "*" and (n.info ?? "temp5.nim") and n.info.line == 140: of csEmpty, csNoMatch: best = z
# echo "Matches ", n.info, " ", typeToString(sym.typ) of csMatch:
# debug sym var cmp = cmpCandidates(best, z)
# writeMatches(z) if cmp < 0: best = z # x is better than the best so far
# for i in 1 .. <len(z.call): elif cmp == 0: alt = z # x is as good as the best so far
# z.call[i].typ.debug else: discard
# quit 1 #if sym.name.s == "cmp" and (n.info ?? "rstgen.nim") and n.info.line == 516:
sym = nextOverloadIter(o, c, headSymbol) # echo "Matches ", n.info, " ", typeToString(sym.typ)
# debug sym
# writeMatches(z)
# for i in 1 .. <len(z.call):
# z.call[i].typ.debug
# quit 1
proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
# Gives a detailed error message; this is separated from semOverloadedCall, # Gives a detailed error message; this is separated from semOverloadedCall,
# as semOverlodedCall is already pretty slow (and we need this information # as semOverlodedCall is already pretty slow (and we need this information
# only in case of an error). # only in case of an error).
if c.inCompilesContext > 0: if c.inCompilesContext > 0:
# fail fast: # fail fast:
globalError(n.info, errTypeMismatch, "") globalError(n.info, errTypeMismatch, "")
if errors.isNil or errors.len == 0: if errors.isNil or errors.len == 0:
localError(n.info, errExprXCannotBeCalled, n[0].renderTree) localError(n.info, errExprXCannotBeCalled, n[0].renderTree)
return return
# to avoid confusing errors like: # to avoid confusing errors like:
# got (SslPtr, SocketHandle) # got (SslPtr, SocketHandle)
# but expected one of: # but expected one of:
# openssl.SSL_set_fd(ssl: SslPtr, fd: SocketHandle): cint # openssl.SSL_set_fd(ssl: SslPtr, fd: SocketHandle): cint
# we do a pre-analysis. If all types produce the same string, we will add # we do a pre-analysis. If all types produce the same string, we will add
# module information. # module information.
let proto = describeArgs(c, n, 1, preferName) let proto = describeArgs(c, n, 1, preferName)
var prefer = preferName var prefer = preferName
for err in errors: for err in errors:
var errProto = "" var errProto = ""
let n = err.typ.n let n = err.typ.n
for i in countup(1, n.len - 1): for i in countup(1, n.len - 1):
var p = n.sons[i] var p = n.sons[i]
if p.kind == nkSym: if p.kind == nkSym:
add(errProto, typeToString(p.sym.typ, preferName)) add(errProto, typeToString(p.sym.typ, preferName))
@ -159,9 +166,9 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
n.sons.insert(hiddenArg, 1) n.sons.insert(hiddenArg, 1)
orig.sons.insert(hiddenArg, 1) orig.sons.insert(hiddenArg, 1)
pickBest(f) pickBest(f)
if result.state != csMatch: if result.state != csMatch:
n.sons.delete(1) n.sons.delete(1)
orig.sons.delete(1) orig.sons.delete(1)
@ -179,7 +186,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
# we are going to try multiple variants # we are going to try multiple variants
n.sons[0..1] = [nil, n[1], calleeName] n.sons[0..1] = [nil, n[1], calleeName]
orig.sons[0..1] = [nil, orig[1], calleeName] orig.sons[0..1] = [nil, orig[1], calleeName]
template tryOp(x) = template tryOp(x) =
let op = newIdentNode(getIdent(x), n.info) let op = newIdentNode(getIdent(x), n.info)
n.sons[0] = op n.sons[0] = op
@ -188,7 +195,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
if nfExplicitCall in n.flags: if nfExplicitCall in n.flags:
tryOp ".()" tryOp ".()"
if result.state in {csEmpty, csNoMatch}: if result.state in {csEmpty, csNoMatch}:
tryOp "." tryOp "."
@ -199,7 +206,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
n.sons[0..1] = [callOp, n[1], calleeName] n.sons[0..1] = [callOp, n[1], calleeName]
orig.sons[0..1] = [callOp, orig[1], calleeName] orig.sons[0..1] = [callOp, orig[1], calleeName]
pickBest(callOp) pickBest(callOp)
if overloadsState == csEmpty and result.state == csEmpty: if overloadsState == csEmpty and result.state == csEmpty:
localError(n.info, errUndeclaredIdentifier, considerQuotedIdent(f).s) localError(n.info, errUndeclaredIdentifier, considerQuotedIdent(f).s)
return return
@ -224,7 +231,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
internalAssert result.state == csMatch internalAssert result.state == csMatch
#writeMatches(result) #writeMatches(result)
#writeMatches(alt) #writeMatches(alt)
if c.inCompilesContext > 0: if c.inCompilesContext > 0:
# quick error message for performance of 'compiles' built-in: # quick error message for performance of 'compiles' built-in:
globalError(n.info, errGenerated, "ambiguous call") globalError(n.info, errGenerated, "ambiguous call")
elif gErrorCounter == 0: elif gErrorCounter == 0:
@ -254,7 +261,7 @@ proc instGenericConvertersSons*(c: PContext, n: PNode, x: TCandidate) =
for i in 1 .. <n.len: for i in 1 .. <n.len:
instGenericConvertersArg(c, n.sons[i], x) instGenericConvertersArg(c, n.sons[i], x)
proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode = proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode =
var m: TCandidate var m: TCandidate
initCandidate(c, m, f) initCandidate(c, m, f)
result = paramTypesMatch(m, f, a, arg, nil) result = paramTypesMatch(m, f, a, arg, nil)
@ -325,7 +332,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
# get rid of the deref again for a better error message: # get rid of the deref again for a better error message:
n.sons[1] = n.sons[1].sons[0] n.sons[1] = n.sons[1].sons[0]
notFoundError(c, n, errors) notFoundError(c, n, errors)
else: else:
notFoundError(c, n, errors) notFoundError(c, n, errors)
# else: result = errorNode(c, n) # else: result = errorNode(c, n)
@ -341,7 +348,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
styleCheckUse(n.info, s) styleCheckUse(n.info, s)
result = newSymNode(newInst, n.info) result = newSymNode(newInst, n.info)
proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
assert n.kind == nkBracketExpr assert n.kind == nkBracketExpr
for i in 1..sonsLen(n)-1: for i in 1..sonsLen(n)-1:
n.sons[i].typ = semTypeNode(c, n.sons[i], nil) n.sons[i].typ = semTypeNode(c, n.sons[i], nil)
@ -361,11 +368,11 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
# XXX I think this could be improved by reusing sigmatch.paramTypesMatch. # XXX I think this could be improved by reusing sigmatch.paramTypesMatch.
# It's good enough for now. # It's good enough for now.
result = newNodeI(a.kind, n.info) result = newNodeI(a.kind, n.info)
for i in countup(0, len(a)-1): for i in countup(0, len(a)-1):
var candidate = a.sons[i].sym var candidate = a.sons[i].sym
if candidate.kind in {skProc, skMethod, skConverter, if candidate.kind in {skProc, skMethod, skConverter,
skIterator, skClosureIterator}: skIterator, skClosureIterator}:
# it suffices that the candidate has the proper number of generic # it suffices that the candidate has the proper number of generic
# type parameters: # type parameters:
if safeLen(candidate.ast.sons[genericParamsPos]) == n.len-1: if safeLen(candidate.ast.sons[genericParamsPos]) == n.len-1:
result.add(explicitGenericSym(c, n, candidate)) result.add(explicitGenericSym(c, n, candidate))

File diff suppressed because it is too large Load diff

View file

@ -356,7 +356,7 @@ proc semGenericStmt(c: PContext, n: PNode,
of nkIdent: a = n.sons[i] of nkIdent: a = n.sons[i]
else: illFormedAst(n) else: illFormedAst(n)
addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c)) addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c))
of nkObjectTy, nkTupleTy: of nkObjectTy, nkTupleTy, nkTupleClassTy:
discard discard
of nkFormalParams: of nkFormalParams:
checkMinSonsLen(n, 1) checkMinSonsLen(n, 1)

View file

@ -11,12 +11,12 @@
# included from sem.nim # included from sem.nim
proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
entry: var TInstantiation) = entry: var TInstantiation) =
if n.kind != nkGenericParams: if n.kind != nkGenericParams:
internalError(n.info, "instantiateGenericParamList; no generic params") internalError(n.info, "instantiateGenericParamList; no generic params")
newSeq(entry.concreteTypes, n.len) newSeq(entry.concreteTypes, n.len)
for i, a in n.pairs: for i, a in n.pairs:
if a.kind != nkSym: if a.kind != nkSym:
internalError(a.info, "instantiateGenericParamList; no symbol") internalError(a.info, "instantiateGenericParamList; no symbol")
var q = a.sym var q = a.sym
if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses: 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)) var t = PType(idTableGet(pt, q.typ))
if t == nil: if t == nil:
if tfRetType in q.typ.flags: 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 # later by semAsgn in return type inference scenario
t = q.typ t = q.typ
else: else:
localError(a.info, errCannotInstantiateX, s.name.s) localError(a.info, errCannotInstantiateX, s.name.s)
t = errorType(c) t = errorType(c)
elif t.kind == tyGenericParam: elif t.kind == tyGenericParam:
localError(a.info, errCannotInstantiateX, q.name.s) localError(a.info, errCannotInstantiateX, q.name.s)
t = errorType(c) t = errorType(c)
elif t.kind == tyGenericInvocation: elif t.kind == tyGenericInvocation:
@ -58,17 +58,17 @@ proc genericCacheGet(genericSym: PSym, entry: TInstantiation): PSym =
if sameInstantiation(entry, inst[]): if sameInstantiation(entry, inst[]):
return inst.sym return inst.sym
proc removeDefaultParamValues(n: PNode) = proc removeDefaultParamValues(n: PNode) =
# we remove default params, because they cannot be instantiated properly # we remove default params, because they cannot be instantiated properly
# and they are not needed anyway for instantiation (each param is already # and they are not needed anyway for instantiation (each param is already
# provided). # provided).
when false: when false:
for i in countup(1, sonsLen(n)-1): for i in countup(1, sonsLen(n)-1):
var a = n.sons[i] var a = n.sons[i]
if a.kind != nkIdentDefs: IllFormedAst(a) if a.kind != nkIdentDefs: IllFormedAst(a)
var L = a.len var L = a.len
if a.sons[L-1].kind != nkEmpty and a.sons[L-2].kind != nkEmpty: 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 # 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 # ``cmp: proc (a, b: T): int = cmp``. Hm, for ``cmp = cmp`` that is
# not possible... XXX We don't solve this issue here. # 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 var param = fn.typ.n.sons[i].sym
param.owner = fn param.owner = fn
addParamOrResult(c, param, fn.kind) addParamOrResult(c, param, fn.kind)
maybeAddResult(c, fn, fn.ast) maybeAddResult(c, fn, fn.ast)
proc instantiateBody(c: PContext, n, params: PNode, result: PSym) = proc instantiateBody(c: PContext, n, params: PNode, result: PSym) =
@ -132,9 +132,9 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
closeScope(c) closeScope(c)
popInfoContext() popInfoContext()
proc sideEffectsCheck(c: PContext, s: PSym) = proc sideEffectsCheck(c: PContext, s: PSym) =
if {sfNoSideEffect, sfSideEffect} * s.flags == if {sfNoSideEffect, sfSideEffect} * s.flags ==
{sfNoSideEffect, sfSideEffect}: {sfNoSideEffect, sfSideEffect}:
localError(s.info, errXhasSideEffects, s.name.s) localError(s.info, errXhasSideEffects, s.name.s)
proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, 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 # Alas, doing this here is probably not enough, because another
# proc signature could appear in the params: # proc signature could appear in the params:
# proc foo[T](a: proc (x: T, b: type(x.y)) # proc foo[T](a: proc (x: T, b: type(x.y))
# #
# The solution would be to move this logic into semtypinst, but # The solution would be to move this logic into semtypinst, but
# at this point semtypinst have to become part of sem, because it # at this point semtypinst have to become part of sem, because it
# will need to use openScope, addDecl, etc. # will need to use openScope, addDecl, etc.
addDecl(c, prc) addDecl(c, prc)
pushInfoContext(info) pushInfoContext(info)
var cl = initTypeVars(c, pt, info) var cl = initTypeVars(c, pt, info)
var result = instCopyType(cl, prc.typ) var result = instCopyType(cl, prc.typ)
let originalParams = result.n let originalParams = result.n
result.n = originalParams.shallowCopy result.n = originalParams.shallowCopy
for i in 1 .. <result.len: for i in 1 .. <result.len:
# twrong_field_caching requires these 'resetIdTable' calls: # twrong_field_caching requires these 'resetIdTable' calls:
if i > 1: resetIdTable(cl.symMap) if i > 1: resetIdTable(cl.symMap)
@ -198,10 +198,10 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
resetIdTable(cl.symMap) resetIdTable(cl.symMap)
result.sons[0] = replaceTypeVarsT(cl, result.sons[0]) result.sons[0] = replaceTypeVarsT(cl, result.sons[0])
result.n.sons[0] = originalParams[0].copyTree result.n.sons[0] = originalParams[0].copyTree
eraseVoidParams(result) eraseVoidParams(result)
skipIntLiteralParams(result) skipIntLiteralParams(result)
prc.typ = result prc.typ = result
maybeAddResult(c, prc, prc.ast) maybeAddResult(c, prc, prc.ast)
popInfoContext() popInfoContext()

View file

@ -138,7 +138,7 @@ proc guardDotAccess(a: PEffects; n: PNode) =
if g.kind == skUnknown: if g.kind == skUnknown:
var field: PSym = nil var field: PSym = nil
var ty = n.sons[0].typ.skipTypes(abstractPtrs) 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) field = lookupInRecord(ty.n, g.name)
else: else:
while ty != nil and ty.kind == tyObject: while ty != nil and ty.kind == tyObject:

View file

@ -12,13 +12,13 @@
var enforceVoidContext = PType(kind: tyStmt) var enforceVoidContext = PType(kind: tyStmt)
proc semDiscard(c: PContext, n: PNode): PNode = proc semDiscard(c: PContext, n: PNode): PNode =
result = n result = n
checkSonsLen(n, 1) checkSonsLen(n, 1)
if n.sons[0].kind != nkEmpty: if n.sons[0].kind != nkEmpty:
n.sons[0] = semExprWithType(c, n.sons[0]) n.sons[0] = semExprWithType(c, n.sons[0])
if isEmptyType(n.sons[0].typ): localError(n.info, errInvalidDiscard) if isEmptyType(n.sons[0].typ): localError(n.info, errInvalidDiscard)
proc semBreakOrContinue(c: PContext, n: PNode): PNode = proc semBreakOrContinue(c: PContext, n: PNode): PNode =
result = n result = n
checkSonsLen(n, 1) checkSonsLen(n, 1)
@ -29,7 +29,7 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode =
of nkIdent: s = lookUp(c, n.sons[0]) of nkIdent: s = lookUp(c, n.sons[0])
of nkSym: s = n.sons[0].sym of nkSym: s = n.sons[0].sym
else: illFormedAst(n) 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) var x = newSymNode(s)
x.info = n.info x.info = n.info
incl(s.flags, sfUsed) incl(s.flags, sfUsed)
@ -41,16 +41,16 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode =
else: else:
localError(n.info, errGenerated, "'continue' cannot have a label") localError(n.info, errGenerated, "'continue' cannot have a label")
elif (c.p.nestedLoopCounter <= 0) and (c.p.nestedBlockCounter <= 0): elif (c.p.nestedLoopCounter <= 0) and (c.p.nestedBlockCounter <= 0):
localError(n.info, errInvalidControlFlowX, localError(n.info, errInvalidControlFlowX,
renderTree(n, {renderNoComments})) renderTree(n, {renderNoComments}))
proc semAsm(con: PContext, n: PNode): PNode = proc semAsm(con: PContext, n: PNode): PNode =
checkSonsLen(n, 2) checkSonsLen(n, 2)
var marker = pragmaAsm(con, n.sons[0]) var marker = pragmaAsm(con, n.sons[0])
if marker == '\0': marker = '`' # default marker if marker == '\0': marker = '`' # default marker
result = semAsmOrEmit(con, n, marker) result = semAsmOrEmit(con, n, marker)
proc semWhile(c: PContext, n: PNode): PNode = proc semWhile(c: PContext, n: PNode): PNode =
result = n result = n
checkSonsLen(n, 2) checkSonsLen(n, 2)
openScope(c) openScope(c)
@ -62,16 +62,16 @@ proc semWhile(c: PContext, n: PNode): PNode =
if n.sons[1].typ == enforceVoidContext: if n.sons[1].typ == enforceVoidContext:
result.typ = enforceVoidContext result.typ = enforceVoidContext
proc toCover(t: PType): BiggestInt = proc toCover(t: PType): BiggestInt =
var t2 = skipTypes(t, abstractVarRange-{tyTypeDesc}) var t2 = skipTypes(t, abstractVarRange-{tyTypeDesc})
if t2.kind == tyEnum and enumHasHoles(t2): if t2.kind == tyEnum and enumHasHoles(t2):
result = sonsLen(t2.n) result = sonsLen(t2.n)
else: else:
result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc})) result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc}))
proc performProcvarCheck(c: PContext, n: PNode, s: PSym) = proc performProcvarCheck(c: PContext, n: PNode, s: PSym) =
## Checks that the given symbol is a proper procedure variable, meaning ## Checks that the given symbol is a proper procedure variable, meaning
## that it ## that it
var smoduleId = getModule(s).id var smoduleId = getModule(s).id
if sfProcvar notin s.flags and s.typ.callConv == ccDefault and if sfProcvar notin s.flags and s.typ.callConv == ccDefault and
smoduleId != c.module.id: smoduleId != c.module.id:
@ -98,11 +98,11 @@ proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} =
localError(n.info, warnDestructor) localError(n.info, warnDestructor)
# This still breaks too many things: # This still breaks too many things:
when false: 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}: c.p.owner.kind notin {skTemplate, skMacro}:
localError(n.info, errGenerated, "value expected, but got a type") 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]) result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
addSon(result, n) addSon(result, n)
@ -127,7 +127,7 @@ const
proc implicitlyDiscardable(n: PNode): bool = proc implicitlyDiscardable(n: PNode): bool =
var n = n var n = n
while n.kind in skipForDiscardable: n = n.lastSon 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 sfDiscardable in n.sons[0].sym.flags
proc fixNilType(n: PNode) = proc fixNilType(n: PNode) =
@ -160,11 +160,11 @@ proc discardCheck(c: PContext, result: PNode) =
while n.kind in skipForDiscardable: n = n.lastSon while n.kind in skipForDiscardable: n = n.lastSon
localError(n.info, errDiscardValueX, result.typ.typeToString) localError(n.info, errDiscardValueX, result.typ.typeToString)
proc semIf(c: PContext, n: PNode): PNode = proc semIf(c: PContext, n: PNode): PNode =
result = n result = n
var typ = commonTypeBegin var typ = commonTypeBegin
var hasElse = false var hasElse = false
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var it = n.sons[i] var it = n.sons[i]
if it.len == 2: if it.len == 2:
when newScopeForIf: openScope(c) when newScopeForIf: openScope(c)
@ -208,10 +208,10 @@ proc semCase(c: PContext, n: PNode): PNode =
else: else:
localError(n.info, errSelectorMustBeOfCertainTypes) localError(n.info, errSelectorMustBeOfCertainTypes)
return return
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
var x = n.sons[i] var x = n.sons[i]
case x.kind case x.kind
of nkOfBranch: of nkOfBranch:
checkMinSonsLen(x, 2) checkMinSonsLen(x, 2)
semCaseBranch(c, n, x, i, covered) semCaseBranch(c, n, x, i, covered)
var last = sonsLen(x)-1 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]) it.sons[j] = fitNode(c, typ, it.sons[j])
result.typ = typ 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) result = fitNode(c, typ, n)
if result.kind in {nkHiddenStdConv, nkHiddenSubConv}: if result.kind in {nkHiddenStdConv, nkHiddenSubConv}:
changeType(result.sons[1], typ, check=true) changeType(result.sons[1], typ, check=true)
result = result.sons[1] result = result.sons[1]
elif not sameType(result.typ, typ): 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 result = n.kind == nkSym and n.sym.name.id == s.id
proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym = proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
if isTopLevel(c): if isTopLevel(c):
result = semIdentWithPragma(c, kind, n, {sfExported}) result = semIdentWithPragma(c, kind, n, {sfExported})
incl(result.flags, sfGlobal) incl(result.flags, sfGlobal)
else: else:
@ -340,14 +340,14 @@ proc checkNilable(v: PSym) =
elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags: elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags:
message(v.info, warnProveInit, v.name.s) 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 var b: PNode
result = copyNode(n) result = copyNode(n)
var hasCompileTime = false var hasCompileTime = false
for i in countup(0, sonsLen(n)-1): for i in countup(0, sonsLen(n)-1):
var a = n.sons[i] var a = n.sons[i]
if gCmd == cmdIdeTools: suggestStmt(c, a) 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) if a.kind notin {nkIdentDefs, nkVarTuple, nkConstDef}: illFormedAst(a)
checkMinSonsLen(a, 3) checkMinSonsLen(a, 3)
var length = sonsLen(a) var length = sonsLen(a)
@ -369,7 +369,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
typ = def.typ typ = def.typ
else: else:
# BUGFIX: ``fitNode`` is needed here! # 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) def = fitNode(c, typ, def)
#changeType(def.skipConv, typ, check=true) #changeType(def.skipConv, typ, check=true)
else: else:
@ -381,15 +381,15 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
else: else:
def = ast.emptyNode def = ast.emptyNode
if symkind == skLet: localError(a.info, errLetNeedsInit) if symkind == skLet: localError(a.info, errLetNeedsInit)
# this can only happen for errornous var statements: # this can only happen for errornous var statements:
if typ == nil: continue if typ == nil: continue
typeAllowedCheck(a.info, typ, symkind) typeAllowedCheck(a.info, typ, symkind)
var tup = skipTypes(typ, {tyGenericInst}) var tup = skipTypes(typ, {tyGenericInst})
if a.kind == nkVarTuple: if a.kind == nkVarTuple:
if tup.kind != tyTuple: if tup.kind != tyTuple:
localError(a.info, errXExpected, "tuple") localError(a.info, errXExpected, "tuple")
elif length-2 != sonsLen(tup): elif length-2 != sonsLen(tup):
localError(a.info, errWrongNumberOfVariables) localError(a.info, errWrongNumberOfVariables)
else: else:
b = newNodeI(nkVarTuple, a.info) 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-2] = a.sons[length-2] # keep type desc for doc generator
b.sons[length-1] = def b.sons[length-1] = def
addSon(result, b) 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: a.kind == nkIdentDefs and a.len > 3:
message(a.info, warnEachIdentIsTuple) message(a.info, warnEachIdentIsTuple)
for j in countup(0, length-3): for j in countup(0, length-3):
var v = semIdentDef(c, a.sons[j], symkind) var v = semIdentDef(c, a.sons[j], symkind)
if sfGenSym notin v.flags: addInterfaceDecl(c, v) 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) let shadowed = findShadowedVar(c, v)
if shadowed != nil: if shadowed != nil:
shadowed.flags.incl(sfShadowed) shadowed.flags.incl(sfShadowed)
if shadowed.kind == skResult:
message(a.info, warnResultShadowed)
# a shadowed variable is an error unless it appears on the right # a shadowed variable is an error unless it appears on the right
# side of the '=': # side of the '=':
if warnShadowIdent in gNotes and not identWithin(def, v.name): 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 sfCompileTime in v.flags: hasCompileTime = true
if hasCompileTime: vm.setupCompileTimeVar(c.module, result) if hasCompileTime: vm.setupCompileTimeVar(c.module, result)
proc semConst(c: PContext, n: PNode): PNode = proc semConst(c: PContext, n: PNode): PNode =
result = copyNode(n) result = copyNode(n)
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i] var a = n.sons[i]
if gCmd == cmdIdeTools: suggestStmt(c, a) if gCmd == cmdIdeTools: suggestStmt(c, a)
if a.kind == nkCommentStmt: continue if a.kind == nkCommentStmt: continue
if (a.kind != nkConstDef): illFormedAst(a) if (a.kind != nkConstDef): illFormedAst(a)
checkSonsLen(a, 3) checkSonsLen(a, 3)
var v = semIdentDef(c, a.sons[0], skConst) var v = semIdentDef(c, a.sons[0], skConst)
@ -495,7 +498,7 @@ proc semForVars(c: PContext, n: PNode): PNode =
var iter = skipTypes(iterBase, {tyGenericInst}) var iter = skipTypes(iterBase, {tyGenericInst})
# length == 3 means that there is one for loop variable # length == 3 means that there is one for loop variable
# and thus no tuple unpacking: # and thus no tuple unpacking:
if iter.kind != tyTuple or length == 3: if iter.kind != tyTuple or length == 3:
if length == 3: if length == 3:
var v = symForVar(c, n.sons[0]) var v = symForVar(c, n.sons[0])
if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal) 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 = proc implicitIterator(c: PContext, it: string, arg: PNode): PNode =
result = newNodeI(nkCall, arg.info) result = newNodeI(nkCall, arg.info)
result.add(newIdentNode(it.getIdent, 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) result.add newDeref(arg)
else: else:
result.add arg result.add arg
result = semExprNoDeref(c, result, {efWantIterator}) result = semExprNoDeref(c, result, {efWantIterator})
proc semFor(c: PContext, n: PNode): PNode = proc semFor(c: PContext, n: PNode): PNode =
result = n result = n
checkMinSonsLen(n, 3) checkMinSonsLen(n, 3)
var length = sonsLen(n) var length = sonsLen(n)
@ -564,13 +567,13 @@ proc semFor(c: PContext, n: PNode): PNode =
result.typ = enforceVoidContext result.typ = enforceVoidContext
closeScope(c) closeScope(c)
proc semRaise(c: PContext, n: PNode): PNode = proc semRaise(c: PContext, n: PNode): PNode =
result = n result = n
checkSonsLen(n, 1) checkSonsLen(n, 1)
if n.sons[0].kind != nkEmpty: if n.sons[0].kind != nkEmpty:
n.sons[0] = semExprWithType(c, n.sons[0]) n.sons[0] = semExprWithType(c, n.sons[0])
var typ = n.sons[0].typ 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) localError(n.info, errExprCannotBeRaised)
proc addGenericParamListToScope(c: PContext, n: PNode) = proc addGenericParamListToScope(c: PContext, n: PNode) =
@ -580,13 +583,13 @@ proc addGenericParamListToScope(c: PContext, n: PNode) =
if a.kind == nkSym: addDecl(c, a.sym) if a.kind == nkSym: addDecl(c, a.sym)
else: illFormedAst(a) 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 # process the symbols on the left side for the whole type section, before
# we even look at the type definitions on the right # we even look at the type definitions on the right
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i] var a = n.sons[i]
if gCmd == cmdIdeTools: suggestStmt(c, a) if gCmd == cmdIdeTools: suggestStmt(c, a)
if a.kind == nkCommentStmt: continue if a.kind == nkCommentStmt: continue
if a.kind != nkTypeDef: illFormedAst(a) if a.kind != nkTypeDef: illFormedAst(a)
checkSonsLen(a, 3) checkSonsLen(a, 3)
var s = semIdentDef(c, a.sons[0], skType) var s = semIdentDef(c, a.sons[0], skType)
@ -599,29 +602,29 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
a.sons[0] = newSymNode(s) a.sons[0] = newSymNode(s)
proc typeSectionRightSidePass(c: PContext, n: PNode) = 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] var a = n.sons[i]
if a.kind == nkCommentStmt: continue if a.kind == nkCommentStmt: continue
if (a.kind != nkTypeDef): illFormedAst(a) if (a.kind != nkTypeDef): illFormedAst(a)
checkSonsLen(a, 3) checkSonsLen(a, 3)
if (a.sons[0].kind != nkSym): illFormedAst(a) if (a.sons[0].kind != nkSym): illFormedAst(a)
var s = a.sons[0].sym 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) localError(a.info, errImplOfXexpected, s.name.s)
if s.magic != mNone: processMagicType(c, 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, # We have a generic type declaration here. In generic types,
# symbol lookup needs to be done here. # symbol lookup needs to be done here.
openScope(c) openScope(c)
pushOwner(s) pushOwner(s)
if s.magic == mNone: s.typ.kind = tyGenericBody if s.magic == mNone: s.typ.kind = tyGenericBody
# XXX for generic type aliases this is not correct! We need the # XXX for generic type aliases this is not correct! We need the
# underlying Id really: # underlying Id really:
# #
# type # type
# TGObj[T] = object # TGObj[T] = object
# TAlias[T] = TGObj[T] # TAlias[T] = TGObj[T]
# #
s.typ.n = semGenericParamList(c, a.sons[1], s.typ) s.typ.n = semGenericParamList(c, a.sons[1], s.typ)
a.sons[1] = s.typ.n a.sons[1] = s.typ.n
s.typ.size = -1 # could not be computed properly 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 s.typ.sons[sonsLen(s.typ) - 1] = body
popOwner() popOwner()
closeScope(c) closeScope(c)
elif a.sons[2].kind != nkEmpty: elif a.sons[2].kind != nkEmpty:
# process the type's body: # process the type's body:
pushOwner(s) pushOwner(s)
var t = semTypeNode(c, a.sons[2], s.typ) var t = semTypeNode(c, a.sons[2], s.typ)
if s.typ == nil: if s.typ == nil:
s.typ = t s.typ = t
elif t != s.typ: elif t != s.typ:
# this can happen for e.g. tcan_alias_specialised_generic: # this can happen for e.g. tcan_alias_specialised_generic:
assignType(s.typ, t) assignType(s.typ, t)
#debug s.typ #debug s.typ
@ -676,23 +679,23 @@ proc checkForMetaFields(n: PNode) =
else: else:
internalAssert false internalAssert false
proc typeSectionFinalPass(c: PContext, n: PNode) = proc typeSectionFinalPass(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] var a = n.sons[i]
if a.kind == nkCommentStmt: continue if a.kind == nkCommentStmt: continue
if a.sons[0].kind != nkSym: illFormedAst(a) if a.sons[0].kind != nkSym: illFormedAst(a)
var s = a.sons[0].sym var s = a.sons[0].sym
# compute the type's size and check for illegal recursions: # 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}: if a.sons[2].kind in {nkSym, nkIdent, nkAccQuoted}:
# type aliases are hard: # type aliases are hard:
#MessageOut('for type ' + typeToString(s.typ)); #MessageOut('for type ' + typeToString(s.typ));
var t = semTypeNode(c, a.sons[2], nil) var t = semTypeNode(c, a.sons[2], nil)
if t.kind in {tyObject, tyEnum}: if t.kind in {tyObject, tyEnum}:
assignType(s.typ, t) assignType(s.typ, t)
s.typ.id = t.id # same id s.typ.id = t.id # same id
checkConstructedType(s.info, s.typ) 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) checkForMetaFields(s.typ.n)
let aa = a.sons[2] let aa = a.sons[2]
if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and 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: if s.typ.sons[0] != nil and s.typ.sons[0].kind == tyStmt:
localError(n.info, errGenerated, "invalid return type: 'stmt'") localError(n.info, errGenerated, "invalid return type: 'stmt'")
proc addParams(c: PContext, n: PNode, kind: TSymKind) = proc addParams(c: PContext, n: PNode, kind: TSymKind) =
for i in countup(1, sonsLen(n)-1): for i in countup(1, sonsLen(n)-1):
if n.sons[i].kind == nkSym: addParamOrResult(c, n.sons[i].sym, kind) if n.sons[i].kind == nkSym: addParamOrResult(c, n.sons[i].sym, kind)
else: illFormedAst(n) else: illFormedAst(n)
proc semBorrow(c: PContext, n: PNode, s: PSym) = proc semBorrow(c: PContext, n: PNode, s: PSym) =
# search for the correct alias: # search for the correct alias:
var b = searchForBorrowProc(c, c.currentScope.parent, s) var b = searchForBorrowProc(c, c.currentScope.parent, s)
if b != nil: if b != nil:
# store the alias: # store the alias:
n.sons[bodyPos] = newSymNode(b) n.sons[bodyPos] = newSymNode(b)
else: else:
localError(n.info, errNoSymbolToBorrowFromFound) localError(n.info, errNoSymbolToBorrowFromFound)
proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind) = proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind) =
if t != nil: if t != nil:
var s = newSym(skResult, getIdent"result", getCurrOwner(), info) var s = newSym(skResult, getIdent"result", getCurrOwner(), info)
s.typ = t s.typ = t
@ -742,7 +745,7 @@ proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind) =
addParamOrResult(c, s, owner) addParamOrResult(c, s, owner)
c.p.resultSym = s 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)) if c.p.resultSym != nil: addSon(n, newSymNode(c.p.resultSym))
proc copyExcept(n: PNode, i: int): PNode = proc copyExcept(n: PNode, i: int): PNode =
@ -786,7 +789,8 @@ proc semProcAnnotation(c: PContext, prc: PNode;
result = semStmt(c, x) result = semStmt(c, x)
# since a proc annotation can set pragmas, we process these here again. # since a proc annotation can set pragmas, we process these here again.
# This is required for SqueakNim-like export pragmas. # This is required for SqueakNim-like export pragmas.
if result[namePos].kind == nkSym and result[pragmasPos].kind != nkEmpty: if result.kind in procDefs and result[namePos].kind == nkSym and
result[pragmasPos].kind != nkEmpty:
pragma(c, result[namePos].sym, result[pragmasPos], validPragmas) pragma(c, result[namePos].sym, result[pragmasPos], validPragmas)
return return
@ -807,7 +811,7 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
pushOwner(s) pushOwner(s)
openScope(c) openScope(c)
var gp: PNode var gp: PNode
if n.sons[genericParamsPos].kind != nkEmpty: if n.sons[genericParamsPos].kind != nkEmpty:
n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos]) n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos])
gp = n.sons[genericParamsPos] gp = n.sons[genericParamsPos]
else: else:
@ -857,13 +861,13 @@ proc semDo(c: PContext, n: PNode, flags: TExprFlags): PNode =
proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
var n = n var n = n
n = replaceTypesInBody(c, pt, n) n = replaceTypesInBody(c, pt, n)
result = n result = n
n.sons[genericParamsPos] = emptyNode n.sons[genericParamsPos] = emptyNode
n.sons[paramsPos] = n.typ.n n.sons[paramsPos] = n.typ.n
openScope(c) openScope(c)
var s = n.sons[namePos].sym var s = n.sons[namePos].sym
pushOwner(s) pushOwner(s)
@ -876,7 +880,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
popProcCon(c) popProcCon(c)
popOwner() popOwner()
closeScope(c) closeScope(c)
s.ast = result s.ast = result
# alternative variant (not quite working): # alternative variant (not quite working):
@ -922,7 +926,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
else: break else: break
if t.kind in {tyObject, tyDistinct, tyEnum}: if t.kind in {tyObject, tyDistinct, tyEnum}:
if t.deepCopy.isNil: t.deepCopy = s if t.deepCopy.isNil: t.deepCopy = s
else: else:
localError(n.info, errGenerated, localError(n.info, errGenerated,
"cannot bind another 'deepCopy' to: " & typeToString(t)) "cannot bind another 'deepCopy' to: " & typeToString(t))
else: else:
@ -989,10 +993,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
pushOwner(s) pushOwner(s)
openScope(c) openScope(c)
var gp: PNode var gp: PNode
if n.sons[genericParamsPos].kind != nkEmpty: if n.sons[genericParamsPos].kind != nkEmpty:
n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos]) n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos])
gp = n.sons[genericParamsPos] gp = n.sons[genericParamsPos]
else: else:
gp = newNodeI(nkGenericParams, n.info) gp = newNodeI(nkGenericParams, n.info)
# process parameters: # process parameters:
if n.sons[paramsPos].kind != nkEmpty: if n.sons[paramsPos].kind != nkEmpty:
@ -1009,7 +1013,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
n.sons[patternPos] = semPattern(c, n.sons[patternPos]) n.sons[patternPos] = semPattern(c, n.sons[patternPos])
if s.kind in skIterators: if s.kind in skIterators:
s.typ.flags.incl(tfIterator) s.typ.flags.incl(tfIterator)
var proto = searchForProc(c, s.scope, s) var proto = searchForProc(c, s.scope, s)
if proto == nil: if proto == nil:
if s.kind == skClosureIterator: s.typ.callConv = ccClosure if s.kind == skClosureIterator: s.typ.callConv = ccClosure
@ -1027,14 +1031,14 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
else: else:
implicitPragmas(c, s, n, validPragmas) implicitPragmas(c, s, n, validPragmas)
else: else:
if n.sons[pragmasPos].kind != nkEmpty: if n.sons[pragmasPos].kind != nkEmpty:
localError(n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProc) localError(n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProc)
if sfForward notin proto.flags: if sfForward notin proto.flags:
wrongRedefinition(n.info, proto.name.s) wrongRedefinition(n.info, proto.name.s)
excl(proto.flags, sfForward) excl(proto.flags, sfForward)
closeScope(c) # close scope with wrong parameter symbols closeScope(c) # close scope with wrong parameter symbols
openScope(c) # open scope for old (correct) 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]) addGenericParamListToScope(c, proto.ast.sons[genericParamsPos])
addParams(c, proto.typ.n, proto.kind) addParams(c, proto.typ.n, proto.kind)
proto.info = s.info # more accurate line information proto.info = s.info # more accurate line information
@ -1084,7 +1088,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
n.sons[bodyPos] = ast.emptyNode n.sons[bodyPos] = ast.emptyNode
else: else:
if proto != nil: localError(n.info, errImplOfXexpected, proto.name.s) 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) incl(s.flags, sfForward)
elif sfBorrow in s.flags: semBorrow(c, n, s) elif sfBorrow in s.flags: semBorrow(c, n, s)
sideEffectsCheck(c, s) sideEffectsCheck(c, s)
@ -1122,14 +1126,14 @@ proc semIterator(c: PContext, n: PNode): PNode =
else: else:
s.typ.callConv = ccInline s.typ.callConv = ccInline
when false: when false:
if s.typ.callConv != ccInline: if s.typ.callConv != ccInline:
s.typ.callConv = ccClosure s.typ.callConv = ccClosure
# and they always at least use the 'env' for the state field: # and they always at least use the 'env' for the state field:
incl(s.typ.flags, tfCapturesEnv) incl(s.typ.flags, tfCapturesEnv)
if n.sons[bodyPos].kind == nkEmpty and s.magic == mNone: if n.sons[bodyPos].kind == nkEmpty and s.magic == mNone:
localError(n.info, errImplOfXexpected, s.name.s) 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) result = semProcAux(c, n, skProc, procPragmas)
proc hasObjParam(s: PSym): bool = proc hasObjParam(s: PSym): bool =
@ -1142,10 +1146,10 @@ proc finishMethod(c: PContext, s: PSym) =
if hasObjParam(s): if hasObjParam(s):
methodDef(s, false) 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") if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "method")
result = semProcAux(c, n, skMethod, methodPragmas) result = semProcAux(c, n, skMethod, methodPragmas)
var s = result.sons[namePos].sym var s = result.sons[namePos].sym
if not isGenericRoutine(s) and result.sons[bodyPos].kind != nkEmpty: if not isGenericRoutine(s) and result.sons[bodyPos].kind != nkEmpty:
if hasObjParam(s): if hasObjParam(s):
@ -1153,7 +1157,7 @@ proc semMethod(c: PContext, n: PNode): PNode =
else: else:
localError(n.info, errXNeedsParamObjectType, "method") 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") if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "converter")
checkSonsLen(n, bodyPos + 1) checkSonsLen(n, bodyPos + 1)
result = semProcAux(c, n, skConverter, converterPragmas) result = semProcAux(c, n, skConverter, converterPragmas)
@ -1163,7 +1167,7 @@ proc semConverterDef(c: PContext, n: PNode): PNode =
if sonsLen(t) != 2: localError(n.info, errXRequiresOneArgument, "converter") if sonsLen(t) != 2: localError(n.info, errXRequiresOneArgument, "converter")
addConverter(c, s) addConverter(c, s)
proc semMacroDef(c: PContext, n: PNode): PNode = proc semMacroDef(c: PContext, n: PNode): PNode =
checkSonsLen(n, bodyPos + 1) checkSonsLen(n, bodyPos + 1)
result = semProcAux(c, n, skMacro, macroPragmas) result = semProcAux(c, n, skMacro, macroPragmas)
var s = result.sons[namePos].sym var s = result.sons[namePos].sym
@ -1171,23 +1175,23 @@ proc semMacroDef(c: PContext, n: PNode): PNode =
if t.sons[0] == nil: localError(n.info, errXNeedsReturnType, "macro") if t.sons[0] == nil: localError(n.info, errXNeedsReturnType, "macro")
if n.sons[bodyPos].kind == nkEmpty: if n.sons[bodyPos].kind == nkEmpty:
localError(n.info, errImplOfXexpected, s.name.s) localError(n.info, errImplOfXexpected, s.name.s)
proc evalInclude(c: PContext, n: PNode): PNode = proc evalInclude(c: PContext, n: PNode): PNode =
result = newNodeI(nkStmtList, n.info) result = newNodeI(nkStmtList, n.info)
addSon(result, n) 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]) var f = checkModuleName(n.sons[i])
if f != InvalidFileIDX: if f != InvalidFileIDX:
if containsOrIncl(c.includedFiles, f): if containsOrIncl(c.includedFiles, f):
localError(n.info, errRecursiveDependencyX, f.toFilename) localError(n.info, errRecursiveDependencyX, f.toFilename)
else: else:
addSon(result, semStmt(c, gIncludeFile(c.module, f))) addSon(result, semStmt(c, gIncludeFile(c.module, f)))
excl(c.includedFiles, f) excl(c.includedFiles, f)
proc setLine(n: PNode, info: TLineInfo) = proc setLine(n: PNode, info: TLineInfo) =
for i in 0 .. <safeLen(n): setLine(n.sons[i], info) for i in 0 .. <safeLen(n): setLine(n.sons[i], info)
n.info = info n.info = info
proc semPragmaBlock(c: PContext, n: PNode): PNode = proc semPragmaBlock(c: PContext, n: PNode): PNode =
let pragmaList = n.sons[0] let pragmaList = n.sons[0]
pragma(c, nil, pragmaList, exprPragmas) pragma(c, nil, pragmaList, exprPragmas)
@ -1196,7 +1200,7 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode =
for i in 0 .. <pragmaList.len: for i in 0 .. <pragmaList.len:
case whichPragma(pragmaList.sons[i]) case whichPragma(pragmaList.sons[i])
of wLine: setLine(result, pragmaList.sons[i].info) of wLine: setLine(result, pragmaList.sons[i].info)
of wLocks: of wLocks:
result = n result = n
result.typ = n.sons[1].typ result.typ = n.sons[1].typ
else: discard else: discard
@ -1311,8 +1315,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
inner.addSon(semStmtList(c, rest, flags)) inner.addSon(semStmtList(c, rest, flags))
n.sons.setLen(i+1) n.sons.setLen(i+1)
return return
of LastBlockStmts: of LastBlockStmts:
for j in countup(i + 1, length - 1): for j in countup(i + 1, length - 1):
case n.sons[j].kind case n.sons[j].kind
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: discard of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: discard
else: localError(n.sons[j].info, errStmtInvalidAfterReturn) else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
@ -1334,7 +1338,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
# "Last expression must be explicitly returned if it " & # "Last expression must be explicitly returned if it " &
# "is discardable or discarded") # "is discardable or discarded")
proc semStmt(c: PContext, n: PNode): PNode = proc semStmt(c: PContext, n: PNode): PNode =
# now: simply an alias: # now: simply an alias:
result = semExprNoType(c, n) result = semExprNoType(c, n)

View file

@ -10,14 +10,14 @@
# this module does the semantic checking of type declarations # this module does the semantic checking of type declarations
# included from sem.nim # included from sem.nim
proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType = proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType =
if prev == nil: if prev == nil:
result = newTypeS(kind, c) result = newTypeS(kind, c)
else: else:
result = prev result = prev
if result.kind == tyForward: result.kind = kind if result.kind == tyForward: result.kind = kind
proc newConstraint(c: PContext, k: TTypeKind): PType = proc newConstraint(c: PContext, k: TTypeKind): PType =
result = newTypeS(tyBuiltInTypeClass, c) result = newTypeS(tyBuiltInTypeClass, c)
result.addSonSkipIntLit(newTypeS(k, c)) result.addSonSkipIntLit(newTypeS(k, c))
@ -32,22 +32,22 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tyEnum, prev, c) result = newOrPrevType(tyEnum, prev, c)
result.n = newNodeI(nkEnumTy, n.info) result.n = newNodeI(nkEnumTy, n.info)
checkMinSonsLen(n, 1) checkMinSonsLen(n, 1)
if n.sons[0].kind != nkEmpty: if n.sons[0].kind != nkEmpty:
base = semTypeNode(c, n.sons[0].sons[0], nil) base = semTypeNode(c, n.sons[0].sons[0], nil)
if base.kind != tyEnum: if base.kind != tyEnum:
localError(n.sons[0].info, errInheritanceOnlyWithEnums) localError(n.sons[0].info, errInheritanceOnlyWithEnums)
counter = lastOrd(base) + 1 counter = lastOrd(base) + 1
rawAddSon(result, base) rawAddSon(result, base)
let isPure = result.sym != nil and sfPure in result.sym.flags let isPure = result.sym != nil and sfPure in result.sym.flags
var hasNull = false var hasNull = false
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
case n.sons[i].kind case n.sons[i].kind
of nkEnumFieldDef: of nkEnumFieldDef:
e = newSymS(skEnumField, n.sons[i].sons[0], c) e = newSymS(skEnumField, n.sons[i].sons[0], c)
var v = semConstExpr(c, n.sons[i].sons[1]) var v = semConstExpr(c, n.sons[i].sons[1])
var strVal: PNode = nil var strVal: PNode = nil
case skipTypes(v.typ, abstractInst-{tyTypeDesc}).kind case skipTypes(v.typ, abstractInst-{tyTypeDesc}).kind
of tyTuple: of tyTuple:
if sonsLen(v) == 2: if sonsLen(v) == 2:
strVal = v.sons[1] # second tuple part is the string value strVal = v.sons[1] # second tuple part is the string value
if skipTypes(strVal.typ, abstractInst).kind in {tyString, tyCString}: if skipTypes(strVal.typ, abstractInst).kind in {tyString, tyCString}:
@ -63,14 +63,14 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
x = getOrdValue(v) x = getOrdValue(v)
if i != 1: if i != 1:
if x != counter: incl(result.flags, tfEnumHasHoles) if x != counter: incl(result.flags, tfEnumHasHoles)
if x < counter: if x < counter:
localError(n.sons[i].info, errInvalidOrderInEnumX, e.name.s) localError(n.sons[i].info, errInvalidOrderInEnumX, e.name.s)
x = counter x = counter
e.ast = strVal # might be nil e.ast = strVal # might be nil
counter = x counter = x
of nkSym: of nkSym:
e = n.sons[i].sym e = n.sons[i].sym
of nkIdent, nkAccQuoted: of nkIdent, nkAccQuoted:
e = newSymS(skEnumField, n.sons[i], c) e = newSymS(skEnumField, n.sons[i], c)
else: else:
illFormedAst(n[i]) illFormedAst(n[i])
@ -87,28 +87,28 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
inc(counter) inc(counter)
if not hasNull: incl(result.flags, tfNeedsInit) if not hasNull: incl(result.flags, tfNeedsInit)
proc semSet(c: PContext, n: PNode, prev: PType): PType = proc semSet(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tySet, prev, c) result = newOrPrevType(tySet, prev, c)
if sonsLen(n) == 2: if sonsLen(n) == 2:
var base = semTypeNode(c, n.sons[1], nil) var base = semTypeNode(c, n.sons[1], nil)
addSonSkipIntLit(result, base) addSonSkipIntLit(result, base)
if base.kind == tyGenericInst: base = lastSon(base) if base.kind == tyGenericInst: base = lastSon(base)
if base.kind != tyGenericParam: if base.kind != tyGenericParam:
if not isOrdinalType(base): if not isOrdinalType(base):
localError(n.info, errOrdinalTypeExpected) localError(n.info, errOrdinalTypeExpected)
elif lengthOrd(base) > MaxSetElements: elif lengthOrd(base) > MaxSetElements:
localError(n.info, errSetTooBig) localError(n.info, errSetTooBig)
else: else:
localError(n.info, errXExpectsOneTypeParam, "set") localError(n.info, errXExpectsOneTypeParam, "set")
addSonSkipIntLit(result, errorType(c)) addSonSkipIntLit(result, errorType(c))
proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string, proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string,
prev: PType): PType = prev: PType): PType =
result = newOrPrevType(kind, prev, c) result = newOrPrevType(kind, prev, c)
if sonsLen(n) == 2: if sonsLen(n) == 2:
var base = semTypeNode(c, n.sons[1], nil) var base = semTypeNode(c, n.sons[1], nil)
addSonSkipIntLit(result, base) addSonSkipIntLit(result, base)
else: else:
localError(n.info, errXExpectsOneTypeParam, kindStr) localError(n.info, errXExpectsOneTypeParam, kindStr)
addSonSkipIntLit(result, errorType(c)) addSonSkipIntLit(result, errorType(c))
@ -140,23 +140,23 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
var base = semTypeNode(c, n.lastSon, nil) var base = semTypeNode(c, n.lastSon, nil)
addSonSkipIntLit(result, base) addSonSkipIntLit(result, base)
proc semVarType(c: PContext, n: PNode, prev: PType): PType = proc semVarType(c: PContext, n: PNode, prev: PType): PType =
if sonsLen(n) == 1: if sonsLen(n) == 1:
result = newOrPrevType(tyVar, prev, c) result = newOrPrevType(tyVar, prev, c)
var base = semTypeNode(c, n.sons[0], nil) var base = semTypeNode(c, n.sons[0], nil)
if base.kind == tyVar: if base.kind == tyVar:
localError(n.info, errVarVarTypeNotAllowed) localError(n.info, errVarVarTypeNotAllowed)
base = base.sons[0] base = base.sons[0]
addSonSkipIntLit(result, base) addSonSkipIntLit(result, base)
else: else:
result = newConstraint(c, tyVar) result = newConstraint(c, tyVar)
proc semDistinct(c: PContext, n: PNode, prev: PType): PType = proc semDistinct(c: PContext, n: PNode, prev: PType): PType =
if n.len == 0: return newConstraint(c, tyDistinct) if n.len == 0: return newConstraint(c, tyDistinct)
result = newOrPrevType(tyDistinct, prev, c) result = newOrPrevType(tyDistinct, prev, c)
addSonSkipIntLit(result, semTypeNode(c, n.sons[0], nil)) addSonSkipIntLit(result, semTypeNode(c, n.sons[0], nil))
if n.len > 1: result.n = n[1] if n.len > 1: result.n = n[1]
proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
assert isRange(n) assert isRange(n)
checkSonsLen(n, 3) checkSonsLen(n, 3)
@ -164,11 +164,11 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
result.n = newNodeI(nkRange, n.info) result.n = newNodeI(nkRange, n.info)
if (n[1].kind == nkEmpty) or (n[2].kind == nkEmpty): if (n[1].kind == nkEmpty) or (n[2].kind == nkEmpty):
localError(n.info, errRangeIsEmpty) localError(n.info, errRangeIsEmpty)
var range: array[2, PNode] var range: array[2, PNode]
range[0] = semExprWithType(c, n[1], {efDetermineType}) range[0] = semExprWithType(c, n[1], {efDetermineType})
range[1] = semExprWithType(c, n[2], {efDetermineType}) range[1] = semExprWithType(c, n[2], {efDetermineType})
var rangeT: array[2, PType] var rangeT: array[2, PType]
for i in 0..1: for i in 0..1:
rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit
@ -179,13 +179,13 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
localError(n.info, errOrdinalTypeExpected) localError(n.info, errOrdinalTypeExpected)
elif enumHasHoles(rangeT[0]): elif enumHasHoles(rangeT[0]):
localError(n.info, errEnumXHasHoles, rangeT[0].sym.name.s) localError(n.info, errEnumXHasHoles, rangeT[0].sym.name.s)
for i in 0..1: for i in 0..1:
if hasGenericArguments(range[i]): if hasGenericArguments(range[i]):
result.n.addSon makeStaticExpr(c, range[i]) result.n.addSon makeStaticExpr(c, range[i])
else: else:
result.n.addSon semConstExpr(c, range[i]) result.n.addSon semConstExpr(c, range[i])
if weakLeValue(result.n[0], result.n[1]) == impNo: if weakLeValue(result.n[0], result.n[1]) == impNo:
localError(n.info, errRangeIsEmpty) localError(n.info, errRangeIsEmpty)
@ -201,10 +201,10 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType =
incl(result.flags, tfNeedsInit) incl(result.flags, tfNeedsInit)
elif n.sons[1].kind in {nkCharLit..nkUInt64Lit} and n.sons[1].intVal < 0: elif n.sons[1].kind in {nkCharLit..nkUInt64Lit} and n.sons[1].intVal < 0:
incl(result.flags, tfNeedsInit) incl(result.flags, tfNeedsInit)
elif n.sons[0].kind in {nkFloatLit..nkFloat64Lit} and elif n.sons[0].kind in {nkFloatLit..nkFloat64Lit} and
n.sons[0].floatVal > 0.0: n.sons[0].floatVal > 0.0:
incl(result.flags, tfNeedsInit) incl(result.flags, tfNeedsInit)
elif n.sons[1].kind in {nkFloatLit..nkFloat64Lit} and elif n.sons[1].kind in {nkFloatLit..nkFloat64Lit} and
n.sons[1].floatVal < 0.0: n.sons[1].floatVal < 0.0:
incl(result.flags, tfNeedsInit) incl(result.flags, tfNeedsInit)
else: else:
@ -243,13 +243,13 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
else: else:
let x = semConstExpr(c, e) let x = semConstExpr(c, e)
if x.kind in {nkIntLit..nkUInt64Lit}: if x.kind in {nkIntLit..nkUInt64Lit}:
result = makeRangeType(c, 0, x.intVal-1, n.info, result = makeRangeType(c, 0, x.intVal-1, n.info,
x.typ.skipTypes({tyTypeDesc})) x.typ.skipTypes({tyTypeDesc}))
else: else:
result = x.typ.skipTypes({tyTypeDesc}) result = x.typ.skipTypes({tyTypeDesc})
#localError(n[1].info, errConstExprExpected) #localError(n[1].info, errConstExprExpected)
proc semArray(c: PContext, n: PNode, prev: PType): PType = proc semArray(c: PContext, n: PNode, prev: PType): PType =
var base: PType var base: PType
result = newOrPrevType(tyArray, prev, c) result = newOrPrevType(tyArray, prev, c)
if sonsLen(n) == 3: if sonsLen(n) == 3:
@ -260,20 +260,20 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
if indx.kind notin {tyGenericParam, tyStatic, tyFromExpr}: if indx.kind notin {tyGenericParam, tyStatic, tyFromExpr}:
if not isOrdinalType(indx): if not isOrdinalType(indx):
localError(n.sons[1].info, errOrdinalTypeExpected) localError(n.sons[1].info, errOrdinalTypeExpected)
elif enumHasHoles(indx): elif enumHasHoles(indx):
localError(n.sons[1].info, errEnumXHasHoles, indx.sym.name.s) localError(n.sons[1].info, errEnumXHasHoles, indx.sym.name.s)
base = semTypeNode(c, n.sons[2], nil) base = semTypeNode(c, n.sons[2], nil)
addSonSkipIntLit(result, base) addSonSkipIntLit(result, base)
else: else:
localError(n.info, errArrayExpectsTwoTypeParams) localError(n.info, errArrayExpectsTwoTypeParams)
result = newOrPrevType(tyError, prev, c) result = newOrPrevType(tyError, prev, c)
proc semOrdinal(c: PContext, n: PNode, prev: PType): PType = proc semOrdinal(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tyOrdinal, prev, c) result = newOrPrevType(tyOrdinal, prev, c)
if sonsLen(n) == 2: if sonsLen(n) == 2:
var base = semTypeNode(c, n.sons[1], nil) var base = semTypeNode(c, n.sons[1], nil)
if base.kind != tyGenericParam: if base.kind != tyGenericParam:
if not isOrdinalType(base): if not isOrdinalType(base):
localError(n.sons[1].info, errOrdinalTypeExpected) localError(n.sons[1].info, errOrdinalTypeExpected)
addSonSkipIntLit(result, base) addSonSkipIntLit(result, base)
else: else:
@ -281,7 +281,7 @@ proc semOrdinal(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tyError, prev, c) result = newOrPrevType(tyError, prev, c)
proc semTypeIdent(c: PContext, n: PNode): PSym = proc semTypeIdent(c: PContext, n: PNode): PSym =
if n.kind == nkSym: if n.kind == nkSym:
result = n.sym result = n.sym
else: else:
when defined(nimfix): when defined(nimfix):
@ -307,7 +307,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
result = result.typ.sym.copySym result = result.typ.sym.copySym
result.typ = copyType(result.typ, result.typ.owner, true) result.typ = copyType(result.typ, result.typ.owner, true)
result.typ.flags.incl tfUnresolved result.typ.flags.incl tfUnresolved
if result.kind == skGenericParam: if result.kind == skGenericParam:
if result.typ.kind == tyGenericParam and result.typ.len == 0 and if result.typ.kind == tyGenericParam and result.typ.len == 0 and
tfWildcard in result.typ.flags: tfWildcard in result.typ.flags:
@ -319,7 +319,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
localError(n.info, errTypeExpected) localError(n.info, errTypeExpected)
return errorSym(c, n) return errorSym(c, n)
if result.kind != skType: if result.kind != skType:
# this implements the wanted ``var v: V, x: V`` feature ... # this implements the wanted ``var v: V, x: V`` feature ...
var ov: TOverloadIter var ov: TOverloadIter
var amb = initOverloadIter(ov, c, n) var amb = initOverloadIter(ov, c, n)
@ -344,48 +344,54 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
else: else:
localError(n.info, errIdentifierExpected) localError(n.info, errIdentifierExpected)
result = errorSym(c, n) result = errorSym(c, n)
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 = proc semTuple(c: PContext, n: PNode, prev: PType): PType =
if n.sonsLen == 0: return newConstraint(c, tyTuple)
var typ: PType var typ: PType
result = newOrPrevType(tyTuple, prev, c) result = newOrPrevType(tyTuple, prev, c)
result.n = newNodeI(nkRecList, n.info) result.n = newNodeI(nkRecList, n.info)
var check = initIntSet() var check = initIntSet()
var counter = 0 var counter = 0
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i] var a = n.sons[i]
if (a.kind != nkIdentDefs): illFormedAst(a) if (a.kind != nkIdentDefs): illFormedAst(a)
checkMinSonsLen(a, 3) checkMinSonsLen(a, 3)
var length = sonsLen(a) var length = sonsLen(a)
if a.sons[length - 2].kind != nkEmpty: if a.sons[length - 2].kind != nkEmpty:
typ = semTypeNode(c, a.sons[length - 2], nil) typ = semTypeNode(c, a.sons[length - 2], nil)
else: else:
localError(a.info, errTypeExpected) localError(a.info, errTypeExpected)
typ = errorType(c) typ = errorType(c)
if a.sons[length - 1].kind != nkEmpty: if a.sons[length - 1].kind != nkEmpty:
localError(a.sons[length - 1].info, errInitHereNotAllowed) localError(a.sons[length - 1].info, errInitHereNotAllowed)
for j in countup(0, length - 3): for j in countup(0, length - 3):
var field = newSymG(skField, a.sons[j], c) var field = newSymG(skField, a.sons[j], c)
field.typ = typ field.typ = typ
field.position = counter field.position = counter
inc(counter) inc(counter)
if containsOrIncl(check, field.name.id): if containsOrIncl(check, field.name.id):
localError(a.sons[j].info, errAttemptToRedefine, field.name.s) localError(a.sons[j].info, errAttemptToRedefine, field.name.s)
else: else:
addSon(result.n, newSymNode(field)) addSon(result.n, newSymNode(field))
addSonSkipIntLit(result, typ) addSonSkipIntLit(result, typ)
if gCmd == cmdPretty: styleCheckDef(a.sons[j].info, field) if gCmd == cmdPretty: styleCheckDef(a.sons[j].info, field)
proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
allowed: TSymFlags): PSym = allowed: TSymFlags): PSym =
# identifier with visibility # identifier with visibility
if n.kind == nkPostfix: if n.kind == nkPostfix:
if sonsLen(n) == 2 and n.sons[0].kind == nkIdent: if sonsLen(n) == 2 and n.sons[0].kind == nkIdent:
# for gensym'ed identifiers the identifier may already have been # for gensym'ed identifiers the identifier may already have been
# transformed to a symbol and we need to use that here: # transformed to a symbol and we need to use that here:
result = newSymG(kind, n.sons[1], c) result = newSymG(kind, n.sons[1], c)
var v = n.sons[0].ident var v = n.sons[0].ident
if sfExported in allowed and v.id == ord(wStar): if sfExported in allowed and v.id == ord(wStar):
incl(result.flags, sfExported) incl(result.flags, sfExported)
else: else:
localError(n.sons[0].info, errInvalidVisibilityX, v.s) localError(n.sons[0].info, errInvalidVisibilityX, v.s)
@ -393,7 +399,7 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
illFormedAst(n) illFormedAst(n)
else: else:
result = newSymG(kind, n, c) result = newSymG(kind, n, c)
proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
allowed: TSymFlags): PSym = allowed: TSymFlags): PSym =
if n.kind == nkPragmaExpr: if n.kind == nkPragmaExpr:
@ -415,31 +421,31 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) = proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) =
let ex = t[branchIndex][currentEx].skipConv let ex = t[branchIndex][currentEx].skipConv
for i in countup(1, branchIndex): for i in countup(1, branchIndex):
for j in countup(0, sonsLen(t.sons[i]) - 2): for j in countup(0, sonsLen(t.sons[i]) - 2):
if i == branchIndex and j == currentEx: break if i == branchIndex and j == currentEx: break
if overlap(t.sons[i].sons[j].skipConv, ex): if overlap(t.sons[i].sons[j].skipConv, ex):
localError(ex.info, errDuplicateCaseLabel) localError(ex.info, errDuplicateCaseLabel)
proc semBranchRange(c: PContext, t, a, b: PNode, covered: var BiggestInt): PNode = proc semBranchRange(c: PContext, t, a, b: PNode, covered: var BiggestInt): PNode =
checkMinSonsLen(t, 1) checkMinSonsLen(t, 1)
let ac = semConstExpr(c, a) let ac = semConstExpr(c, a)
let bc = semConstExpr(c, b) let bc = semConstExpr(c, b)
let at = fitNode(c, t.sons[0].typ, ac).skipConvTakeType let at = fitNode(c, t.sons[0].typ, ac).skipConvTakeType
let bt = fitNode(c, t.sons[0].typ, bc).skipConvTakeType let bt = fitNode(c, t.sons[0].typ, bc).skipConvTakeType
result = newNodeI(nkRange, a.info) result = newNodeI(nkRange, a.info)
result.add(at) result.add(at)
result.add(bt) result.add(bt)
if emptyRange(ac, bc): localError(b.info, errRangeIsEmpty) if emptyRange(ac, bc): localError(b.info, errRangeIsEmpty)
else: covered = covered + getOrdValue(bc) - getOrdValue(ac) + 1 else: covered = covered + getOrdValue(bc) - getOrdValue(ac) + 1
proc semCaseBranchRange(c: PContext, t, b: PNode, proc semCaseBranchRange(c: PContext, t, b: PNode,
covered: var BiggestInt): PNode = covered: var BiggestInt): PNode =
checkSonsLen(b, 3) checkSonsLen(b, 3)
result = semBranchRange(c, t, b.sons[1], b.sons[2], covered) result = semBranchRange(c, t, b.sons[1], b.sons[2], covered)
proc semCaseBranchSetElem(c: PContext, t, b: PNode, proc semCaseBranchSetElem(c: PContext, t, b: PNode,
covered: var BiggestInt): PNode = covered: var BiggestInt): PNode =
if isRange(b): if isRange(b):
checkSonsLen(b, 3) checkSonsLen(b, 3)
result = semBranchRange(c, t, b.sons[1], b.sons[2], covered) result = semBranchRange(c, t, b.sons[1], b.sons[2], covered)
@ -450,10 +456,10 @@ proc semCaseBranchSetElem(c: PContext, t, b: PNode,
result = fitNode(c, t.sons[0].typ, b) result = fitNode(c, t.sons[0].typ, b)
inc(covered) inc(covered)
proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int, proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
covered: var BiggestInt) = covered: var BiggestInt) =
for i in countup(0, sonsLen(branch) - 2): for i in countup(0, sonsLen(branch) - 2):
var b = branch.sons[i] var b = branch.sons[i]
if b.kind == nkRange: if b.kind == nkRange:
branch.sons[i] = b branch.sons[i] = b
@ -480,7 +486,7 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
var L = branch.len var L = branch.len
swap(branch.sons[L-2], branch.sons[L-1]) swap(branch.sons[L-2], branch.sons[L-1])
checkForOverlap(c, t, i, branchIndex) checkForOverlap(c, t, i, branchIndex)
proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
father: PNode, rectype: PType) father: PNode, rectype: PType)
proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int, proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
@ -514,11 +520,11 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
else: illFormedAst(n) else: illFormedAst(n)
delSon(b, sonsLen(b) - 1) delSon(b, sonsLen(b) - 1)
semRecordNodeAux(c, lastSon(n.sons[i]), check, pos, b, rectype) semRecordNodeAux(c, lastSon(n.sons[i]), check, pos, b, rectype)
if chckCovered and (covered != lengthOrd(a.sons[0].typ)): if chckCovered and (covered != lengthOrd(a.sons[0].typ)):
localError(a.info, errNotAllCasesCovered) localError(a.info, errNotAllCasesCovered)
addSon(father, a) addSon(father, a)
proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
father: PNode, rectype: PType) = father: PNode, rectype: PType) =
if n == nil: return if n == nil: return
case n.kind case n.kind
@ -556,12 +562,12 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
semRecordNodeAux(c, branch, check, pos, father, rectype) semRecordNodeAux(c, branch, check, pos, father, rectype)
of nkRecCase: of nkRecCase:
semRecordCase(c, n, check, pos, father, rectype) semRecordCase(c, n, check, pos, father, rectype)
of nkNilLit: of nkNilLit:
if father.kind != nkRecList: addSon(father, newNodeI(nkRecList, n.info)) if father.kind != nkRecList: addSon(father, newNodeI(nkRecList, n.info))
of nkRecList: of nkRecList:
# attempt to keep the nesting at a sane level: # attempt to keep the nesting at a sane level:
var a = if father.kind == nkRecList: father else: copyNode(n) var a = if father.kind == nkRecList: father else: copyNode(n)
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
semRecordNodeAux(c, n.sons[i], check, pos, a, rectype) semRecordNodeAux(c, n.sons[i], check, pos, a, rectype)
if a != father: addSon(father, a) if a != father: addSon(father, a)
of nkIdentDefs: of nkIdentDefs:
@ -570,10 +576,10 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
var a: PNode var a: PNode
if father.kind != nkRecList and length>=4: a = newNodeI(nkRecList, n.info) if father.kind != nkRecList and length>=4: a = newNodeI(nkRecList, n.info)
else: a = ast.emptyNode else: a = ast.emptyNode
if n.sons[length-1].kind != nkEmpty: if n.sons[length-1].kind != nkEmpty:
localError(n.sons[length-1].info, errInitHereNotAllowed) localError(n.sons[length-1].info, errInitHereNotAllowed)
var typ: PType var typ: PType
if n.sons[length-2].kind == nkEmpty: if n.sons[length-2].kind == nkEmpty:
localError(n.info, errTypeExpected) localError(n.info, errTypeExpected)
typ = errorType(c) typ = errorType(c)
else: else:
@ -586,7 +592,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
f.typ = typ f.typ = typ
f.position = pos f.position = pos
if (rec != nil) and ({sfImportc, sfExportc} * rec.flags != {}) and if (rec != nil) and ({sfImportc, sfExportc} * rec.flags != {}) and
(f.loc.r == nil): (f.loc.r == nil):
f.loc.r = toRope(f.name.s) f.loc.r = toRope(f.name.s)
f.flags = f.flags + ({sfImportc, sfExportc} * rec.flags) f.flags = f.flags + ({sfImportc, sfExportc} * rec.flags)
inc(pos) inc(pos)
@ -598,8 +604,8 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
if a.kind != nkEmpty: addSon(father, a) if a.kind != nkEmpty: addSon(father, a)
of nkEmpty: discard of nkEmpty: discard
else: illFormedAst(n) else: illFormedAst(n)
proc addInheritedFieldsAux(c: PContext, check: var IntSet, pos: var int, proc addInheritedFieldsAux(c: PContext, check: var IntSet, pos: var int,
n: PNode) = n: PNode) =
case n.kind case n.kind
of nkRecCase: of nkRecCase:
@ -618,31 +624,31 @@ proc addInheritedFieldsAux(c: PContext, check: var IntSet, pos: var int,
inc(pos) inc(pos)
else: internalError(n.info, "addInheritedFieldsAux()") else: internalError(n.info, "addInheritedFieldsAux()")
proc skipGenericInvocation(t: PType): PType {.inline.} = proc skipGenericInvocation(t: PType): PType {.inline.} =
result = t result = t
if result.kind == tyGenericInvocation: if result.kind == tyGenericInvocation:
result = result.sons[0] result = result.sons[0]
if result.kind == tyGenericBody: if result.kind == tyGenericBody:
result = lastSon(result) result = lastSon(result)
proc addInheritedFields(c: PContext, check: var IntSet, pos: var int, proc addInheritedFields(c: PContext, check: var IntSet, pos: var int,
obj: PType) = obj: PType) =
assert obj.kind == tyObject assert obj.kind == tyObject
if (sonsLen(obj) > 0) and (obj.sons[0] != nil): if (sonsLen(obj) > 0) and (obj.sons[0] != nil):
addInheritedFields(c, check, pos, obj.sons[0].skipGenericInvocation) addInheritedFields(c, check, pos, obj.sons[0].skipGenericInvocation)
addInheritedFieldsAux(c, check, pos, obj.n) addInheritedFieldsAux(c, check, pos, obj.n)
proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
if n.sonsLen == 0: return newConstraint(c, tyObject) if n.sonsLen == 0: return newConstraint(c, tyObject)
var check = initIntSet() var check = initIntSet()
var pos = 0 var pos = 0
var base: PType = nil var base: PType = nil
# n.sons[0] contains the pragmas (if any). We process these later... # n.sons[0] contains the pragmas (if any). We process these later...
checkSonsLen(n, 3) checkSonsLen(n, 3)
if n.sons[1].kind != nkEmpty: if n.sons[1].kind != nkEmpty:
base = skipTypes(semTypeNode(c, n.sons[1].sons[0], nil), skipPtrs) base = skipTypes(semTypeNode(c, n.sons[1].sons[0], nil), skipPtrs)
var concreteBase = skipGenericInvocation(base).skipTypes(skipPtrs) var concreteBase = skipGenericInvocation(base).skipTypes(skipPtrs)
if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags: if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags:
addInheritedFields(c, check, pos, concreteBase) addInheritedFields(c, check, pos, concreteBase)
else: else:
if concreteBase.kind != tyError: if concreteBase.kind != tyError:
@ -723,7 +729,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
genericParams.addSon(newSymNode(s)) genericParams.addSon(newSymNode(s))
result = typeClass result = typeClass
addDecl(c, s) addDecl(c, s)
# XXX: There are codegen errors if this is turned into a nested proc # XXX: There are codegen errors if this is turned into a nested proc
template liftingWalk(typ: PType, anonFlag = false): expr = template liftingWalk(typ: PType, anonFlag = false): expr =
liftParamType(c, procKind, genericParams, typ, paramName, info, anonFlag) liftParamType(c, procKind, genericParams, typ, paramName, info, anonFlag)
@ -742,7 +748,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
case paramType.kind: case paramType.kind:
of tyAnything: of tyAnything:
result = addImplicitGeneric(newTypeS(tyGenericParam, c)) result = addImplicitGeneric(newTypeS(tyGenericParam, c))
of tyStatic: of tyStatic:
# proc(a: expr{string}, b: expr{nkLambda}) # proc(a: expr{string}, b: expr{nkLambda})
# overload on compile time values and AST trees # overload on compile time values and AST trees
@ -753,7 +759,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
localError(info, errMacroBodyDependsOnGenericTypes, paramName) localError(info, errMacroBodyDependsOnGenericTypes, paramName)
result = addImplicitGeneric(c.newTypeWithSons(tyStatic, @[base])) result = addImplicitGeneric(c.newTypeWithSons(tyStatic, @[base]))
result.flags.incl({tfHasStatic, tfUnresolved}) result.flags.incl({tfHasStatic, tfUnresolved})
of tyTypeDesc: of tyTypeDesc:
if tfUnresolved notin paramType.flags: if tfUnresolved notin paramType.flags:
# naked typedescs are not bindOnce types # naked typedescs are not bindOnce types
@ -761,12 +767,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
paramTypId.id == typedescId.id: paramTypId = nil paramTypId.id == typedescId.id: paramTypId = nil
result = addImplicitGeneric( result = addImplicitGeneric(
c.newTypeWithSons(tyTypeDesc, @[paramType.base])) c.newTypeWithSons(tyTypeDesc, @[paramType.base]))
of tyDistinct: of tyDistinct:
if paramType.sonsLen == 1: if paramType.sonsLen == 1:
# disable the bindOnce behavior for the type class # disable the bindOnce behavior for the type class
result = liftingWalk(paramType.sons[0], true) result = liftingWalk(paramType.sons[0], true)
of tySequence, tySet, tyArray, tyOpenArray, of tySequence, tySet, tyArray, tyOpenArray,
tyVar, tyPtr, tyRef, tyProc: tyVar, tyPtr, tyRef, tyProc:
# XXX: this is a bit strange, but proc(s: seq) # XXX: this is a bit strange, but proc(s: seq)
@ -785,22 +791,22 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
if lifted != nil: if lifted != nil:
paramType.sons[i] = lifted paramType.sons[i] = lifted
result = paramType result = paramType
of tyGenericBody: of tyGenericBody:
result = newTypeS(tyGenericInvocation, c) result = newTypeS(tyGenericInvocation, c)
result.rawAddSon(paramType) result.rawAddSon(paramType)
for i in 0 .. paramType.sonsLen - 2: for i in 0 .. paramType.sonsLen - 2:
if paramType.sons[i].kind == tyStatic: if paramType.sons[i].kind == tyStatic:
result.rawAddSon makeTypeFromExpr(c, ast.emptyNode) # aka 'tyUnknown' result.rawAddSon makeTypeFromExpr(c, ast.emptyNode) # aka 'tyUnknown'
else: else:
result.rawAddSon newTypeS(tyAnything, c) result.rawAddSon newTypeS(tyAnything, c)
if paramType.lastSon.kind == tyUserTypeClass: if paramType.lastSon.kind == tyUserTypeClass:
result.kind = tyUserTypeClassInst result.kind = tyUserTypeClassInst
result.rawAddSon paramType.lastSon result.rawAddSon paramType.lastSon
return addImplicitGeneric(result) return addImplicitGeneric(result)
result = instGenericContainer(c, paramType.sym.info, result, result = instGenericContainer(c, paramType.sym.info, result,
allowMetaTypes = true) allowMetaTypes = true)
result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result]) result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result])
@ -832,7 +838,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
if liftBody != nil: if liftBody != nil:
result = liftBody result = liftBody
result.shouldHaveMeta result.shouldHaveMeta
of tyGenericInvocation: of tyGenericInvocation:
for i in 1 .. <paramType.sonsLen: for i in 1 .. <paramType.sonsLen:
let lifted = liftingWalk(paramType.sons[i]) let lifted = liftingWalk(paramType.sons[i])
@ -844,18 +850,18 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
of tyUserTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot: of tyUserTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true)) result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true))
of tyExpr: of tyExpr:
if procKind notin {skMacro, skTemplate}: if procKind notin {skMacro, skTemplate}:
result = addImplicitGeneric(newTypeS(tyAnything, c)) result = addImplicitGeneric(newTypeS(tyAnything, c))
of tyGenericParam: of tyGenericParam:
markUsed(info, paramType.sym) markUsed(info, paramType.sym)
styleCheckUse(info, paramType.sym) styleCheckUse(info, paramType.sym)
if tfWildcard in paramType.flags: if tfWildcard in paramType.flags:
paramType.flags.excl tfWildcard paramType.flags.excl tfWildcard
paramType.sym.kind = skType paramType.sym.kind = skType
else: discard else: discard
# result = liftingWalk(paramType) # result = liftingWalk(paramType)
@ -872,7 +878,7 @@ proc newProcType(c: PContext; info: TLineInfo; prev: PType = nil): PType =
result.callConv = lastOptionEntry(c).defaultCC result.callConv = lastOptionEntry(c).defaultCC
result.n = newNodeI(nkFormalParams, info) result.n = newNodeI(nkFormalParams, info)
rawAddSon(result, nil) # return type rawAddSon(result, nil) # return type
# result.n[0] used to be `nkType`, but now it's `nkEffectList` because # result.n[0] used to be `nkType`, but now it's `nkEffectList` because
# the effects are now stored in there too ... this is a bit hacky, but as # the effects are now stored in there too ... this is a bit hacky, but as
# usual we desperately try to save memory: # usual we desperately try to save memory:
addSon(result.n, newNodeI(nkEffectList, info)) addSon(result.n, newNodeI(nkEffectList, info))
@ -909,7 +915,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
typ = semParamType(c, a.sons[length-2], constraint) typ = semParamType(c, a.sons[length-2], constraint)
if hasDefault: if hasDefault:
def = semExprWithType(c, a.sons[length-1]) def = semExprWithType(c, a.sons[length-1])
# check type compatibility between def.typ and typ: # check type compatibility between def.typ and typ:
if typ == nil: if typ == nil:
typ = def.typ typ = def.typ
@ -927,7 +933,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
typ = newTypeS(tdef, c) typ = newTypeS(tdef, c)
if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue
for j in countup(0, length-3): for j in countup(0, length-3):
var arg = newSymG(skParam, a.sons[j], c) var arg = newSymG(skParam, a.sons[j], c)
let lifted = liftParamType(c, kind, genericParams, typ, let lifted = liftParamType(c, kind, genericParams, typ,
arg.name.s, arg.info) arg.name.s, arg.info)
@ -937,7 +943,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
arg.constraint = constraint arg.constraint = constraint
inc(counter) inc(counter)
if def != nil and def.kind != nkEmpty: arg.ast = copyTree(def) if def != nil and def.kind != nkEmpty: arg.ast = copyTree(def)
if containsOrIncl(check, arg.name.id): if containsOrIncl(check, arg.name.id):
localError(a.sons[j].info, errAttemptToRedefine, arg.name.s) localError(a.sons[j].info, errAttemptToRedefine, arg.name.s)
addSon(result.n, newSymNode(arg)) addSon(result.n, newSymNode(arg))
rawAddSon(result, finalType) rawAddSon(result, finalType)
@ -950,9 +956,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
elif kind == skIterator: elif kind == skIterator:
# XXX This is special magic we should likely get rid of # XXX This is special magic we should likely get rid of
r = newTypeS(tyExpr, c) r = newTypeS(tyExpr, c)
if r != nil: if r != nil:
# turn explicit 'void' return type into 'nil' because the rest of the # turn explicit 'void' return type into 'nil' because the rest of the
# compiler only checks for 'nil': # compiler only checks for 'nil':
if skipTypes(r, {tyGenericInst}).kind != tyEmpty: if skipTypes(r, {tyGenericInst}).kind != tyEmpty:
# 'auto' as a return type does not imply a generic: # 'auto' as a return type does not imply a generic:
@ -988,8 +994,8 @@ proc semStmtListType(c: PContext, n: PNode, prev: PType): PType =
n.sons[length - 1].typ = result n.sons[length - 1].typ = result
else: else:
result = nil result = nil
proc semBlockType(c: PContext, n: PNode, prev: PType): PType = proc semBlockType(c: PContext, n: PNode, prev: PType): PType =
inc(c.p.nestedBlockCounter) inc(c.p.nestedBlockCounter)
checkSonsLen(n, 2) checkSonsLen(n, 2)
openScope(c) openScope(c)
@ -1009,7 +1015,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
localError(n.info, "cannot instantiate the '$1' $2" % localError(n.info, "cannot instantiate the '$1' $2" %
[s.name.s, ($s.kind).substr(2).toLower]) [s.name.s, ($s.kind).substr(2).toLower])
return newOrPrevType(tyError, prev, c) return newOrPrevType(tyError, prev, c)
var t = s.typ var t = s.typ
if t.kind == tyCompositeTypeClass and t.base.kind == tyGenericBody: if t.kind == tyCompositeTypeClass and t.base.kind == tyGenericBody:
t = t.base t = t.base
@ -1036,7 +1042,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
else: else:
var m = newCandidate(c, t) var m = newCandidate(c, t)
matches(c, n, copyTree(n), m) matches(c, n, copyTree(n), m)
if m.state != csMatch: if m.state != csMatch:
var err = "cannot instantiate " & typeToString(t) & "\n" & var err = "cannot instantiate " & typeToString(t) & "\n" &
"got: (" & describeArgs(c, n) & ")\n" & "got: (" & describeArgs(c, n) & ")\n" &
@ -1045,12 +1051,12 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
return newOrPrevType(tyError, prev, c) return newOrPrevType(tyError, prev, c)
var isConcrete = true var isConcrete = true
for i in 1 .. <m.call.len: for i in 1 .. <m.call.len:
let typ = m.call[i].typ.skipTypes({tyTypeDesc}) let typ = m.call[i].typ.skipTypes({tyTypeDesc})
if containsGenericType(typ): isConcrete = false if containsGenericType(typ): isConcrete = false
addToResult(typ) addToResult(typ)
if isConcrete: if isConcrete:
if s.ast == nil and s.typ.kind != tyCompositeTypeClass: if s.ast == nil and s.typ.kind != tyCompositeTypeClass:
# XXX: What kind of error is this? is it still relevant? # XXX: What kind of error is this? is it still relevant?
@ -1081,7 +1087,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
let let
pragmas = n[1] pragmas = n[1]
inherited = n[2] inherited = n[2]
if inherited.kind != nkEmpty: if inherited.kind != nkEmpty:
for n in inherited.sons: for n in inherited.sons:
let typ = semTypeNode(c, n, nil) let typ = semTypeNode(c, n, nil)
@ -1114,12 +1120,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
checkSonsLen(n, 1) checkSonsLen(n, 1)
let typExpr = semExprWithType(c, n.sons[0], {efInTypeof}) let typExpr = semExprWithType(c, n.sons[0], {efInTypeof})
result = typExpr.typ.skipTypes({tyIter}) result = typExpr.typ.skipTypes({tyIter})
of nkPar: of nkPar:
if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev) if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev)
else: else:
# XXX support anon tuple here result = semAnonTuple(c, n, prev)
localError(n.info, errTypeExpected)
result = newOrPrevType(tyError, prev, c)
of nkCallKinds: of nkCallKinds:
if isRange(n): if isRange(n):
result = semRangeAux(c, n, prev) result = semRangeAux(c, n, prev)
@ -1197,7 +1201,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = makeTypeFromExpr(c, preprocessed.copyTree) result = makeTypeFromExpr(c, preprocessed.copyTree)
of nkIdent, nkAccQuoted: of nkIdent, nkAccQuoted:
var s = semTypeIdent(c, n) var s = semTypeIdent(c, n)
if s.typ == nil: if s.typ == nil:
if s.kind != skError: localError(n.info, errTypeExpected) if s.kind != skError: localError(n.info, errTypeExpected)
result = newOrPrevType(tyError, prev, c) result = newOrPrevType(tyError, prev, c)
elif s.kind == skParam and s.typ.kind == tyTypeDesc: elif s.kind == skParam and s.typ.kind == tyTypeDesc:
@ -1205,19 +1209,19 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = s.typ.base result = s.typ.base
elif prev == nil: elif prev == nil:
result = s.typ result = s.typ
else: else:
assignType(prev, s.typ) assignType(prev, s.typ)
# bugfix: keep the fresh id for aliases to integral types: # bugfix: keep the fresh id for aliases to integral types:
if s.typ.kind notin {tyBool, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, if s.typ.kind notin {tyBool, tyChar, tyInt..tyInt64, tyFloat..tyFloat128,
tyUInt..tyUInt64}: tyUInt..tyUInt64}:
prev.id = s.typ.id prev.id = s.typ.id
result = prev result = prev
of nkSym: of nkSym:
if n.sym.kind == skType and n.sym.typ != nil: if n.sym.kind == skType and n.sym.typ != nil:
var t = n.sym.typ var t = n.sym.typ
if prev == nil: if prev == nil:
result = t result = t
else: else:
assignType(prev, t) assignType(prev, t)
result = prev result = prev
markUsed(n.info, n.sym) markUsed(n.info, n.sym)
@ -1227,6 +1231,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tyError, prev, c) result = newOrPrevType(tyError, prev, c)
of nkObjectTy: result = semObjectNode(c, n, prev) of nkObjectTy: result = semObjectNode(c, n, prev)
of nkTupleTy: result = semTuple(c, n, prev) of nkTupleTy: result = semTuple(c, n, prev)
of nkTupleClassTy: result = newConstraint(c, tyTuple)
of nkTypeClassTy: result = semTypeClass(c, n, prev) of nkTypeClassTy: result = semTypeClass(c, n, prev)
of nkRefTy: result = semAnyRef(c, n, tyRef, prev) of nkRefTy: result = semAnyRef(c, n, tyRef, prev)
of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev) of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev)
@ -1267,12 +1272,12 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tyError, prev, c) result = newOrPrevType(tyError, prev, c)
n.typ = result n.typ = result
proc setMagicType(m: PSym, kind: TTypeKind, size: int) = proc setMagicType(m: PSym, kind: TTypeKind, size: int) =
m.typ.kind = kind m.typ.kind = kind
m.typ.align = size.int16 m.typ.align = size.int16
m.typ.size = size m.typ.size = size
proc processMagicType(c: PContext, m: PSym) = proc processMagicType(c: PContext, m: PSym) =
case m.magic case m.magic
of mInt: setMagicType(m, tyInt, intSize) of mInt: setMagicType(m, tyInt, intSize)
of mInt8: setMagicType(m, tyInt8, 1) of mInt8: setMagicType(m, tyInt8, 1)
@ -1290,21 +1295,21 @@ proc processMagicType(c: PContext, m: PSym) =
of mFloat128: setMagicType(m, tyFloat128, 16) of mFloat128: setMagicType(m, tyFloat128, 16)
of mBool: setMagicType(m, tyBool, 1) of mBool: setMagicType(m, tyBool, 1)
of mChar: setMagicType(m, tyChar, 1) of mChar: setMagicType(m, tyChar, 1)
of mString: of mString:
setMagicType(m, tyString, ptrSize) setMagicType(m, tyString, ptrSize)
rawAddSon(m.typ, getSysType(tyChar)) rawAddSon(m.typ, getSysType(tyChar))
of mCstring: of mCstring:
setMagicType(m, tyCString, ptrSize) setMagicType(m, tyCString, ptrSize)
rawAddSon(m.typ, getSysType(tyChar)) rawAddSon(m.typ, getSysType(tyChar))
of mPointer: setMagicType(m, tyPointer, ptrSize) of mPointer: setMagicType(m, tyPointer, ptrSize)
of mEmptySet: of mEmptySet:
setMagicType(m, tySet, 1) setMagicType(m, tySet, 1)
rawAddSon(m.typ, newTypeS(tyEmpty, c)) rawAddSon(m.typ, newTypeS(tyEmpty, c))
of mIntSetBaseType: setMagicType(m, tyRange, intSize) of mIntSetBaseType: setMagicType(m, tyRange, intSize)
of mNil: setMagicType(m, tyNil, ptrSize) of mNil: setMagicType(m, tyNil, ptrSize)
of mExpr: setMagicType(m, tyExpr, 0) of mExpr: setMagicType(m, tyExpr, 0)
of mStmt: setMagicType(m, tyStmt, 0) of mStmt: setMagicType(m, tyStmt, 0)
of mTypeDesc: of mTypeDesc:
setMagicType(m, tyTypeDesc, 0) setMagicType(m, tyTypeDesc, 0)
rawAddSon(m.typ, newTypeS(tyNone, c)) rawAddSon(m.typ, newTypeS(tyNone, c))
of mVoidType: setMagicType(m, tyEmpty, 0) of mVoidType: setMagicType(m, tyEmpty, 0)
@ -1318,8 +1323,8 @@ proc processMagicType(c: PContext, m: PSym) =
setMagicType(m, tyRange, 0) setMagicType(m, tyRange, 0)
rawAddSon(m.typ, newTypeS(tyNone, c)) rawAddSon(m.typ, newTypeS(tyNone, c))
of mSet: of mSet:
setMagicType(m, tySet, 0) setMagicType(m, tySet, 0)
of mSeq: of mSeq:
setMagicType(m, tySequence, 0) setMagicType(m, tySequence, 0)
of mOrdinal: of mOrdinal:
setMagicType(m, tyOrdinal, 0) setMagicType(m, tyOrdinal, 0)
@ -1335,13 +1340,13 @@ proc processMagicType(c: PContext, m: PSym) =
incl m.typ.flags, tfShared incl m.typ.flags, tfShared
rawAddSon(m.typ, sysTypeFromName"shared") rawAddSon(m.typ, sysTypeFromName"shared")
else: localError(m.info, errTypeExpected) else: localError(m.info, errTypeExpected)
proc semGenericConstraints(c: PContext, x: PType): PType = proc semGenericConstraints(c: PContext, x: PType): PType =
result = newTypeWithSons(c, tyGenericParam, @[x]) result = newTypeWithSons(c, tyGenericParam, @[x])
proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
result = copyNode(n) result = copyNode(n)
if n.kind != nkGenericParams: if n.kind != nkGenericParams:
illFormedAst(n) illFormedAst(n)
return return
for i in countup(0, sonsLen(n)-1): for i in countup(0, sonsLen(n)-1):
@ -1351,7 +1356,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
var def = a{-1} var def = a{-1}
let constraint = a{-2} let constraint = a{-2}
var typ: PType var typ: PType
if constraint.kind != nkEmpty: if constraint.kind != nkEmpty:
typ = semTypeNode(c, constraint, nil) typ = semTypeNode(c, constraint, nil)
if typ.kind != tyStatic or typ.len == 0: if typ.kind != tyStatic or typ.len == 0:
@ -1360,7 +1365,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)]) typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)])
else: else:
typ = semGenericConstraints(c, typ) typ = semGenericConstraints(c, typ)
if def.kind != nkEmpty: if def.kind != nkEmpty:
def = semConstExpr(c, def) def = semConstExpr(c, def)
if typ == nil: if typ == nil:
@ -1372,7 +1377,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
def.typ = def.typ.skipTypes({tyTypeDesc}) def.typ = def.typ.skipTypes({tyTypeDesc})
if not containsGenericType(def.typ): if not containsGenericType(def.typ):
def = fitNode(c, typ, def) def = fitNode(c, typ, def)
if typ == nil: if typ == nil:
typ = newTypeS(tyGenericParam, c) typ = newTypeS(tyGenericParam, c)
if father == nil: typ.flags.incl tfWildcard if father == nil: typ.flags.incl tfWildcard

View file

@ -341,6 +341,8 @@ proc skipIntLiteralParams*(t: PType) =
proc propagateFieldFlags(t: PType, n: PNode) = proc propagateFieldFlags(t: PType, n: PNode) =
# This is meant for objects and tuples # This is meant for objects and tuples
# The type must be fully instantiated! # The type must be fully instantiated!
if n.isNil:
return
internalAssert n.kind != nkRecWhen internalAssert n.kind != nkRecWhen
case n.kind case n.kind
of nkSym: of nkSym:

View file

@ -10,7 +10,7 @@
## This module implements the signature matching for resolving ## This module implements the signature matching for resolving
## the call to overloaded procs, generic procs and operators. ## the call to overloaded procs, generic procs and operators.
import import
intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst, intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst,
magicsys, condsyms, idents, lexer, options, parampatterns, strutils, trees, magicsys, condsyms, idents, lexer, options, parampatterns, strutils, trees,
nimfix.pretty nimfix.pretty
@ -19,7 +19,7 @@ when not defined(noDocgen):
import docgen import docgen
type type
TCandidateState* = enum TCandidateState* = enum
csEmpty, csMatch, csNoMatch csEmpty, csMatch, csNoMatch
CandidateErrors* = seq[PSym] CandidateErrors* = seq[PSym]
@ -62,10 +62,10 @@ type
isGeneric, isGeneric,
isFromIntLit, # conversion *from* int literal; proven safe isFromIntLit, # conversion *from* int literal; proven safe
isEqual isEqual
const const
isNilConversion = isConvertible # maybe 'isIntConv' fits better? isNilConversion = isConvertible # maybe 'isIntConv' fits better?
proc markUsed*(info: TLineInfo, s: PSym) proc markUsed*(info: TLineInfo, s: PSym)
template hasFauxMatch*(c: TCandidate): bool = c.fauxMatch != tyNone 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 = proc newCandidate*(ctx: PContext, callee: PType): TCandidate =
initCandidate(ctx, result, callee) initCandidate(ctx, result, callee)
proc copyCandidate(a: var TCandidate, b: TCandidate) = proc copyCandidate(a: var TCandidate, b: TCandidate) =
a.c = b.c a.c = b.c
a.exactMatches = b.exactMatches a.exactMatches = b.exactMatches
a.subtypeMatches = b.subtypeMatches a.subtypeMatches = b.subtypeMatches
@ -157,17 +157,40 @@ proc sumGeneric(t: PType): int =
result = ord(t.kind == tyGenericInvocation) result = ord(t.kind == tyGenericInvocation)
for i in 0 .. <t.len: result += t.sons[i].sumGeneric for i in 0 .. <t.len: result += t.sons[i].sumGeneric
break break
of tyProc:
# proc matches proc better than 'stmt' to disambiguate 'spawn'
return 1
of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break 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 else: return 0
#var ggDebug: bool
proc complexDisambiguation(a, b: PType): int = proc complexDisambiguation(a, b: PType): int =
var x, y: int # 'a' matches better if *every* argument matches better or equal than 'b'.
for i in 1 .. <a.len: x += a.sons[i].sumGeneric var winner = 0
for i in 1 .. <b.len: y += b.sons[i].sumGeneric for i in 1 .. <min(a.len, b.len):
result = x - y 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: when false:
proc betterThan(a, b: PType): bool {.inline.} = a.sumGeneric > b.sumGeneric 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 let bb = b.sons[1].sumGeneric
var a = a var a = a
var b = b var b = b
if aa < bb: swap(a, b) if aa < bb: swap(a, b)
# all must be better # all must be better
for i in 2 .. <min(a.len, b.len): 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: # prefer more specialized generic over more general generic:
result = complexDisambiguation(a.callee, b.callee) result = complexDisambiguation(a.callee, b.callee)
proc writeMatches*(c: TCandidate) = proc writeMatches*(c: TCandidate) =
writeln(stdout, "exact matches: " & $c.exactMatches) writeln(stdout, "exact matches: " & $c.exactMatches)
writeln(stdout, "generic matches: " & $c.genericMatches) writeln(stdout, "generic matches: " & $c.genericMatches)
writeln(stdout, "subtype matches: " & $c.subtypeMatches) writeln(stdout, "subtype matches: " & $c.subtypeMatches)
@ -225,7 +248,7 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1;
result = "" result = ""
for i in countup(startIdx, n.len - 1): for i in countup(startIdx, n.len - 1):
var arg = n.sons[i] 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, renderTree(n.sons[i].sons[0]))
add(result, ": ") add(result, ": ")
if arg.typ.isNil: if arg.typ.isNil:
@ -241,9 +264,9 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1;
if i != sonsLen(n) - 1: add(result, ", ") if i != sonsLen(n) - 1: add(result, ", ")
proc typeRel*(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation 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 case t.kind
of tyArrayConstr: of tyArrayConstr:
# make it an array # make it an array
result = newType(tyArray, t.owner) result = newType(tyArray, t.owner)
addSonSkipIntLit(result, t.sons[0]) # XXX: t.owner is wrong for ID! 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 else: result = t
of tyGenericParam, tyAnything: of tyGenericParam, tyAnything:
result = t result = t
while true: while true:
result = PType(idTableGet(c.bindings, t)) result = PType(idTableGet(c.bindings, t))
if result == nil: if result == nil:
break # it's ok, no match break # it's ok, no match
@ -267,15 +290,15 @@ proc concreteType(c: TCandidate, t: PType): PType =
result = t result = t
else: else:
result = t # Note: empty is valid here result = t # Note: empty is valid here
proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation = proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation =
if a.kind == f.kind: if a.kind == f.kind:
result = isEqual result = isEqual
else: else:
let ab = skipTypes(a, {tyRange}) let ab = skipTypes(a, {tyRange})
let k = ab.kind let k = ab.kind
if k == f.kind: result = isSubrange 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 tyUInt..tyUInt64} and
isIntLit(ab) and ab.n.intVal >= firstOrd(f) and isIntLit(ab) and ab.n.intVal >= firstOrd(f) and
ab.n.intVal <= lastOrd(f): ab.n.intVal <= lastOrd(f):
@ -286,7 +309,7 @@ proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation =
result = isIntConv result = isIntConv
elif k >= min and k <= max: elif k >= min and k <= max:
result = isConvertible 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 tyUInt8..tyUInt32} and
a.n[0].intVal >= firstOrd(f) and a.n[0].intVal >= firstOrd(f) and
a.n[1].intVal <= lastOrd(f): a.n[1].intVal <= lastOrd(f):
@ -318,12 +341,12 @@ proc handleFloatRange(f, a: PType): TTypeRelation =
if f.kind == tyFloat32: result = isConvertible if f.kind == tyFloat32: result = isConvertible
else: result = isIntConv else: result = isIntConv
else: result = isNone else: result = isNone
proc isObjectSubtype(a, f: PType): int = proc isObjectSubtype(a, f: PType): int =
var t = a var t = a
assert t.kind == tyObject assert t.kind == tyObject
var depth = 0 var depth = 0
while t != nil and not sameObjectTypes(f, t): while t != nil and not sameObjectTypes(f, t):
assert t.kind == tyObject assert t.kind == tyObject
t = t.sons[0] t = t.sons[0]
if t == nil: break if t == nil: break
@ -332,17 +355,18 @@ proc isObjectSubtype(a, f: PType): int =
if t != nil: if t != nil:
result = depth result = depth
proc minRel(a, b: TTypeRelation): TTypeRelation = proc minRel(a, b: TTypeRelation): TTypeRelation =
if a <= b: result = a if a <= b: result = a
else: result = b else: result = b
proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation = proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
result = isNone result = isNone
if sameType(f, a): result = isEqual if sameType(f, a):
result = isEqual
elif sonsLen(a) == sonsLen(f): elif sonsLen(a) == sonsLen(f):
result = isEqual result = isEqual
let firstField = if f.kind == tyTuple: 0 let firstField = if f.kind == tyTuple: 0
else: 1 else: 1
for i in countup(firstField, sonsLen(f) - 1): for i in countup(firstField, sonsLen(f) - 1):
var m = typeRel(c, f.sons[i], a.sons[i]) var m = typeRel(c, f.sons[i], a.sons[i])
if m < isSubtype: return isNone 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) # We are matching a generic proc (as proc param)
# to another generic type appearing in the proc # to another generic type appearing in the proc
# signature. There is a change that the target # 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 # going to try resolve it
f = generateTypeInstance(c.c, c.bindings, c.call.info, f) f = generateTypeInstance(c.c, c.bindings, c.call.info, f)
if f == nil or f.isMetaType: if f == nil or f.isMetaType:
# no luck resolving the type, so the inference fails # no luck resolving the type, so the inference fails
return isNone return isNone
let reverseRel = typeRel(c, a, f) let reverseRel = typeRel(c, a, f)
if reverseRel == isGeneric: if reverseRel >= isGeneric:
result = isInferred result = isInferred
inc c.genericMatches #inc c.genericMatches
else: else:
result = typeRel(c, f, a) result = typeRel(c, f, a)
if result <= isSubtype or inconsistentVarTypes(f, a): if result <= isSubtype or inconsistentVarTypes(f, a):
result = isNone result = isNone
if result == isEqual: #if result == isEqual:
inc c.exactMatches # inc c.exactMatches
proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
case a.kind case a.kind
of tyProc: of tyProc:
if sonsLen(f) != sonsLen(a): return if sonsLen(f) != sonsLen(a): return
result = isEqual # start with maximum; also correct for no result = isEqual # start with maximum; also correct for no
# params at all # params at all
template checkParam(f, a) = template checkParam(f, a) =
result = minRel(result, procParamTypeRel(c, f, a)) result = minRel(result, procParamTypeRel(c, f, a))
if result == isNone: return if result == isNone: return
@ -407,7 +431,7 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
# return type! # return type!
for i in 1 .. <f.sonsLen: for i in 1 .. <f.sonsLen:
checkParam(f.sons[i], a.sons[i]) checkParam(f.sons[i], a.sons[i])
if f.sons[0] != nil: if f.sons[0] != nil:
if a.sons[0] != nil: if a.sons[0] != nil:
checkParam(f.sons[0], a.sons[0]) checkParam(f.sons[0], a.sons[0])
@ -433,6 +457,7 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
return isNone return isNone
when useEffectSystem: when useEffectSystem:
if not compatibleEffects(f, a): return isNone if not compatibleEffects(f, a): return isNone
of tyNil: of tyNil:
result = f.allowsNil result = f.allowsNil
of tyIter: of tyIter:
@ -487,14 +512,14 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
else: else:
param = paramSym skType param = paramSym skType
param.typ = makeTypeDesc(c, typ) param.typ = makeTypeDesc(c, typ)
addDecl(c, param) addDecl(c, param)
for param in body.n[0]: for param in body.n[0]:
var var
dummyName: PNode dummyName: PNode
dummyType: PType dummyType: PType
if param.kind == nkVarTy: if param.kind == nkVarTy:
dummyName = param[0] dummyName = param[0]
dummyType = if a.kind != tyVar: makeVarType(c, a) dummyType = if a.kind != tyVar: makeVarType(c, a)
@ -520,7 +545,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
of nkTypeSection: discard of nkTypeSection: discard
of nkConstDef: discard of nkConstDef: discard
else: discard else: discard
return isGeneric return isGeneric
proc shouldSkipDistinct(rules: PNode, callIdent: PIdent): bool = 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: # typeRel can be used to establish various relationships between types:
# #
# 1) When used with concrete types, it will check for type equivalence # 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 # 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 # 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 result = isNone
assert(f != nil) assert(f != nil)
if f.kind == tyExpr: if f.kind == tyExpr:
if aOrig != nil: put(c.bindings, f, aOrig) if aOrig != nil: put(c.bindings, f, aOrig)
return isGeneric 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` # start the param matching process. This could be done in `prepareOperand`
# for example, but unfortunately `prepareOperand` is not called in certain # for example, but unfortunately `prepareOperand` is not called in certain
# situation when nkDotExpr are rotated to nkDotCalls # situation when nkDotExpr are rotated to nkDotCalls
if a.kind == tyGenericInst and if a.kind == tyGenericInst and
skipTypes(f, {tyVar}).kind notin { skipTypes(f, {tyVar}).kind notin {
tyGenericBody, tyGenericInvocation, tyGenericBody, tyGenericInvocation,
@ -590,10 +615,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
return typeRel(c, f, lastSon(a)) return typeRel(c, f, lastSon(a))
template bindingRet(res) = template bindingRet(res) =
when res == isGeneric: if doBind:
if doBind: let bound = aOrig.skipTypes({tyRange}).skipIntLit
let bound = aOrig.skipTypes({tyRange}).skipIntLit if doBind: put(c.bindings, f, bound)
if doBind: put(c.bindings, f, bound)
return res return res
template considerPreviousT(body: stmt) {.immediate.} = template considerPreviousT(body: stmt) {.immediate.} =
@ -605,20 +629,21 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyOr: of tyOr:
# seq[int|string] vs seq[number] # seq[int|string] vs seq[number]
# both int and string must match against 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: for branch in a.sons:
if typeRel(c, f, branch, false) == isNone: let x = typeRel(c, f, branch, false)
return isNone if x == isNone: return isNone
if x < result: result = x
return isGeneric
of tyAnd: of tyAnd:
# seq[Sortable and Iterable] vs seq[Sortable] # seq[Sortable and Iterable] vs seq[Sortable]
# only one match is enough # only one match is enough
for branch in a.sons: for branch in a.sons:
if typeRel(c, f, branch, false) != isNone: let x = typeRel(c, f, branch, false)
return isGeneric if x != isNone:
return if x >= isGeneric: isGeneric else: x
return isNone result = isNone
of tyNot: of tyNot:
case f.kind case f.kind
@ -626,9 +651,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
# seq[!int] vs seq[!number] # seq[!int] vs seq[!number]
# seq[float] matches the first, but not the second # seq[float] matches the first, but not the second
# we must turn the problem around: # 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) return typeRel(c, a.lastSon, f.lastSon)
else: else:
# negative type classes are essentially infinite, # negative type classes are essentially infinite,
# so only the `any` type class is their superset # 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: of tyOpenArray, tyVarargs:
result = typeRel(c, base(f), base(a)) result = typeRel(c, base(f), base(a))
if result < isGeneric: result = isNone if result < isGeneric: result = isNone
of tyArrayConstr: of tyArrayConstr:
if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty): if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty):
result = isSubtype # [] is allowed here 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 result = isSubtype
of tyArray: of tyArray:
if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty): if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty):
result = isSubtype result = isSubtype
elif typeRel(c, base(f), a.sons[1]) >= isGeneric: elif typeRel(c, base(f), a.sons[1]) >= isGeneric:
result = isConvertible result = isConvertible
of tySequence: of tySequence:
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
result = isConvertible result = isConvertible
elif typeRel(c, base(f), a.sons[0]) >= isGeneric: elif typeRel(c, base(f), a.sons[0]) >= isGeneric:
result = isConvertible result = isConvertible
else: discard else: discard
of tySequence: 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 tyForward: internalError("forward type in typeRel()")
of tyNil: of tyNil:
if a.kind == f.kind: result = isEqual if a.kind == f.kind: result = isEqual
of tyTuple: of tyTuple:
if a.kind == tyTuple: result = recordRel(c, f, a) if a.kind == tyTuple: result = recordRel(c, f, a)
of tyObject: of tyObject:
if a.kind == tyObject: if a.kind == tyObject:
@ -781,15 +806,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
inc(c.inheritancePenalty, depth) inc(c.inheritancePenalty, depth)
result = isSubtype result = isSubtype
of tyDistinct: 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) elif c.coerceDistincts: result = typeRel(c, f.base, a)
of tySet: of tySet:
if a.kind == tySet: if a.kind == tySet:
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): if f.sons[0].kind != tyGenericParam and a.sons[0].kind == tyEmpty:
result = isSubtype result = isSubtype
else: else:
result = typeRel(c, f.sons[0], a.sons[0]) result = typeRel(c, f.sons[0], a.sons[0])
if result <= isConvertible: if result <= isConvertible:
result = isNone # BUGFIX! result = isNone # BUGFIX!
of tyPtr, tyRef: of tyPtr, tyRef:
if a.kind == f.kind: 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 if a.len == 1: result = isConvertible
of tyCString: result = isConvertible of tyCString: result = isConvertible
else: discard else: discard
of tyString: of tyString:
case a.kind case a.kind
of tyString: of tyString:
if tfNotNil in f.flags and tfNotNil notin a.flags: if tfNotNil in f.flags and tfNotNil notin a.flags:
result = isNilConversion result = isNilConversion
else: else:
@ -848,7 +873,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyArray: of tyArray:
if (firstOrd(a.sons[0]) == 0) and if (firstOrd(a.sons[0]) == 0) and
(skipTypes(a.sons[0], {tyRange}).kind in {tyInt..tyInt64}) and (skipTypes(a.sons[0], {tyRange}).kind in {tyInt..tyInt64}) and
(a.sons[1].kind == tyChar): (a.sons[1].kind == tyChar):
result = isConvertible result = isConvertible
else: discard else: discard
@ -863,9 +888,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
let ff = rootf.sons[i] let ff = rootf.sons[i]
let aa = roota.sons[i] let aa = roota.sons[i]
result = typeRel(c, ff, aa) result = typeRel(c, ff, aa)
if result == isNone: return if result == isNone: return
if ff.kind == tyRange and result != isEqual: return isNone 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 # XXX See bug #2220. A[int] should match A[int] better than some generic X
else: else:
result = typeRel(c, lastSon(f), a) 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") #InternalError("typeRel: tyGenericInvocation -> tyGenericInvocation")
# simply no match for now: # simply no match for now:
discard discard
elif x.kind == tyGenericInst and elif x.kind == tyGenericInst and
(f.sons[0] == x.sons[0]) and (f.sons[0] == x.sons[0]) and
(sonsLen(x) - 1 == sonsLen(f)): (sonsLen(x) - 1 == sonsLen(f)):
for i in countup(1, sonsLen(f) - 1): for i in countup(1, sonsLen(f) - 1):
if x.sons[i].kind == tyGenericParam: if x.sons[i].kind == tyGenericParam:
internalError("wrong instantiated type!") 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 result = isGeneric
else: else:
result = typeRel(c, f.sons[0], x) 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}: if x == nil or x.kind in {tyGenericInvocation, tyGenericParam}:
internalError("wrong instantiated type!") internalError("wrong instantiated type!")
put(c.bindings, f.sons[i], x) put(c.bindings, f.sons[i], x)
of tyAnd: of tyAnd:
considerPreviousT: considerPreviousT:
for branch in f.sons: for branch in f.sons:
if typeRel(c, branch, aOrig) < isSubtype: let x = typeRel(c, branch, aOrig)
return isNone if x < isSubtype: return isNone
# 'and' implies minimum matching result:
bindingRet isGeneric if x < result: result = x
bindingRet result
of tyOr: of tyOr:
considerPreviousT: considerPreviousT:
result = isNone
for branch in f.sons: for branch in f.sons:
if typeRel(c, branch, aOrig) >= isSubtype: let x = typeRel(c, branch, aOrig)
bindingRet isGeneric # 'or' implies maximum matching result:
if x > result: result = x
return isNone if result >= isSubtype:
bindingRet result
else:
result = isNone
of tyNot: of tyNot:
considerPreviousT: considerPreviousT:
for branch in f.sons: for branch in f.sons:
if typeRel(c, branch, aOrig) != isNone: if typeRel(c, branch, aOrig) != isNone:
return isNone return isNone
bindingRet isGeneric bindingRet isGeneric
of tyAnything: of tyAnything:
@ -961,7 +991,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
if x == nil: if x == nil:
if c.callee.kind == tyGenericBody and if c.callee.kind == tyGenericBody and
f.kind == tyGenericParam and not c.typedescMatched: 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 # their parameters is really a misnomer. tyGenericParam means "match
# any value" and what we need is "match any type", which can be encoded # any value" and what we need is "match any type", which can be encoded
# by a tyTypeDesc params. Unfortunately, this requires more substantial # 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 internalAssert a.sons != nil and a.sons.len > 0
c.typedescMatched = true c.typedescMatched = true
result = typeRel(c, f.base, a.skipTypes({tyGenericParam, tyTypeDesc})) result = typeRel(c, f.base, a.skipTypes({tyGenericParam, tyTypeDesc}))
if result > isGeneric: result = isGeneric
else: else:
result = isNone result = isNone
else: else:
@ -998,13 +1029,16 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
return isNone return isNone
if doBind: if doBind:
put(c.bindings, f, concrete) put(c.bindings, f, concrete)
elif result > isGeneric:
result = isGeneric
elif a.kind == tyEmpty: elif a.kind == tyEmpty:
result = isGeneric result = isGeneric
elif x.kind == tyGenericParam: elif x.kind == tyGenericParam:
result = isGeneric result = isGeneric
else: else:
result = typeRel(c, x, a) # check if it fits result = typeRel(c, x, a) # check if it fits
if result > isGeneric: result = isGeneric
of tyStatic: of tyStatic:
let prev = PType(idTableGet(c.bindings, f)) let prev = PType(idTableGet(c.bindings, f))
if prev == nil: 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 # when `f` is an unresolved typedesc, `a` could be any
# type, so we should not perform this check earlier # type, so we should not perform this check earlier
if a.kind != tyTypeDesc: return isNone if a.kind != tyTypeDesc: return isNone
if f.base.kind == tyNone: if f.base.kind == tyNone:
result = isGeneric result = isGeneric
else: else:
result = typeRel(c, f.base, a.base) result = typeRel(c, f.base, a.base)
if result != isNone: if result != isNone:
put(c.bindings, f, a) put(c.bindings, f, a)
else: else:
@ -1049,9 +1083,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
result = typeRel(c, prev.base, a.base) result = typeRel(c, prev.base, a.base)
else: else:
result = isNone result = isNone
of tyIter: of tyIter:
if a.kind == tyIter or if a.kind == tyIter or
(a.kind == tyProc and tfIterator in a.flags): (a.kind == tyProc and tfIterator in a.flags):
result = typeRel(c, f.base, a.base) result = typeRel(c, f.base, a.base)
else: else:
@ -1059,7 +1093,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyStmt: of tyStmt:
result = isGeneric result = isGeneric
of tyProxy: of tyProxy:
result = isEqual result = isEqual
@ -1078,11 +1112,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
else: else:
localError(f.n.info, errTypeExpected) localError(f.n.info, errTypeExpected)
result = isNone result = isNone
else: else:
internalAssert false internalAssert false
proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation = proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation =
var m: TCandidate var m: TCandidate
initCandidate(c, m, f) initCandidate(c, m, f)
result = typeRel(m, f, a) result = typeRel(m, f, a)
@ -1095,9 +1129,9 @@ proc getInstantiatedType(c: PContext, arg: PNode, m: TCandidate,
if result == nil: if result == nil:
internalError(arg.info, "getInstantiatedType") internalError(arg.info, "getInstantiatedType")
result = errorType(c) result = errorType(c)
proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate, proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
c: PContext): PNode = c: PContext): PNode =
result = newNodeI(kind, arg.info) result = newNodeI(kind, arg.info)
if containsGenericType(f): if containsGenericType(f):
if not m.hasFauxMatch: if not m.hasFauxMatch:
@ -1110,10 +1144,10 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
addSon(result, ast.emptyNode) addSon(result, ast.emptyNode)
addSon(result, arg) addSon(result, arg)
proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
arg: PNode): PNode = arg: PNode): PNode =
result = nil 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 src = c.converters[i].typ.sons[1]
var dest = c.converters[i].typ.sons[0] var dest = c.converters[i].typ.sons[0]
# for generic type converters we need to check 'src <- a' before # 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: # see tests/tgenericconverter:
let srca = typeRel(m, src, a) let srca = typeRel(m, src, a)
if srca notin {isEqual, isGeneric}: continue if srca notin {isEqual, isGeneric}: continue
let destIsGeneric = containsGenericType(dest) let destIsGeneric = containsGenericType(dest)
if destIsGeneric: if destIsGeneric:
dest = generateTypeInstance(c, m.bindings, arg, dest) dest = generateTypeInstance(c, m.bindings, arg, dest)
let fdest = typeRel(m, f, dest) let fdest = typeRel(m, f, dest)
if fdest in {isEqual, isGeneric}: if fdest in {isEqual, isGeneric}:
markUsed(arg.info, c.converters[i]) markUsed(arg.info, c.converters[i])
var s = newSymNode(c.converters[i]) var s = newSymNode(c.converters[i])
s.typ = c.converters[i].typ 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 m.genericConverter = srca == isGeneric or destIsGeneric
return result return result
proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType, proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType,
arg: PNode): PNode = arg: PNode): PNode =
# arg.typ can be nil in 'suggest': # arg.typ can be nil in 'suggest':
if isNil(arg.typ): return nil if isNil(arg.typ): return nil
@ -1181,12 +1215,12 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
arg = argSemantized arg = argSemantized
argType = argType argType = argType
c = m.c c = m.c
if tfHasStatic in fMaybeStatic.flags: if tfHasStatic in fMaybeStatic.flags:
# XXX: When implicit statics are the default # XXX: When implicit statics are the default
# this will be done earlier - we just have to # this will be done earlier - we just have to
# make sure that static types enter here # make sure that static types enter here
# XXX: weaken tyGenericParam and call it tyGenericPlaceholder # XXX: weaken tyGenericParam and call it tyGenericPlaceholder
# and finally start using tyTypedesc for generic types properly. # and finally start using tyTypedesc for generic types properly.
if argType.kind == tyGenericParam and tfWildcard in argType.flags: 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.sons = @[evaluated.typ]
arg.typ.n = evaluated arg.typ.n = evaluated
argType = arg.typ argType = arg.typ
var var
a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc, tyFieldAccessor}) a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc, tyFieldAccessor})
else: argType else: argType
r = typeRel(m, f, a) r = typeRel(m, f, a)
if r != isNone and m.calleeSym != nil and 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 isConvertible, isIntConv: inc(m.convMatches)
of isSubtype, isSubrange: inc(m.subtypeMatches) of isSubtype, isSubrange: inc(m.subtypeMatches)
of isGeneric, isInferred: inc(m.genericMatches) of isGeneric, isInferred: inc(m.genericMatches)
of isInferredConvertible: inc(m.genericMatches); inc(m.convMatches)
of isFromIntLit: inc(m.intConvMatches, 256) of isFromIntLit: inc(m.intConvMatches, 256)
of isInferredConvertible:
inc(m.convMatches)
of isEqual: inc(m.exactMatches) of isEqual: inc(m.exactMatches)
of isNone: discard of isNone: discard
@ -1232,7 +1267,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
elif f.kind == tyStatic: elif f.kind == tyStatic:
return arg.typ.n return arg.typ.n
else: else:
return argOrig return argSemantized # argOrig
if r != isNone and f.isInlineIterator: if r != isNone and f.isInlineIterator:
var inlined = newTypeS(tyStatic, c) var inlined = newTypeS(tyStatic, c)
@ -1244,21 +1279,22 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
case r case r
of isConvertible: of isConvertible:
inc(m.convMatches) inc(m.convMatches)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) result = implicitConv(nkHiddenStdConv, f, arg, m, c)
of isIntConv: of isIntConv:
# I'm too lazy to introduce another ``*matches`` field, so we conflate # I'm too lazy to introduce another ``*matches`` field, so we conflate
# ``isIntConv`` and ``isIntLit`` here: # ``isIntConv`` and ``isIntLit`` here:
inc(m.intConvMatches) inc(m.intConvMatches)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) result = implicitConv(nkHiddenStdConv, f, arg, m, c)
of isSubtype: of isSubtype:
inc(m.subtypeMatches) inc(m.subtypeMatches)
result = implicitConv(nkHiddenSubConv, f, copyTree(arg), m, c) result = implicitConv(nkHiddenSubConv, f, arg, m, c)
of isSubrange: of isSubrange:
inc(m.subtypeMatches) inc(m.subtypeMatches)
#result = copyTree(arg) if f.kind == tyVar:
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) result = arg
else:
result = implicitConv(nkHiddenStdConv, f, arg, m, c)
of isInferred, isInferredConvertible: of isInferred, isInferredConvertible:
inc(m.genericMatches)
if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds: if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds:
result = c.semInferredLambda(c, m.bindings, arg) result = c.semInferredLambda(c, m.bindings, arg)
else: else:
@ -1267,42 +1303,36 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
if r == isInferredConvertible: if r == isInferredConvertible:
inc(m.convMatches) inc(m.convMatches)
result = implicitConv(nkHiddenStdConv, f, result, m, c) result = implicitConv(nkHiddenStdConv, f, result, m, c)
else:
inc(m.genericMatches)
of isGeneric: of isGeneric:
inc(m.genericMatches) inc(m.genericMatches)
when true: if arg.typ == nil:
if skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple: result = arg
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple:
elif arg.typ != nil and arg.typ.isEmptyContainer: result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
result = arg.copyTree elif arg.typ.isEmptyContainer:
result.typ = getInstantiatedType(c, arg, m, f) result = arg.copyTree
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)
result.typ = getInstantiatedType(c, arg, m, f) result.typ = getInstantiatedType(c, arg, m, f)
# BUG: f may not be the right key! else:
if skipTypes(result.typ, abstractVar-{tyTypeDesc}).kind in {tyTuple}: result = arg
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
# BUGFIX: use ``result.typ`` and not `f` here
of isFromIntLit: of isFromIntLit:
# too lazy to introduce another ``*matches`` field, so we conflate # too lazy to introduce another ``*matches`` field, so we conflate
# ``isIntConv`` and ``isIntLit`` here: # ``isIntConv`` and ``isIntLit`` here:
inc(m.intConvMatches, 256) inc(m.intConvMatches, 256)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) result = implicitConv(nkHiddenStdConv, f, arg, m, c)
of isEqual: of isEqual:
inc(m.exactMatches) inc(m.exactMatches)
result = copyTree(arg) result = arg
if skipTypes(f, abstractVar-{tyTypeDesc}).kind in {tyTuple}: 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: of isNone:
# do not do this in ``typeRel`` as it then can't infere T in ``ref T``: # do not do this in ``typeRel`` as it then can't infere T in ``ref T``:
if a.kind in {tyProxy, tyUnknown}: if a.kind in {tyProxy, tyUnknown}:
inc(m.genericMatches) inc(m.genericMatches)
m.fauxMatch = a.kind m.fauxMatch = a.kind
return copyTree(arg) return arg
result = userConvMatch(c, m, f, a, arg) result = userConvMatch(c, m, f, a, arg)
# check for a base type match, which supports varargs[T] without [] # check for a base type match, which supports varargs[T] without []
# constructor in a call: # constructor in a call:
if result == nil and f.kind == tyVarargs: if result == nil and f.kind == tyVarargs:
@ -1323,7 +1353,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
arg, argOrig: PNode): PNode = arg, argOrig: PNode): PNode =
if arg == nil or arg.kind notin nkSymChoices: if arg == nil or arg.kind notin nkSymChoices:
result = paramTypesMatchAux(m, f, a, arg, argOrig) result = paramTypesMatchAux(m, f, a, arg, argOrig)
else: else:
# CAUTION: The order depends on the used hashing scheme. Thus it is # CAUTION: The order depends on the used hashing scheme. Thus it is
# incorrect to simply use the first fitting match. However, to implement # 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 # this correctly is inefficient. We have to copy `m` here to be able to
@ -1337,28 +1367,37 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
y.calleeSym = m.calleeSym y.calleeSym = m.calleeSym
z.calleeSym = m.calleeSym z.calleeSym = m.calleeSym
var best = -1 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: if arg.sons[i].sym.kind in {skProc, skMethod, skConverter}+skIterators:
copyCandidate(z, m) 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) 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 case x.state
of csEmpty, csNoMatch: of csEmpty, csNoMatch:
x = z x = z
best = i best = i
x.state = csMatch x.state = csMatch
of csMatch: of csMatch:
var cmp = cmpCandidates(x, z) var cmp = cmpCandidates(x, z)
if cmp < 0: if cmp < 0:
best = i best = i
x = z x = z
elif cmp == 0: elif cmp == 0:
y = z # z is as good as x y = z # z is as good as x
if x.state == csEmpty: if x.state == csEmpty:
result = nil result = nil
elif y.state == csMatch and cmpCandidates(x, y) == 0: elif y.state == csMatch and cmpCandidates(x, y) == 0:
if x.state != csMatch: if x.state != csMatch:
internalError(arg.info, "x.state is not csMatch") internalError(arg.info, "x.state is not csMatch")
# ambiguous: more than one symbol fits! # ambiguous: more than one symbol fits!
# See tsymchoice_for_expr as an example. 'f.kind == tyExpr' should match # See tsymchoice_for_expr as an example. 'f.kind == tyExpr' should match
# anyway: # anyway:
@ -1371,7 +1410,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
result = paramTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best], result = paramTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best],
argOrig) 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) if sonsLen(father) <= at: setLen(father.sons, at + 1)
father.sons[at] = son father.sons[at] = son
@ -1415,7 +1454,7 @@ proc incrIndexType(t: PType) =
inc t.sons[0].n.sons[1].intVal inc t.sons[0].n.sons[1].intVal
proc matchesAux(c: PContext, n, nOrig: PNode, 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.} = template checkConstraint(n: expr) {.immediate, dirty.} =
if not formal.constraint.isNil: if not formal.constraint.isNil:
if matchNodeKinds(formal.constraint, n): if matchNodeKinds(formal.constraint, n):
@ -1445,20 +1484,20 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
# named param # named param
# check if m.callee has such a param: # check if m.callee has such a param:
prepareNamedParam(n.sons[a]) 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) localError(n.sons[a].info, errNamedParamHasToBeIdent)
m.state = csNoMatch m.state = csNoMatch
return return
formal = getSymFromList(m.callee.n, n.sons[a].sons[0].ident, 1) formal = getSymFromList(m.callee.n, n.sons[a].sons[0].ident, 1)
if formal == nil: if formal == nil:
# no error message! # no error message!
m.state = csNoMatch m.state = csNoMatch
return return
if containsOrIncl(marker, formal.position): if containsOrIncl(marker, formal.position):
# already in namedParams: # already in namedParams:
localError(n.sons[a].info, errCannotBindXTwice, formal.name.s) localError(n.sons[a].info, errCannotBindXTwice, formal.name.s)
m.state = csNoMatch m.state = csNoMatch
return return
m.baseTypeMatch = false m.baseTypeMatch = false
n.sons[a].sons[1] = prepareOperand(c, formal.typ, n.sons[a].sons[1]) n.sons[a].sons[1] = prepareOperand(c, formal.typ, n.sons[a].sons[1])
n.sons[a].typ = n.sons[a].sons[1].typ n.sons[a].typ = n.sons[a].sons[1].typ
@ -1468,7 +1507,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
m.state = csNoMatch m.state = csNoMatch
return return
checkConstraint(n.sons[a].sons[1]) checkConstraint(n.sons[a].sons[1])
if m.baseTypeMatch: if m.baseTypeMatch:
#assert(container == nil) #assert(container == nil)
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg)) container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
addSon(container, arg) addSon(container, arg)
@ -1505,7 +1544,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
m.state = csNoMatch m.state = csNoMatch
return return
else: else:
if m.callee.n.sons[f].kind != nkSym: if m.callee.n.sons[f].kind != nkSym:
internalError(n.sons[a].info, "matches") internalError(n.sons[a].info, "matches")
return return
formal = m.callee.n.sons[f].sym formal = m.callee.n.sons[f].sym
@ -1513,7 +1552,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
# already in namedParams: # already in namedParams:
localError(n.sons[a].info, errCannotBindXTwice, formal.name.s) localError(n.sons[a].info, errCannotBindXTwice, formal.name.s)
m.state = csNoMatch m.state = csNoMatch
return return
m.baseTypeMatch = false m.baseTypeMatch = false
n.sons[a] = prepareOperand(c, formal.typ, n.sons[a]) n.sons[a] = prepareOperand(c, formal.typ, n.sons[a])
var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
@ -1526,7 +1565,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
if container.isNil: if container.isNil:
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg)) container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
addSon(container, arg) addSon(container, arg)
setSon(m.call, formal.position + 1, setSon(m.call, formal.position + 1,
implicitConv(nkHiddenStdConv, formal.typ, container, m, c)) implicitConv(nkHiddenStdConv, formal.typ, container, m, c))
#if f != formalLen - 1: container = nil #if f != formalLen - 1: container = nil
@ -1601,7 +1640,7 @@ when not declared(tests):
tests: tests:
var dummyOwner = newSym(skModule, getIdent("test_module"), nil, UnknownLineInfo()) var dummyOwner = newSym(skModule, getIdent("test_module"), nil, UnknownLineInfo())
proc `|` (t1, t2: PType): PType = proc `|` (t1, t2: PType): PType =
result = newType(tyOr, dummyOwner) result = newType(tyOr, dummyOwner)
result.rawAddSon(t1) result.rawAddSon(t1)
@ -1622,12 +1661,12 @@ tests:
proc array(x: int, t: PType): PType = proc array(x: int, t: PType): PType =
result = newType(tyArray, dummyOwner) result = newType(tyArray, dummyOwner)
var n = newNodeI(nkRange, UnknownLineInfo()) var n = newNodeI(nkRange, UnknownLineInfo())
addSon(n, newIntNode(nkIntLit, 0)) addSon(n, newIntNode(nkIntLit, 0))
addSon(n, newIntNode(nkIntLit, x)) addSon(n, newIntNode(nkIntLit, x))
let range = newType(tyRange, dummyOwner) let range = newType(tyRange, dummyOwner)
result.rawAddSon(range) result.rawAddSon(range)
result.rawAddSon(t) result.rawAddSon(t)
@ -1653,7 +1692,7 @@ tests:
setup: setup:
var c: TCandidate var c: TCandidate
InitCandidate(nil, c, nil) initCandidate(nil, c, nil)
template yes(x, y) = template yes(x, y) =
test astToStr(x) & " is " & astToStr(y): test astToStr(x) & " is " & astToStr(y):
@ -1662,7 +1701,7 @@ tests:
template no(x, y) = template no(x, y) =
test astToStr(x) & " is not " & astToStr(y): test astToStr(x) & " is not " & astToStr(y):
check typeRel(c, y, x) == isNone check typeRel(c, y, x) == isNone
yes seq(any), array(10, int) | seq(any) yes seq(any), array(10, int) | seq(any)
# Sure, seq[any] is directly included # Sure, seq[any] is directly included
@ -1670,16 +1709,16 @@ tests:
yes seq(int), seq(number) yes seq(int), seq(number)
# Sure, the int sequence is certainly # Sure, the int sequence is certainly
# part of the number sequences (and all sequences) # part of the number sequences (and all sequences)
no seq(any), seq(float) no seq(any), seq(float)
# Nope, seq[any] includes types that are not seq[float] (e.g. seq[int]) # Nope, seq[any] includes types that are not seq[float] (e.g. seq[int])
yes seq(int|string), seq(any) yes seq(int|string), seq(any)
# Sure # Sure
yes seq(int&string), seq(any) yes seq(int&string), seq(any)
# Again # Again
yes seq(int&string), seq(int) yes seq(int&string), seq(int)
# A bit more complicated # A bit more complicated
# seq[int&string] is not a real type, but it's analogous to # seq[int&string] is not a real type, but it's analogous to
@ -1688,23 +1727,23 @@ tests:
no seq(int|string), seq(int|float) no seq(int|string), seq(int|float)
# Nope, seq[string] is not included in not included in # Nope, seq[string] is not included in not included in
# the seq[int|float] set # the seq[int|float] set
no seq(!(int|string)), seq(string) no seq(!(int|string)), seq(string)
# A sequence that is neither seq[int] or seq[string] # A sequence that is neither seq[int] or seq[string]
# is obviously not seq[string] # is obviously not seq[string]
no seq(!int), seq(number) no seq(!int), seq(number)
# Now your head should start to hurt a bit # Now your head should start to hurt a bit
# A sequence that is not seq[int] is not necessarily a number sequence # A sequence that is not seq[int] is not necessarily a number sequence
# it could well be seq[string] for example # it could well be seq[string] for example
yes seq(!(int|string)), seq(!string) yes seq(!(int|string)), seq(!string)
# all sequnece types besides seq[int] and seq[string] # all sequnece types besides seq[int] and seq[string]
# are subset of all sequence types that are not seq[string] # are subset of all sequence types that are not seq[string]
no seq(!(int|string)), seq(!(string|TFoo)) no seq(!(int|string)), seq(!(string|TFoo))
# Nope, seq[TFoo] is included in the first set, but not in the second # Nope, seq[TFoo] is included in the first set, but not in the second
no seq(!string), seq(!number) no seq(!string), seq(!number)
# Nope, seq[int] in included in the first set, but not in the second # Nope, seq[int] in included in the first set, but not in the second
@ -1712,7 +1751,7 @@ tests:
yes seq(!int), seq(any) yes seq(!int), seq(any)
no seq(any), seq(!any) no seq(any), seq(!any)
no seq(!int), seq(!any) no seq(!int), seq(!any)
yes int, ordinal yes int, ordinal
no string, ordinal no string, ordinal

View file

@ -17,27 +17,27 @@
# * introduces method dispatchers # * introduces method dispatchers
# * performs lambda lifting for closure support # * performs lambda lifting for closure support
import import
intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os, intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread, idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread,
lambdalifting, sempass2, lowerings lambdalifting, sempass2, lowerings
# implementation # implementation
type type
PTransNode* = distinct PNode PTransNode* = distinct PNode
PTransCon = ref TTransCon PTransCon = ref TTransCon
TTransCon{.final.} = object # part of TContext; stackable TTransCon{.final.} = object # part of TContext; stackable
mapping: TIdNodeTable # mapping from symbols to nodes mapping: TIdNodeTable # mapping from symbols to nodes
owner: PSym # current owner owner: PSym # current owner
forStmt: PNode # current for stmt forStmt: PNode # current for stmt
forLoopBody: PTransNode # transformed for loop body forLoopBody: PTransNode # transformed for loop body
yieldStmts: int # we count the number of yield statements, yieldStmts: int # we count the number of yield statements,
# because we need to introduce new variables # because we need to introduce new variables
# if we encounter the 2nd yield statement # if we encounter the 2nd yield statement
next: PTransCon # for stacking next: PTransCon # for stacking
TTransfContext = object of passes.TPassContext TTransfContext = object of passes.TPassContext
module: PSym module: PSym
transCon: PTransCon # top of a TransCon stack transCon: PTransCon # top of a TransCon stack
@ -46,52 +46,52 @@ type
contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break'
PTransf = ref TTransfContext PTransf = ref TTransfContext
proc newTransNode(a: PNode): PTransNode {.inline.} = proc newTransNode(a: PNode): PTransNode {.inline.} =
result = PTransNode(shallowCopy(a)) result = PTransNode(shallowCopy(a))
proc newTransNode(kind: TNodeKind, info: TLineInfo, proc newTransNode(kind: TNodeKind, info: TLineInfo,
sons: int): PTransNode {.inline.} = sons: int): PTransNode {.inline.} =
var x = newNodeI(kind, info) var x = newNodeI(kind, info)
newSeq(x.sons, sons) newSeq(x.sons, sons)
result = x.PTransNode result = x.PTransNode
proc newTransNode(kind: TNodeKind, n: PNode, proc newTransNode(kind: TNodeKind, n: PNode,
sons: int): PTransNode {.inline.} = sons: int): PTransNode {.inline.} =
var x = newNodeIT(kind, n.info, n.typ) var x = newNodeIT(kind, n.info, n.typ)
newSeq(x.sons, sons) newSeq(x.sons, sons)
x.typ = n.typ x.typ = n.typ
result = x.PTransNode result = x.PTransNode
proc `[]=`(a: PTransNode, i: int, x: PTransNode) {.inline.} = proc `[]=`(a: PTransNode, i: int, x: PTransNode) {.inline.} =
var n = PNode(a) var n = PNode(a)
n.sons[i] = PNode(x) n.sons[i] = PNode(x)
proc `[]`(a: PTransNode, i: int): PTransNode {.inline.} = proc `[]`(a: PTransNode, i: int): PTransNode {.inline.} =
var n = PNode(a) var n = PNode(a)
result = n.sons[i].PTransNode result = n.sons[i].PTransNode
proc add(a, b: PTransNode) {.inline.} = addSon(PNode(a), PNode(b)) proc add(a, b: PTransNode) {.inline.} = addSon(PNode(a), PNode(b))
proc len(a: PTransNode): int {.inline.} = result = sonsLen(a.PNode) proc len(a: PTransNode): int {.inline.} = result = sonsLen(a.PNode)
proc newTransCon(owner: PSym): PTransCon = proc newTransCon(owner: PSym): PTransCon =
assert owner != nil assert owner != nil
new(result) new(result)
initIdNodeTable(result.mapping) initIdNodeTable(result.mapping)
result.owner = owner result.owner = owner
proc pushTransCon(c: PTransf, t: PTransCon) = proc pushTransCon(c: PTransf, t: PTransCon) =
t.next = c.transCon t.next = c.transCon
c.transCon = t c.transCon = t
proc popTransCon(c: PTransf) = proc popTransCon(c: PTransf) =
if (c.transCon == nil): internalError("popTransCon") if (c.transCon == nil): internalError("popTransCon")
c.transCon = c.transCon.next c.transCon = c.transCon.next
proc getCurrOwner(c: PTransf): PSym = proc getCurrOwner(c: PTransf): PSym =
if c.transCon != nil: result = c.transCon.owner if c.transCon != nil: result = c.transCon.owner
else: result = c.module 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 = newSym(skTemp, getIdent(genPrefix), getCurrOwner(c), info)
result.typ = skipTypes(typ, {tyGenericInst}) result.typ = skipTypes(typ, {tyGenericInst})
incl(result.flags, sfFromGeneric) incl(result.flags, sfFromGeneric)
@ -100,10 +100,10 @@ proc transform(c: PTransf, n: PNode): PTransNode
proc transformSons(c: PTransf, n: PNode): PTransNode = proc transformSons(c: PTransf, n: PNode): PTransNode =
result = newTransNode(n) 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]) 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 = newTransNode(nkFastAsgn, PNode(ri).info, 2)
result[0] = PTransNode(le) result[0] = PTransNode(le)
result[1] = ri result[1] = ri
@ -113,30 +113,30 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
# return liftIterSym(n) # return liftIterSym(n)
var b: PNode var b: PNode
var tc = c.transCon var tc = c.transCon
if sfBorrow in n.sym.flags: if sfBorrow in n.sym.flags:
# simply exchange the symbol: # simply exchange the symbol:
b = n.sym.getBody b = n.sym.getBody
if b.kind != nkSym: internalError(n.info, "wrong AST for borrowed symbol") if b.kind != nkSym: internalError(n.info, "wrong AST for borrowed symbol")
b = newSymNode(b.sym) b = newSymNode(b.sym)
b.info = n.info b.info = n.info
else: else:
b = n b = n
while tc != nil: while tc != nil:
result = idNodeTableGet(tc.mapping, b.sym) result = idNodeTableGet(tc.mapping, b.sym)
if result != nil: return if result != nil: return
tc = tc.next tc = tc.next
result = b result = b
proc transformSym(c: PTransf, n: PNode): PTransNode = proc transformSym(c: PTransf, n: PNode): PTransNode =
result = PTransNode(transformSymAux(c, n)) result = PTransNode(transformSymAux(c, n))
proc transformVarSection(c: PTransf, v: PNode): PTransNode = proc transformVarSection(c: PTransf, v: PNode): PTransNode =
result = newTransNode(v) result = newTransNode(v)
for i in countup(0, sonsLen(v)-1): for i in countup(0, sonsLen(v)-1):
var it = v.sons[i] var it = v.sons[i]
if it.kind == nkCommentStmt: if it.kind == nkCommentStmt:
result[i] = PTransNode(it) result[i] = PTransNode(it)
elif it.kind == nkIdentDefs: elif it.kind == nkIdentDefs:
if it.sons[0].kind != nkSym: internalError(it.info, "transformVarSection") if it.sons[0].kind != nkSym: internalError(it.info, "transformVarSection")
internalAssert(it.len == 3) internalAssert(it.len == 3)
var newVar = copySym(it.sons[0].sym) var newVar = copySym(it.sons[0].sym)
@ -153,12 +153,12 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode =
defs[1] = it.sons[1].PTransNode defs[1] = it.sons[1].PTransNode
defs[2] = transform(c, it.sons[2]) defs[2] = transform(c, it.sons[2])
result[i] = defs result[i] = defs
else: else:
if it.kind != nkVarTuple: if it.kind != nkVarTuple:
internalError(it.info, "transformVarSection: not nkVarTuple") internalError(it.info, "transformVarSection: not nkVarTuple")
var L = sonsLen(it) var L = sonsLen(it)
var defs = newTransNode(it.kind, it.info, L) 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) var newVar = copySym(it.sons[j].sym)
incl(newVar.flags, sfFromGeneric) incl(newVar.flags, sfFromGeneric)
newVar.owner = getCurrOwner(c) newVar.owner = getCurrOwner(c)
@ -188,12 +188,12 @@ proc transformConstSection(c: PTransf, v: PNode): PTransNode =
else: else:
result[i] = PTransNode(it) result[i] = PTransNode(it)
proc hasContinue(n: PNode): bool = proc hasContinue(n: PNode): bool =
case n.kind case n.kind
of nkEmpty..nkNilLit, nkForStmt, nkParForStmt, nkWhileStmt: discard of nkEmpty..nkNilLit, nkForStmt, nkParForStmt, nkWhileStmt: discard
of nkContinueStmt: result = true of nkContinueStmt: result = true
else: else:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if hasContinue(n.sons[i]): return true if hasContinue(n.sons[i]): return true
proc newLabel(c: PTransf, n: PNode): PSym = proc newLabel(c: PTransf, n: PNode): PSym =
@ -224,10 +224,10 @@ proc transformBlock(c: PTransf, n: PNode): PTransNode =
discard c.breakSyms.pop discard c.breakSyms.pop
result[0] = newSymNode(labl).PTransNode result[0] = newSymNode(labl).PTransNode
proc transformLoopBody(c: PTransf, n: PNode): PTransNode = proc transformLoopBody(c: PTransf, n: PNode): PTransNode =
# What if it contains "continue" and "break"? "break" needs # What if it contains "continue" and "break"? "break" needs
# an explicit label too, but not the same! # an explicit label too, but not the same!
# We fix this here by making every 'break' belong to its enclosing loop # 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 # and changing all breaks that belong to a 'block' by annotating it with
# a label (if it hasn't one already). # a label (if it hasn't one already).
@ -239,7 +239,7 @@ proc transformLoopBody(c: PTransf, n: PNode): PTransNode =
result[0] = newSymNode(labl).PTransNode result[0] = newSymNode(labl).PTransNode
result[1] = transform(c, n) result[1] = transform(c, n)
discard c.contSyms.pop() discard c.contSyms.pop()
else: else:
result = transform(c, n) result = transform(c, n)
proc transformWhile(c: PTransf; n: PNode): PTransNode = proc transformWhile(c: PTransf; n: PNode): PTransNode =
@ -273,27 +273,27 @@ proc transformBreak(c: PTransf, n: PNode): PTransNode =
result = transformSons(c, n) result = transformSons(c, n)
result[0] = newSymNode(labl).PTransNode 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? # XXX: BUG: what if `n` is an expression with side-effects?
for i in countup(0, sonsLen(c.transCon.forStmt) - 3): for i in countup(0, sonsLen(c.transCon.forStmt) - 3):
add(father, newAsgnStmt(c, c.transCon.forStmt.sons[i], add(father, newAsgnStmt(c, c.transCon.forStmt.sons[i],
transform(c, newTupleAccess(n, i)))) transform(c, newTupleAccess(n, i))))
proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode = proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode =
case n.kind case n.kind
of nkSym: of nkSym:
result = transformSym(c, n) result = transformSym(c, n)
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
# nothing to be done for leaves: # nothing to be done for leaves:
result = PTransNode(n) result = PTransNode(n)
of nkVarSection, nkLetSection: of nkVarSection, nkLetSection:
result = transformVarSection(c, n) result = transformVarSection(c, n)
else: else:
result = newTransNode(n) 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]) 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) result = newTransNode(nkStmtList, n.info, 0)
var e = n.sons[0] var e = n.sons[0]
# c.transCon.forStmt.len == 3 means that there is one for loop variable # c.transCon.forStmt.len == 3 means that there is one for loop variable
@ -301,21 +301,21 @@ proc transformYield(c: PTransf, n: PNode): PTransNode =
if skipTypes(e.typ, {tyGenericInst}).kind == tyTuple and if skipTypes(e.typ, {tyGenericInst}).kind == tyTuple and
c.transCon.forStmt.len != 3: c.transCon.forStmt.len != 3:
e = skipConv(e) e = skipConv(e)
if e.kind == nkPar: if e.kind == nkPar:
for i in countup(0, sonsLen(e) - 1): for i in countup(0, sonsLen(e) - 1):
add(result, newAsgnStmt(c, c.transCon.forStmt.sons[i], add(result, newAsgnStmt(c, c.transCon.forStmt.sons[i],
transform(c, e.sons[i]))) transform(c, e.sons[i])))
else: else:
unpackTuple(c, e, result) unpackTuple(c, e, result)
else: else:
var x = transform(c, e) var x = transform(c, e)
add(result, newAsgnStmt(c, c.transCon.forStmt.sons[0], x)) add(result, newAsgnStmt(c, c.transCon.forStmt.sons[0], x))
inc(c.transCon.yieldStmts) inc(c.transCon.yieldStmts)
if c.transCon.yieldStmts <= 1: if c.transCon.yieldStmts <= 1:
# common case # common case
add(result, c.transCon.forLoopBody) add(result, c.transCon.forLoopBody)
else: else:
# we need to introduce new local variables: # we need to introduce new local variables:
add(result, introduceNewLocalVars(c, c.transCon.forLoopBody.PNode)) add(result, introduceNewLocalVars(c, c.transCon.forLoopBody.PNode))
@ -340,25 +340,25 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
if n.sons[0].kind == a or n.sons[0].kind == b: if n.sons[0].kind == a or n.sons[0].kind == b:
# addr ( deref ( x )) --> x # addr ( deref ( x )) --> x
result = PTransNode(n.sons[0].sons[0]) 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: # numeric types need range checks:
var dest = skipTypes(n.typ, abstractVarRange) var dest = skipTypes(n.typ, abstractVarRange)
var source = skipTypes(n.sons[1].typ, abstractVarRange) var source = skipTypes(n.sons[1].typ, abstractVarRange)
case dest.kind 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 ;-) # we don't include uint and uint64 here as these are no ordinal types ;-)
if not isOrdinalType(source): if not isOrdinalType(source):
# float -> int conversions. ugh. # float -> int conversions. ugh.
result = transformSons(c, n) result = transformSons(c, n)
elif firstOrd(n.typ) <= firstOrd(n.sons[1].typ) and 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, # BUGFIX: simply leave n as it is; we need a nkConv node,
# but no range check: # but no range check:
result = transformSons(c, n) result = transformSons(c, n)
else: else:
# generate a range check: # 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) result = newTransNode(nkChckRange64, n, 3)
else: else:
result = newTransNode(nkChckRange, n, 3) result = newTransNode(nkChckRange, n, 3)
@ -368,7 +368,7 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
result[2] = newIntTypeNode(nkIntLit, lastOrd(dest), source).PTransNode result[2] = newIntTypeNode(nkIntLit, lastOrd(dest), source).PTransNode
of tyFloat..tyFloat128: of tyFloat..tyFloat128:
# XXX int64 -> float conversion? # XXX int64 -> float conversion?
if skipTypes(n.typ, abstractVar).kind == tyRange: if skipTypes(n.typ, abstractVar).kind == tyRange:
result = newTransNode(nkChckRangeF, n, 3) result = newTransNode(nkChckRangeF, n, 3)
dest = skipTypes(n.typ, abstractVar) dest = skipTypes(n.typ, abstractVar)
result[0] = transform(c, n.sons[1]) result[0] = transform(c, n.sons[1])
@ -378,81 +378,81 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
result = transformSons(c, n) result = transformSons(c, n)
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
result = transform(c, n.sons[1]) result = transform(c, n.sons[1])
of tyCString: of tyCString:
if source.kind == tyString: if source.kind == tyString:
result = newTransNode(nkStringToCString, n, 1) result = newTransNode(nkStringToCString, n, 1)
result[0] = transform(c, n.sons[1]) result[0] = transform(c, n.sons[1])
else: else:
result = transformSons(c, n) result = transformSons(c, n)
of tyString: of tyString:
if source.kind == tyCString: if source.kind == tyCString:
result = newTransNode(nkCStringToString, n, 1) result = newTransNode(nkCStringToString, n, 1)
result[0] = transform(c, n.sons[1]) result[0] = transform(c, n.sons[1])
else: else:
result = transformSons(c, n) result = transformSons(c, n)
of tyRef, tyPtr: of tyRef, tyPtr:
dest = skipTypes(dest, abstractPtrs) dest = skipTypes(dest, abstractPtrs)
source = skipTypes(source, abstractPtrs) source = skipTypes(source, abstractPtrs)
if source.kind == tyObject: if source.kind == tyObject:
var diff = inheritanceDiff(dest, source) var diff = inheritanceDiff(dest, source)
if diff < 0: if diff < 0:
result = newTransNode(nkObjUpConv, n, 1) result = newTransNode(nkObjUpConv, n, 1)
result[0] = transform(c, n.sons[1]) result[0] = transform(c, n.sons[1])
elif diff > 0: elif diff > 0:
result = newTransNode(nkObjDownConv, n, 1) result = newTransNode(nkObjDownConv, n, 1)
result[0] = transform(c, n.sons[1]) result[0] = transform(c, n.sons[1])
else: else:
result = transform(c, n.sons[1]) result = transform(c, n.sons[1])
else: else:
result = transformSons(c, n) result = transformSons(c, n)
of tyObject: of tyObject:
var diff = inheritanceDiff(dest, source) var diff = inheritanceDiff(dest, source)
if diff < 0: if diff < 0:
result = newTransNode(nkObjUpConv, n, 1) result = newTransNode(nkObjUpConv, n, 1)
result[0] = transform(c, n.sons[1]) result[0] = transform(c, n.sons[1])
elif diff > 0: elif diff > 0:
result = newTransNode(nkObjDownConv, n, 1) result = newTransNode(nkObjDownConv, n, 1)
result[0] = transform(c, n.sons[1]) result[0] = transform(c, n.sons[1])
else: else:
result = transform(c, n.sons[1]) result = transform(c, n.sons[1])
of tyGenericParam, tyOrdinal: of tyGenericParam, tyOrdinal:
result = transform(c, n.sons[1]) result = transform(c, n.sons[1])
# happens sometimes for generated assignments, etc. # happens sometimes for generated assignments, etc.
else: else:
result = transformSons(c, n) result = transformSons(c, n)
type type
TPutArgInto = enum TPutArgInto = enum
paDirectMapping, paFastAsgn, paVarAsgn 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 # This analyses how to treat the mapping "formal <-> arg" in an
# inline context. # inline context.
if skipTypes(formal, abstractInst).kind in {tyOpenArray, tyVarargs}: if skipTypes(formal, abstractInst).kind in {tyOpenArray, tyVarargs}:
return paDirectMapping # XXX really correct? return paDirectMapping # XXX really correct?
# what if ``arg`` has side-effects? # what if ``arg`` has side-effects?
case arg.kind case arg.kind
of nkEmpty..nkNilLit: of nkEmpty..nkNilLit:
result = paDirectMapping result = paDirectMapping
of nkPar, nkCurly, nkBracket: of nkPar, nkCurly, nkBracket:
result = paFastAsgn result = paFastAsgn
for i in countup(0, sonsLen(arg) - 1): for i in countup(0, sonsLen(arg) - 1):
if putArgInto(arg.sons[i], formal) != paDirectMapping: return if putArgInto(arg.sons[i], formal) != paDirectMapping: return
result = paDirectMapping result = paDirectMapping
else: else:
if skipTypes(formal, abstractInst).kind == tyVar: result = paVarAsgn if skipTypes(formal, abstractInst).kind == tyVar: result = paVarAsgn
else: result = paFastAsgn else: result = paFastAsgn
proc findWrongOwners(c: PTransf, n: PNode) = proc findWrongOwners(c: PTransf, n: PNode) =
if n.kind == nkVarSection: if n.kind == nkVarSection:
let x = n.sons[0].sons[0] let x = n.sons[0].sons[0]
if x.kind == nkSym and x.sym.owner != getCurrOwner(c): 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) x.sym.owner.name.s & " " & getCurrOwner(c).name.s)
else: else:
for i in 0 .. <safeLen(n): findWrongOwners(c, n.sons[i]) 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) # generate access statements for the parameters (unless they are constant)
# put mapping from formal parameters to actual parameters # put mapping from formal parameters to actual parameters
if n.kind != nkForStmt: internalError(n.info, "transformFor") if n.kind != nkForStmt: internalError(n.info, "transformFor")
@ -466,26 +466,26 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
result[0] = newSymNode(labl).PTransNode result[0] = newSymNode(labl).PTransNode
if call.typ.kind != tyIter and 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): call.sons[0].sym.kind != skIterator):
n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode
result[1] = lambdalifting.liftForLoop(n).PTransNode result[1] = lambdalifting.liftForLoop(n).PTransNode
discard c.breakSyms.pop discard c.breakSyms.pop
return result return result
#echo "transforming: ", renderTree(n) #echo "transforming: ", renderTree(n)
var stmtList = newTransNode(nkStmtList, n.info, 0) var stmtList = newTransNode(nkStmtList, n.info, 0)
var loopBody = transformLoopBody(c, n.sons[length-1]) var loopBody = transformLoopBody(c, n.sons[length-1])
result[1] = stmtList result[1] = stmtList
discard c.breakSyms.pop discard c.breakSyms.pop
var v = newNodeI(nkVarSection, n.info) 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 addVar(v, copyTree(n.sons[i])) # declare new vars
add(stmtList, v.PTransNode) add(stmtList, v.PTransNode)
# Bugfix: inlined locals belong to the invoking routine, not to the invoked # Bugfix: inlined locals belong to the invoking routine, not to the invoked
# iterator! # iterator!
let iter = call.sons[0].sym let iter = call.sons[0].sym
@ -496,9 +496,9 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
if iter.kind != skIterator: return result if iter.kind != skIterator: return result
# generate access statements for the parameters (unless they are constant) # generate access statements for the parameters (unless they are constant)
pushTransCon(c, newC) 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 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 if arg.typ.kind == tyIter: continue
case putArgInto(arg, formal.typ) case putArgInto(arg, formal.typ)
of paDirectMapping: of paDirectMapping:
@ -527,20 +527,20 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
popInfoContext() popInfoContext()
popTransCon(c) popTransCon(c)
# echo "transformed: ", stmtList.PNode.renderTree # echo "transformed: ", stmtList.PNode.renderTree
proc getMagicOp(call: PNode): TMagic = proc getMagicOp(call: PNode): TMagic =
if call.sons[0].kind == nkSym and 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 result = call.sons[0].sym.magic
else: else:
result = mNone result = mNone
proc transformCase(c: PTransf, n: PNode): PTransNode = proc transformCase(c: PTransf, n: PNode): PTransNode =
# removes `elif` branches of a case stmt # removes `elif` branches of a case stmt
# adds ``else: nil`` if needed for the code generator # adds ``else: nil`` if needed for the code generator
result = newTransNode(nkCaseStmt, n, 0) result = newTransNode(nkCaseStmt, n, 0)
var ifs = PTransNode(nil) var ifs = PTransNode(nil)
for i in 0 .. sonsLen(n)-1: for i in 0 .. sonsLen(n)-1:
var it = n.sons[i] var it = n.sons[i]
var e = transform(c, it) var e = transform(c, it)
case it.kind case it.kind
@ -564,8 +564,8 @@ proc transformCase(c: PTransf, n: PNode): PTransNode =
var elseBranch = newTransNode(nkElse, n.info, 1) var elseBranch = newTransNode(nkElse, n.info, 1)
elseBranch[0] = newTransNode(nkNilLit, n.info, 0) elseBranch[0] = newTransNode(nkNilLit, n.info, 0)
add(result, elseBranch) 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 # 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: if n.sons[0].kind == nkSym and n.sons[0].sym.kind == skType:
result = n.PTransNode result = n.PTransNode
@ -573,45 +573,45 @@ proc transformArrayAccess(c: PTransf, n: PNode): PTransNode =
result = newTransNode(n) result = newTransNode(n)
for i in 0 .. < n.len: for i in 0 .. < n.len:
result[i] = transform(c, skipConv(n.sons[i])) result[i] = transform(c, skipConv(n.sons[i]))
proc getMergeOp(n: PNode): PSym = proc getMergeOp(n: PNode): PSym =
case n.kind case n.kind
of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix, of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
nkCallStrLit: nkCallStrLit:
if (n.sons[0].kind == nkSym) and (n.sons[0].sym.kind == skProc) and 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 result = n.sons[0].sym
else: discard else: discard
proc flattenTreeAux(d, a: PNode, op: PSym) = proc flattenTreeAux(d, a: PNode, op: PSym) =
let op2 = getMergeOp(a) let op2 = getMergeOp(a)
if op2 != nil and 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) for i in countup(1, sonsLen(a)-1): flattenTreeAux(d, a.sons[i], op)
else: else:
addSon(d, copyTree(a)) addSon(d, copyTree(a))
proc flattenTree(root: PNode): PNode = proc flattenTree(root: PNode): PNode =
let op = getMergeOp(root) let op = getMergeOp(root)
if op != nil: if op != nil:
result = copyNode(root) result = copyNode(root)
addSon(result, copyTree(root.sons[0])) addSon(result, copyTree(root.sons[0]))
flattenTreeAux(result, root, op) flattenTreeAux(result, root, op)
else: else:
result = root result = root
proc transformCall(c: PTransf, n: PNode): PTransNode = proc transformCall(c: PTransf, n: PNode): PTransNode =
var n = flattenTree(n) var n = flattenTree(n)
let op = getMergeOp(n) let op = getMergeOp(n)
let magic = getMagic(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) result = newTransNode(nkCall, n, 0)
add(result, transform(c, n.sons[0])) add(result, transform(c, n.sons[0]))
var j = 1 var j = 1
while j < sonsLen(n): while j < sonsLen(n):
var a = transform(c, n.sons[j]).PNode var a = transform(c, n.sons[j]).PNode
inc(j) inc(j)
if isConstExpr(a): if isConstExpr(a):
while (j < sonsLen(n)): while (j < sonsLen(n)):
let b = transform(c, n.sons[j]).PNode let b = transform(c, n.sons[j]).PNode
if not isConstExpr(b): break if not isConstExpr(b): break
@ -640,7 +640,7 @@ proc transformCall(c: PTransf, n: PNode): PTransNode =
proc dontInlineConstant(orig, cnst: PNode): bool {.inline.} = proc dontInlineConstant(orig, cnst: PNode): bool {.inline.} =
# symbols that expand to a complex constant (array, etc.) should not be # symbols that expand to a complex constant (array, etc.) should not be
# inlined, unless it's the empty array: # 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 cnst.len != 0
proc commonOptimizations*(c: PSym, n: PNode): PNode = proc commonOptimizations*(c: PSym, n: PNode): PNode =
@ -673,11 +673,11 @@ proc commonOptimizations*(c: PSym, n: PNode): PNode =
else: else:
result = n result = n
proc transform(c: PTransf, n: PNode): PTransNode = proc transform(c: PTransf, n: PNode): PTransNode =
case n.kind case n.kind
of nkSym: of nkSym:
result = transformSym(c, n) result = transformSym(c, n)
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
# nothing to be done for leaves: # nothing to be done for leaves:
result = PTransNode(n) result = PTransNode(n)
of nkBracketExpr: result = transformArrayAccess(c, n) of nkBracketExpr: result = transformArrayAccess(c, n)
@ -702,7 +702,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
n.sons[bodyPos] = PNode(transform(c, s.getBody)) n.sons[bodyPos] = PNode(transform(c, s.getBody))
if n.kind == nkMethodDef: methodDef(s, false) if n.kind == nkMethodDef: methodDef(s, false)
result = PTransNode(n) result = PTransNode(n)
of nkForStmt: of nkForStmt:
result = transformFor(c, n) result = transformFor(c, n)
of nkParForStmt: of nkParForStmt:
result = transformSons(c, n) result = transformSons(c, n)
@ -713,14 +713,14 @@ proc transform(c: PTransf, n: PNode): PTransNode =
add(result, PTransNode(newSymNode(labl))) add(result, PTransNode(newSymNode(labl)))
of nkBreakStmt: result = transformBreak(c, n) of nkBreakStmt: result = transformBreak(c, n)
of nkWhileStmt: result = transformWhile(c, n) of nkWhileStmt: result = transformWhile(c, n)
of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix, of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
nkCallStrLit: nkCallStrLit:
result = transformCall(c, n) result = transformCall(c, n)
of nkAddr, nkHiddenAddr: of nkAddr, nkHiddenAddr:
result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref) result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref)
of nkDerefExpr, nkHiddenDeref: of nkDerefExpr, nkHiddenDeref:
result = transformAddrDeref(c, n, nkAddr, nkHiddenAddr) result = transformAddrDeref(c, n, nkAddr, nkHiddenAddr)
of nkHiddenStdConv, nkHiddenSubConv, nkConv: of nkHiddenStdConv, nkHiddenSubConv, nkConv:
result = transformConv(c, n) result = transformConv(c, n)
of nkDiscardStmt: of nkDiscardStmt:
result = PTransNode(n) result = PTransNode(n)
@ -730,7 +730,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
# ensure that e.g. discard "some comment" gets optimized away # ensure that e.g. discard "some comment" gets optimized away
# completely: # completely:
result = PTransNode(newNode(nkCommentStmt)) result = PTransNode(newNode(nkCommentStmt))
of nkCommentStmt, nkTemplateDef: of nkCommentStmt, nkTemplateDef:
return n.PTransNode return n.PTransNode
of nkConstSection: of nkConstSection:
# do not replace ``const c = 3`` with ``const 3 = 3`` # do not replace ``const c = 3`` with ``const 3 = 3``
@ -744,10 +744,10 @@ proc transform(c: PTransf, n: PNode): PTransNode =
result = transformVarSection(c, n) result = transformVarSection(c, n)
else: else:
result = transformSons(c, n) result = transformSons(c, n)
of nkYieldStmt: of nkYieldStmt:
if c.inlining > 0: if c.inlining > 0:
result = transformYield(c, n) result = transformYield(c, n)
else: else:
result = transformSons(c, n) result = transformSons(c, n)
of nkBlockStmt, nkBlockExpr: of nkBlockStmt, nkBlockExpr:
result = transformBlock(c, n) result = transformBlock(c, n)
@ -764,7 +764,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
if cnst != nil and not dontInlineConstant(n, cnst): if cnst != nil and not dontInlineConstant(n, cnst):
result = PTransNode(cnst) # do not miss an optimization 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 # Note: For interactive mode we cannot call 'passes.skipCodegen' and skip
# this step! We have to rely that the semantic pass transforms too errornous # this step! We have to rely that the semantic pass transforms too errornous
# nodes into an empty node. # nodes into an empty node.
@ -774,7 +774,7 @@ proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode =
popTransCon(c) popTransCon(c)
incl(result.flags, nfTransf) incl(result.flags, nfTransf)
proc openTransf(module: PSym, filename: string): PTransf = proc openTransf(module: PSym, filename: string): PTransf =
new(result) new(result)
result.contSyms = @[] result.contSyms = @[]
result.breakSyms = @[] result.breakSyms = @[]

File diff suppressed because it is too large Load diff

View file

@ -68,7 +68,6 @@ proc renderType(n: PNode): string =
assert n[i].kind == nkIdent assert n[i].kind == nkIdent
result.add(',' & typeStr) result.add(',' & typeStr)
of nkTupleTy: of nkTupleTy:
assert len(n) > 0
result = "tuple[" result = "tuple["
for i in 0 .. <len(n): result.add(renderType(n[i]) & ',') for i in 0 .. <len(n): result.add(renderType(n[i]) & ',')
result[<len(result)] = ']' result[<len(result)] = ']'

View file

@ -239,7 +239,7 @@ proc pushSafePoint(f: PStackFrame; pc: int) =
proc popSafePoint(f: PStackFrame) = discard f.safePoints.pop() proc popSafePoint(f: PStackFrame) = discard f.safePoints.pop()
proc cleanUpOnException(c: PCtx; tos: PStackFrame): proc cleanUpOnException(c: PCtx; tos: PStackFrame):
tuple[pc: int, f: PStackFrame] = tuple[pc: int, f: PStackFrame] =
let raisedType = c.currentExceptionA.typ.skipTypes(abstractPtrs) let raisedType = c.currentExceptionA.typ.skipTypes(abstractPtrs)
var f = tos var f = tos
@ -257,7 +257,7 @@ proc cleanUpOnException(c: PCtx; tos: PStackFrame):
let exceptType = c.types[c.code[pc2].regBx-wordExcess].skipTypes( let exceptType = c.types[c.code[pc2].regBx-wordExcess].skipTypes(
abstractPtrs) abstractPtrs)
if inheritanceDiff(exceptType, raisedType) <= 0: if inheritanceDiff(exceptType, raisedType) <= 0:
# mark exception as handled but keep it in B for # mark exception as handled but keep it in B for
# the getCurrentException() builtin: # the getCurrentException() builtin:
c.currentExceptionB = c.currentExceptionA c.currentExceptionB = c.currentExceptionA
c.currentExceptionA = nil c.currentExceptionA = nil
@ -349,14 +349,14 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool =
if dest.kind != rkFloat: if dest.kind != rkFloat:
myreset(dest); dest.kind = rkFloat myreset(dest); dest.kind = rkFloat
case skipTypes(srctyp, abstractRange).kind case skipTypes(srctyp, abstractRange).kind
of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyBool, tyChar: of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyBool, tyChar:
dest.floatVal = toFloat(src.intVal.int) dest.floatVal = toFloat(src.intVal.int)
else: else:
dest.floatVal = src.floatVal dest.floatVal = src.floatVal
else: else:
asgnComplex(dest, src) asgnComplex(dest, src)
proc compile(c: PCtx, s: PSym): int = proc compile(c: PCtx, s: PSym): int =
result = vmgen.genProc(c, s) result = vmgen.genProc(c, s)
when debugEchoCode: c.echoCode result when debugEchoCode: c.echoCode result
#c.echoCode #c.echoCode
@ -396,10 +396,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
pc = tos.comesFrom pc = tos.comesFrom
tos = tos.next tos = tos.next
let retVal = regs[0] let retVal = regs[0]
if tos.isNil: if tos.isNil:
#echo "RET ", retVal.rendertree #echo "RET ", retVal.rendertree
return retVal return retVal
move(regs, tos.slots) move(regs, tos.slots)
assert c.code[pc].opcode in {opcIndCall, opcIndCallAsgn} assert c.code[pc].opcode in {opcIndCall, opcIndCallAsgn}
if c.code[pc].opcode == opcIndCallAsgn: if c.code[pc].opcode == opcIndCallAsgn:
@ -653,7 +653,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcSubu: of opcSubu:
decodeBC(rkInt) decodeBC(rkInt)
regs[ra].intVal = regs[rb].intVal -% regs[rc].intVal regs[ra].intVal = regs[rb].intVal -% regs[rc].intVal
of opcMulu: of opcMulu:
decodeBC(rkInt) decodeBC(rkInt)
regs[ra].intVal = regs[rb].intVal *% regs[rc].intVal regs[ra].intVal = regs[rb].intVal *% regs[rc].intVal
of opcDivu: of opcDivu:
@ -726,7 +726,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcLeSet: of opcLeSet:
decodeBC(rkInt) decodeBC(rkInt)
regs[ra].intVal = ord(containsSets(regs[rb].node, regs[rc].node)) regs[ra].intVal = ord(containsSets(regs[rb].node, regs[rc].node))
of opcEqSet: of opcEqSet:
decodeBC(rkInt) decodeBC(rkInt)
regs[ra].intVal = ord(equalSets(regs[rb].node, regs[rc].node)) regs[ra].intVal = ord(equalSets(regs[rb].node, regs[rc].node))
of opcLtSet: of opcLtSet:
@ -737,9 +737,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcMulSet: of opcMulSet:
decodeBC(rkNode) decodeBC(rkNode)
createSet(regs[ra]) createSet(regs[ra])
move(regs[ra].node.sons, move(regs[ra].node.sons,
nimsets.intersectSets(regs[rb].node, regs[rc].node).sons) nimsets.intersectSets(regs[rb].node, regs[rc].node).sons)
of opcPlusSet: of opcPlusSet:
decodeBC(rkNode) decodeBC(rkNode)
createSet(regs[ra]) createSet(regs[ra])
move(regs[ra].node.sons, move(regs[ra].node.sons,
@ -753,7 +753,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
decodeBC(rkNode) decodeBC(rkNode)
createSet(regs[ra]) createSet(regs[ra])
move(regs[ra].node.sons, move(regs[ra].node.sons,
nimsets.symdiffSets(regs[rb].node, regs[rc].node).sons) nimsets.symdiffSets(regs[rb].node, regs[rc].node).sons)
of opcConcatStr: of opcConcatStr:
decodeBC(rkNode) decodeBC(rkNode)
createStr regs[ra] createStr regs[ra]
@ -793,7 +793,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
assert c.code[pc].opcode == opcSubStr assert c.code[pc].opcode == opcSubStr
let rd = c.code[pc].regA let rd = c.code[pc].regA
createStr regs[ra] createStr regs[ra]
regs[ra].node.strVal = substr(regs[rb].node.strVal, regs[ra].node.strVal = substr(regs[rb].node.strVal,
regs[rc].intVal.int, regs[rd].intVal.int) regs[rc].intVal.int, regs[rd].intVal.int)
of opcParseFloat: of opcParseFloat:
decodeBC(rkInt) decodeBC(rkInt)
@ -896,12 +896,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
# we know the next instruction is a 'fjmp': # we know the next instruction is a 'fjmp':
let branch = c.constants[instr.regBx-wordExcess] let branch = c.constants[instr.regBx-wordExcess]
var cond = false var cond = false
for j in countup(0, sonsLen(branch) - 2): for j in countup(0, sonsLen(branch) - 2):
if overlap(regs[ra].regToNode, branch.sons[j]): if overlap(regs[ra].regToNode, branch.sons[j]):
cond = true cond = true
break break
assert c.code[pc+1].opcode == opcFJmp assert c.code[pc+1].opcode == opcFJmp
inc pc inc pc
# we skip this instruction so that the final 'inc(pc)' skips # we skip this instruction so that the final 'inc(pc)' skips
# the following jump # the following jump
if not cond: if not cond:
@ -1273,7 +1273,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcNSetIntVal: of opcNSetIntVal:
decodeB(rkNode) decodeB(rkNode)
var dest = regs[ra].node var dest = regs[ra].node
if dest.kind in {nkCharLit..nkInt64Lit} and if dest.kind in {nkCharLit..nkInt64Lit} and
regs[rb].kind in {rkInt}: regs[rb].kind in {rkInt}:
dest.intVal = regs[rb].intVal dest.intVal = regs[rb].intVal
else: else:
@ -1281,24 +1281,24 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcNSetFloatVal: of opcNSetFloatVal:
decodeB(rkNode) decodeB(rkNode)
var dest = regs[ra].node var dest = regs[ra].node
if dest.kind in {nkFloatLit..nkFloat64Lit} and if dest.kind in {nkFloatLit..nkFloat64Lit} and
regs[rb].kind in {rkFloat}: regs[rb].kind in {rkFloat}:
dest.floatVal = regs[rb].floatVal dest.floatVal = regs[rb].floatVal
else: else:
stackTrace(c, tos, pc, errFieldXNotFound, "floatVal") stackTrace(c, tos, pc, errFieldXNotFound, "floatVal")
of opcNSetSymbol: of opcNSetSymbol:
decodeB(rkNode) decodeB(rkNode)
var dest = regs[ra].node var dest = regs[ra].node
if dest.kind == nkSym and regs[rb].node.kind == nkSym: if dest.kind == nkSym and regs[rb].node.kind == nkSym:
dest.sym = regs[rb].node.sym dest.sym = regs[rb].node.sym
else: else:
stackTrace(c, tos, pc, errFieldXNotFound, "symbol") stackTrace(c, tos, pc, errFieldXNotFound, "symbol")
of opcNSetIdent: of opcNSetIdent:
decodeB(rkNode) decodeB(rkNode)
var dest = regs[ra].node var dest = regs[ra].node
if dest.kind == nkIdent and regs[rb].node.kind == nkIdent: if dest.kind == nkIdent and regs[rb].node.kind == nkIdent:
dest.ident = regs[rb].node.ident dest.ident = regs[rb].node.ident
else: else:
stackTrace(c, tos, pc, errFieldXNotFound, "ident") stackTrace(c, tos, pc, errFieldXNotFound, "ident")
of opcNSetType: of opcNSetType:
decodeB(rkNode) decodeB(rkNode)
@ -1309,7 +1309,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcNSetStrVal: of opcNSetStrVal:
decodeB(rkNode) decodeB(rkNode)
var dest = regs[ra].node var dest = regs[ra].node
if dest.kind in {nkStrLit..nkTripleStrLit} and if dest.kind in {nkStrLit..nkTripleStrLit} and
regs[rb].kind in {rkNode}: regs[rb].kind in {rkNode}:
dest.strVal = regs[rb].node.strVal dest.strVal = regs[rb].node.strVal
else: else:
@ -1435,8 +1435,9 @@ proc evalConstExprAux(module, prc: PSym, n: PNode, mode: TEvalMode): PNode =
newSeq(tos.slots, c.prc.maxSlots) newSeq(tos.slots, c.prc.maxSlots)
#for i in 0 .. <c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty) #for i in 0 .. <c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty)
result = rawExecute(c, start, tos).regToNode result = rawExecute(c, start, tos).regToNode
if result.info.line < 0: result.info = n.info
proc evalConstExpr*(module: PSym, e: PNode): PNode = proc evalConstExpr*(module: PSym, e: PNode): PNode =
result = evalConstExprAux(module, nil, e, emConst) result = evalConstExprAux(module, nil, e, emConst)
proc evalStaticExpr*(module: PSym, e: PNode, prc: PSym): PNode = proc evalStaticExpr*(module: PSym, e: PNode, prc: PSym): PNode =
@ -1496,6 +1497,7 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
# temporary storage: # temporary storage:
#for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty) #for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty)
result = rawExecute(c, start, tos).regToNode result = rawExecute(c, start, tos).regToNode
if result.info.line < 0: result.info = n.info
if cyclicTree(result): globalError(n.info, errCyclicTree) if cyclicTree(result): globalError(n.info, errCyclicTree)
dec(evalMacroCounter) dec(evalMacroCounter)
c.callsite = nil c.callsite = nil

View file

@ -108,7 +108,7 @@ path="$lib/pure/unidecode"
gcc.options.always = "-w" gcc.options.always = "-w"
gcc.cpp.options.always = "-w -fpermissive" gcc.cpp.options.always = "-w -fpermissive"
@else: @else:
gcc.options.always = "-w" gcc.options.always = "-w"
gcc.cpp.options.always = "-w -fpermissive" gcc.cpp.options.always = "-w -fpermissive"
@end @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.linker = "/DEBUG /Zi /Fd\"$projectName.pdb\" /F33554432" # set the stack size to 8 MB
vcc.options.debug = "/Zi /Fd\"$projectName.pdb\"" vcc.options.debug = "/Zi /Fd\"$projectName.pdb\""
vcc.options.always = "/nologo" vcc.options.always = "/nologo"
vcc.options.speed = "/Ox /arch:SSE2" vcc.options.speed = "/O2 /arch:SSE2"
vcc.options.size = "/O1" vcc.options.size = "/O1"
# Configuration for the Digital Mars C/C++ compiler: # Configuration for the Digital Mars C/C++ compiler:

View file

@ -175,6 +175,9 @@ Generic Operating System Services
This module implements the ability to monitor a directory/file for changes This module implements the ability to monitor a directory/file for changes
using Posix's inotify API. using Posix's inotify API.
* `asyncfile <asyncfile.html>`_
This module implements asynchronous file reading and writing using
``asyncdispatch``.
Math libraries Math libraries
-------------- --------------

View file

@ -351,7 +351,7 @@ dispatch.
.. code-block:: nim .. code-block:: nim
type type
Expression = object ## abstract base class for an expression Expression = object of RootObj ## abstract base class for an expression
Literal = object of Expression Literal = object of Expression
x: int x: int
PlusExpr = object of Expression PlusExpr = object of Expression
@ -387,7 +387,7 @@ dispatching:
.. code-block:: nim .. code-block:: nim
type type
Thing = object Thing = object of RootObj
Unit = object of Thing Unit = object of Thing
x: int x: int

View file

@ -347,7 +347,7 @@ proc temp(args: string) =
if args.len > 0: exec(finalDest & " " & args) if args.len > 0: exec(finalDest & " " & args)
proc showHelp() = proc showHelp() =
quit(HelpText % [VersionAsString & repeatChar(44-len(VersionAsString)), quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)),
CompileDate, CompileTime], QuitSuccess) CompileDate, CompileTime], QuitSuccess)
var op = initOptParser() var op = initOptParser()

View file

@ -60,7 +60,7 @@ type
nnkStmtListType, nnkBlockType, nnkStmtListType, nnkBlockType,
nnkWith, nnkWithout, nnkWith, nnkWithout,
nnkTypeOfExpr, nnkObjectTy, nnkTypeOfExpr, nnkObjectTy,
nnkTupleTy, nnkTypeClassTy, nnkStaticTy, nnkTupleTy, nnkTupleClassTy, nnkTypeClassTy, nnkStaticTy,
nnkRecList, nnkRecCase, nnkRecWhen, nnkRecList, nnkRecCase, nnkRecWhen,
nnkRefTy, nnkPtrTy, nnkVarTy, nnkRefTy, nnkPtrTy, nnkVarTy,
nnkConstTy, nnkMutableTy, nnkConstTy, nnkMutableTy,
@ -343,7 +343,7 @@ proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =
## checks that `n` is of kind `k`. If this is not the case, ## checks that `n` is of kind `k`. If this is not the case,
## compilation aborts with an error message. This is useful for writing ## compilation aborts with an error message. This is useful for writing
## macros that check the AST that is passed to them. ## macros that check the AST that is passed to them.
if n.kind != k: error("macro expects a node of kind: " & $k) if n.kind != k: error("Expected a node of kind " & $k & ", got " & $n.kind)
proc expectMinLen*(n: PNimrodNode, min: int) {.compileTime.} = proc expectMinLen*(n: PNimrodNode, min: int) {.compileTime.} =
## checks that `n` has at least `min` children. If this is not the case, ## checks that `n` has at least `min` children. If this is not the case,
@ -581,10 +581,8 @@ const
CallNodes* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand, CallNodes* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
nnkCallStrLit, nnkHiddenCallConv} nnkCallStrLit, nnkHiddenCallConv}
from strutils import cmpIgnoreStyle, format
proc expectKind*(n: PNimrodNode; k: set[TNimrodNodeKind]) {.compileTime.} = proc expectKind*(n: PNimrodNode; k: set[TNimrodNodeKind]) {.compileTime.} =
assert n.kind in k, "Expected one of $1, got $2".format(k, n.kind) assert n.kind in k, "Expected one of " & $k & ", got " & $n.kind
proc newProc*(name = newEmptyNode(); params: openArray[PNimrodNode] = [newEmptyNode()]; proc newProc*(name = newEmptyNode(); params: openArray[PNimrodNode] = [newEmptyNode()];
body: PNimrodNode = newStmtList(), procType = nnkProcDef): PNimrodNode {.compileTime.} = body: PNimrodNode = newStmtList(), procType = nnkProcDef): PNimrodNode {.compileTime.} =
@ -654,7 +652,7 @@ proc `pragma=`*(someProc: PNimrodNode; val: PNimrodNode){.compileTime.}=
template badNodeKind(k; f): stmt{.immediate.} = template badNodeKind(k; f): stmt{.immediate.} =
assert false, "Invalid node kind $# for macros.`$2`".format(k, f) assert false, "Invalid node kind " & $k & " for macros.`" & $f & "`"
proc body*(someProc: PNimrodNode): PNimrodNode {.compileTime.} = proc body*(someProc: PNimrodNode): PNimrodNode {.compileTime.} =
case someProc.kind: case someProc.kind:
@ -776,6 +774,22 @@ proc copy*(node: PNimrodNode): PNimrodNode {.compileTime.} =
## An alias for copyNimTree(). ## An alias for copyNimTree().
return node.copyNimTree() return node.copyNimTree()
proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} =
proc toLower(c: char): char {.inline.} =
if c in {'A'..'Z'}: result = chr(ord(c) + (ord('a') - ord('A')))
else: result = c
var i = 0
var j = 0
while true:
while a[i] == '_': inc(i)
while b[j] == '_': inc(j) # BUGFIX: typo
var aa = toLower(a[i])
var bb = toLower(b[j])
result = ord(aa) - ord(bb)
if result != 0 or aa == '\0': break
inc(i)
inc(j)
proc eqIdent* (a, b: string): bool = cmpIgnoreStyle(a, b) == 0 proc eqIdent* (a, b: string): bool = cmpIgnoreStyle(a, b) == 0
## Check if two idents are identical. ## Check if two idents are identical.

View file

@ -66,9 +66,9 @@ type
ppointer = ptr pointer ppointer = ptr pointer
pbyteArray = ptr array[0.. 0xffff, int8] pbyteArray = ptr array[0.. 0xffff, int8]
TGenSeq = object TGenericSeq {.importc.} = object
len, space: int len, space: int
PGenSeq = ptr TGenSeq PGenSeq = ptr TGenericSeq
const const
GenericSeqSize = (2 * sizeof(int)) GenericSeqSize = (2 * sizeof(int))

View file

@ -8,16 +8,16 @@
# #
## This module contains code for reading from `stdin`:idx:. On UNIX the GNU ## 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`` ## (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. ## wanted functionality.
{.deadCodeElim: on.} {.deadCodeElim: on.}
when defined(Windows): when defined(Windows):
proc readLineFromStdin*(prompt: string): TaintedString {. proc readLineFromStdin*(prompt: string): TaintedString {.
tags: [ReadIOEffect, WriteIOEffect].} = tags: [ReadIOEffect, WriteIOEffect].} =
## Reads a line from stdin. ## Reads a line from stdin.
stdout.write(prompt) stdout.write(prompt)
result = readLine(stdin) result = readLine(stdin)
@ -33,27 +33,71 @@ when defined(Windows):
stdout.write(prompt) stdout.write(prompt)
result = readLine(stdin, line) 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): proc readPasswordFromStdin*(prompt: string, password: var TaintedString):
bool {.tags: [ReadIOEffect, WriteIOEffect].} = bool {.tags: [ReadIOEffect, WriteIOEffect].} =
## Reads a `password` from stdin without printing it. `password` must not ## Reads a `password` from stdin without printing it. `password` must not
## be ``nil``! Returns ``false`` if the end of the file has been reached, ## be ``nil``! Returns ``false`` if the end of the file has been reached,
## ``true`` otherwise. ## ``true`` otherwise.
proc getch(): cint {.header: "<conio.h>", importc: "_getch".}
password.setLen(0) password.setLen(0)
var c: char
stdout.write(prompt) stdout.write(prompt)
while true: while true:
c = getch().char let c = getch()
case c case c.char
of '\r', chr(0xA): of '\r', chr(0xA):
break break
of '\b': 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: else:
password.add(c) password.add(toUTF8(c.Rune))
stdout.write "\n" stdout.write "\n"
# TODO: How to detect EOF on Windows?
else: else:
import readline, history, termios, unsigned import readline, history, termios, unsigned

View file

@ -66,7 +66,7 @@ proc rawCompile(pattern: string, flags: cint): PPcre =
offset: cint offset: cint
result = pcre.compile(pattern, flags, addr(msg), addr(offset), nil) result = pcre.compile(pattern, flags, addr(msg), addr(offset), nil)
if result == nil: if result == nil:
raiseInvalidRegex($msg & "\n" & pattern & "\n" & repeatChar(offset) & "^\n") raiseInvalidRegex($msg & "\n" & pattern & "\n" & spaces(offset) & "^\n")
proc finalizeRegEx(x: Regex) = proc finalizeRegEx(x: Regex) =
# XXX This is a hack, but PCRE does not export its "free" function properly. # XXX This is a hack, but PCRE does not export its "free" function properly.
@ -291,7 +291,7 @@ proc replace*(s: string, sub: Regex, by = ""): string =
## accessed in `by`. Examples: ## accessed in `by`. Examples:
## ##
## .. code-block:: nim ## .. code-block:: nim
## "var1=key; var2=key2".replace(re"(\w+)'='(\w+)") ## "var1=key; var2=key2".replace(re"(\w+)=(\w+)")
## ##
## Results in: ## Results in:
## ##
@ -313,7 +313,7 @@ proc replacef*(s: string, sub: Regex, by: string): string =
## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples: ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
## ##
## .. code-block:: nim ## .. code-block:: nim
## "var1=key; var2=key2".replacef(re"(\w+)'='(\w+)", "$1<-$2$2") ## "var1=key; var2=key2".replacef(re"(\w+)=(\w+)", "$1<-$2$2")
## ##
## Results in: ## Results in:
## ##

View file

@ -67,7 +67,7 @@ __clang__
#endif #endif
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
# define NIM_THREADVAR __declspec(thread) # define NIM_THREADVAR __declspec(thread)
#else #else
# define NIM_THREADVAR __thread # define NIM_THREADVAR __thread
#endif #endif
@ -103,7 +103,11 @@ __clang__
# define N_FASTCALL_PTR(rettype, name) rettype (__fastcall *name) # define N_FASTCALL_PTR(rettype, name) rettype (__fastcall *name)
# define N_SAFECALL_PTR(rettype, name) rettype (__safecall *name) # define N_SAFECALL_PTR(rettype, name) rettype (__safecall *name)
# define N_LIB_EXPORT extern __declspec(dllexport) # ifdef __cplusplus
# define N_LIB_EXPORT extern "C" __declspec(dllexport)
# else
# define N_LIB_EXPORT extern __declspec(dllexport)
# endif
# define N_LIB_IMPORT extern __declspec(dllimport) # define N_LIB_IMPORT extern __declspec(dllimport)
#else #else
# define N_CDECL(rettype, name) rettype name # define N_CDECL(rettype, name) rettype name
@ -118,7 +122,11 @@ __clang__
# define N_FASTCALL_PTR(rettype, name) rettype (*name) # define N_FASTCALL_PTR(rettype, name) rettype (*name)
# define N_SAFECALL_PTR(rettype, name) rettype (*name) # define N_SAFECALL_PTR(rettype, name) rettype (*name)
# define N_LIB_EXPORT extern # ifdef __cplusplus
# define N_LIB_EXPORT extern "C"
# else
# define N_LIB_EXPORT extern
# endif
# define N_LIB_IMPORT extern # define N_LIB_IMPORT extern
#endif #endif
@ -345,7 +353,7 @@ struct TFrame {
#define nimln(n, file) \ #define nimln(n, file) \
F.line = n; F.filename = file; F.line = n; F.filename = file;
#define NIM_POSIX_INIT __attribute__((constructor)) #define NIM_POSIX_INIT __attribute__((constructor))
#if defined(_MSCVER) && defined(__i386__) #if defined(_MSCVER) && defined(__i386__)
__declspec(naked) int __fastcall NimXadd(volatile int* pNum, int val) { __declspec(naked) int __fastcall NimXadd(volatile int* pNum, int val) {
@ -380,7 +388,7 @@ static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); }
#endif #endif
/* Test to see if Nim and the C compiler agree on the size of a pointer. /* Test to see if Nim and the C compiler agree on the size of a pointer.
On disagreement, your C compiler will say something like: On disagreement, your C compiler will say something like:
"error: 'assert_numbits' declared as an array with a negative size" */ "error: 'assert_numbits' declared as an array with a negative size" */
typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1]; typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1];
#endif #endif
@ -398,4 +406,6 @@ typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(
# include <sys/types.h> # include <sys/types.h>
# include <types/vxWind.h> # include <types/vxWind.h>
# include <tool/gnu/toolMacros.h> # include <tool/gnu/toolMacros.h>
#elif defined(__FreeBSD__)
# include <sys/types.h>
#endif #endif

View file

@ -189,7 +189,7 @@ proc getIndent(L: var TLexer, tok: var TToken) =
tok.line = L.line tok.line = L.line
L.col = tok.ival L.col = tok.ival
tok.ival = max(tok.ival - L.baseIndent, 0) tok.ival = max(tok.ival - L.baseIndent, 0)
tok.symbol = "\n" & repeatChar(tok.ival) tok.symbol = "\n" & spaces(tok.ival)
proc rawGetTok(L: var TLexer, tok: var TToken) = proc rawGetTok(L: var TLexer, tok: var TToken) =
tok.symbol = "" tok.symbol = ""
@ -963,7 +963,7 @@ proc parseLiteralBlock(p: var TRstParser): PRstNode =
break break
else: else:
add(n.text, "\n") add(n.text, "\n")
add(n.text, repeatChar(p.tok[p.idx].ival - indent)) add(n.text, spaces(p.tok[p.idx].ival - indent))
inc(p.idx) inc(p.idx)
else: else:
add(n.text, p.tok[p.idx].symbol) add(n.text, p.tok[p.idx].symbol)

View file

@ -110,7 +110,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
const const
lvlToChar: array[0..8, char] = ['!', '=', '-', '~', '`', '<', '*', '|', '+'] lvlToChar: array[0..8, char] = ['!', '=', '-', '~', '`', '<', '*', '|', '+']
if n == nil: return if n == nil: return
var ind = repeatChar(d.indent) var ind = spaces(d.indent)
case n.kind case n.kind
of rnInner: of rnInner:
renderRstSons(d, n, result) renderRstSons(d, n, result)
@ -124,7 +124,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
result.add("\n") result.add("\n")
result.add(ind) result.add(ind)
result.add repeatChar(headlineLen, lvlToChar[n.level]) result.add repeat(lvlToChar[n.level], headlineLen)
of rnOverline: of rnOverline:
result.add("\n") result.add("\n")
result.add(ind) result.add(ind)
@ -132,7 +132,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
var headline = "" var headline = ""
renderRstSons(d, n, headline) renderRstSons(d, n, headline)
let lvl = repeatChar(headline.len - d.indent, lvlToChar[n.level]) let lvl = repeat(lvlToChar[n.level], headline.len - d.indent)
result.add(lvl) result.add(lvl)
result.add("\n") result.add("\n")
result.add(headline) result.add(headline)
@ -143,7 +143,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
of rnTransition: of rnTransition:
result.add("\n\n") result.add("\n\n")
result.add(ind) result.add(ind)
result.add repeatChar(78-d.indent, '-') result.add repeat('-', 78-d.indent)
result.add("\n\n") result.add("\n\n")
of rnParagraph: of rnParagraph:
result.add("\n\n") result.add("\n\n")
@ -196,7 +196,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
result.add ':' result.add ':'
result.add tmp result.add tmp
result.add ':' result.add ':'
result.add repeatChar(L - tmp.len - 2) result.add spaces(L - tmp.len - 2)
renderRstToRst(d, n.sons[1], result) renderRstToRst(d, n.sons[1], result)
dec(d.indent, L) dec(d.indent, L)

View file

@ -461,9 +461,9 @@ proc indentToLevel(level: var int, newLevel: int): string =
if level == newLevel: if level == newLevel:
return return
if newLevel > level: if newLevel > level:
result = repeatStr(newLevel - level, "<ul>") result = repeat("<ul>", newLevel - level)
else: else:
result = repeatStr(level - newLevel, "</ul>") result = repeat("</ul>", level - newLevel)
level = newLevel level = newLevel
proc generateDocumentationTOC(entries: seq[TIndexEntry]): string = proc generateDocumentationTOC(entries: seq[TIndexEntry]): string =
@ -701,7 +701,7 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) =
# Generate index entry using spaces to indicate TOC level for the output HTML. # Generate index entry using spaces to indicate TOC level for the output HTML.
assert n.level >= 0 assert n.level >= 0
setIndexTerm(d, refname, tmp.stripTOCHTML, setIndexTerm(d, refname, tmp.stripTOCHTML,
repeatChar(max(0, n.level), ' ') & tmp) spaces(max(0, n.level)) & tmp)
proc renderOverline(d: PDoc, n: PRstNode, result: var string) = proc renderOverline(d: PDoc, n: PRstNode, result: var string) =
if d.meta[metaTitle].len == 0: if d.meta[metaTitle].len == 0:

View file

@ -11,15 +11,15 @@
type type
SortOrder* = enum ## sort order SortOrder* = enum ## sort order
Descending, Ascending Descending, Ascending
{.deprecated: [TSortOrder: SortOrder].} {.deprecated: [TSortOrder: SortOrder].}
proc `*`*(x: int, order: SortOrder): int {.inline.} = proc `*`*(x: int, order: SortOrder): int {.inline.} =
## flips `x` if ``order == Descending``; ## flips `x` if ``order == Descending``;
## if ``order == Ascending`` then `x` is returned. ## 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*. ## *less than*, ``== 0`` for *equal*, ``> 0`` for *greater than*.
var y = order.ord - 1 var y = order.ord - 1
result = (x xor y) - y result = (x xor y) - y
@ -73,14 +73,15 @@ const
onlySafeCode = true onlySafeCode = true
proc lowerBound*[T](a: openArray[T], key: T, cmp: proc(x,y: T): int {.closure.}): int = 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 ## 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)) ## words if you have a sorted sequence and you call
## the sequence will still be sorted ## 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:: ## example::
## ##
## var arr = @[1,2,3,5,6,7,8,9] ## 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 count = step
proc lowerBound*[T](a: openArray[T], key: T): int = lowerBound(a, key, cmp[T]) 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) = cmp: proc (x, y: T): int {.closure.}, order: SortOrder) =
template `<-` (a, b: expr) = template `<-` (a, b: expr) =
when false: when false:
a = b a = b
elif onlySafeCode: 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], proc sort*[T](a: var openArray[T],
cmp: proc (x, y: T): int {.closure.}, cmp: proc (x, y: T): int {.closure.},
order = SortOrder.Ascending) = 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 worst case is guaranteed to be O(n log n).
## The current implementation uses an iterative ## 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 ## length ``a.len div 2``. Currently Nim does not support a
## sensible default argument for ``cmp``, so you have to provide one ## sensible default argument for ``cmp``, so you have to provide one
## of your own. However, the ``system.cmp`` procs can be used: ## of your own. However, the ``system.cmp`` procs can be used:
@ -187,6 +188,35 @@ proc sort*[T](a: var openArray[T],
dec(m, s*2) dec(m, s*2)
s = s*2 s = s*2
proc sorted*[T](a: openArray[T], cmp: proc(x, y: T): int {.closure.},
order = SortOrder.Ascending): seq[T] =
## returns `a` sorted by `cmp` in the specified `order`.
result = newSeq[T](a.len)
for i in 0 .. a.high:
result[i] = a[i]
sort(result, cmp, order)
template sortByIt*(seq1, op: expr): expr =
## Convenience template around the ``sorted`` proc to reduce typing.
##
## The template injects the ``it`` variable which you can use directly in an
## expression. Example:
##
## .. code-block:: nim
##
## var users: seq[tuple[id: int, name: string]] =
## @[(0, "Smith"), (1, "Pratt"), (2, "Sparrow")]
##
## echo users.sortByIt(it.name)
##
var result {.gensym.} = sorted(seq1, proc(x, y: type(seq1[0])): int =
var it {.inject.} = x
let a = op
it = y
let b = op
result = cmp(a, b))
result
proc product*[T](x: openArray[seq[T]]): seq[seq[T]] = proc product*[T](x: openArray[seq[T]]): seq[seq[T]] =
## produces the Cartesian product of the array. Warning: complexity ## produces the Cartesian product of the array. Warning: complexity
## may explode. ## may explode.

View file

@ -121,7 +121,7 @@ export Port, SocketFlag
## ##
## Limitations/Bugs ## Limitations/Bugs
## ---------------- ## ----------------
## ##
## * ``except`` statement (without `try`) does not work inside async procedures. ## * ``except`` statement (without `try`) does not work inside async procedures.
## * The effect system (``raises: []``) does not work with async procedures. ## * The effect system (``raises: []``) does not work with async procedures.
## * Can't await in a ``except`` body ## * 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: if p.handles.len == 0 and p.timers.len == 0:
raise newException(ValueError, raise newException(ValueError,
"No handles or timers registered in dispatcher.") "No handles or timers registered in dispatcher.")
let llTimeout = let llTimeout =
if timeout == -1: winlean.INFINITE if timeout == -1: winlean.INFINITE
else: timeout.int32 else: timeout.int32
@ -436,12 +436,12 @@ when defined(windows) or defined(nimdoc):
if not initPointer(dummySock, getAcceptExSockAddrsPtr, WSAID_GETACCEPTEXSOCKADDRS): if not initPointer(dummySock, getAcceptExSockAddrsPtr, WSAID_GETACCEPTEXSOCKADDRS):
raiseOSError(osLastError()) raiseOSError(osLastError())
proc connectEx(s: SocketHandle, name: ptr SockAddr, namelen: cint, proc connectEx(s: SocketHandle, name: ptr SockAddr, namelen: cint,
lpSendBuffer: pointer, dwSendDataLength: Dword, lpSendBuffer: pointer, dwSendDataLength: Dword,
lpdwBytesSent: PDword, lpOverlapped: POVERLAPPED): bool = lpdwBytesSent: PDword, lpOverlapped: POVERLAPPED): bool =
if connectExPtr.isNil: raise newException(ValueError, "Need to initialise ConnectEx().") if connectExPtr.isNil: raise newException(ValueError, "Need to initialise ConnectEx().")
let fun = let fun =
cast[proc (s: SocketHandle, name: ptr SockAddr, namelen: cint, cast[proc (s: SocketHandle, name: ptr SockAddr, namelen: cint,
lpSendBuffer: pointer, dwSendDataLength: Dword, lpSendBuffer: pointer, dwSendDataLength: Dword,
lpdwBytesSent: PDword, lpOverlapped: POVERLAPPED): bool {.stdcall,gcsafe.}](connectExPtr) lpdwBytesSent: PDword, lpOverlapped: POVERLAPPED): bool {.stdcall,gcsafe.}](connectExPtr)
@ -475,7 +475,7 @@ when defined(windows) or defined(nimdoc):
dwRemoteAddressLength: Dword, LocalSockaddr: ptr ptr SockAddr, dwRemoteAddressLength: Dword, LocalSockaddr: ptr ptr SockAddr,
LocalSockaddrLength: LPInt, RemoteSockaddr: ptr ptr SockAddr, LocalSockaddrLength: LPInt, RemoteSockaddr: ptr ptr SockAddr,
RemoteSockaddrLength: LPInt) {.stdcall,gcsafe.}](getAcceptExSockAddrsPtr) RemoteSockaddrLength: LPInt) {.stdcall,gcsafe.}](getAcceptExSockAddrsPtr)
fun(lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength, fun(lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength,
dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength, dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength,
RemoteSockaddr, RemoteSockaddrLength) RemoteSockaddr, RemoteSockaddrLength)
@ -514,7 +514,7 @@ when defined(windows) or defined(nimdoc):
else: else:
retFuture.fail(newException(OSError, osErrorMsg(errcode))) retFuture.fail(newException(OSError, osErrorMsg(errcode)))
) )
var ret = connectEx(socket.SocketHandle, it.ai_addr, var ret = connectEx(socket.SocketHandle, it.ai_addr,
sizeof(Sockaddr_in).cint, nil, 0, nil, sizeof(Sockaddr_in).cint, nil, 0, nil,
cast[POVERLAPPED](ol)) cast[POVERLAPPED](ol))
@ -565,7 +565,7 @@ when defined(windows) or defined(nimdoc):
var dataBuf: TWSABuf var dataBuf: TWSABuf
dataBuf.buf = cast[cstring](alloc0(size)) dataBuf.buf = cast[cstring](alloc0(size))
dataBuf.len = size dataBuf.len = size
var bytesReceived: Dword var bytesReceived: Dword
var flagsio = flags.toOSFlags().Dword var flagsio = flags.toOSFlags().Dword
var ol = PCustomOverlapped() var ol = PCustomOverlapped()
@ -612,9 +612,9 @@ when defined(windows) or defined(nimdoc):
# the empty string (which signals a disconnection) when there is # the empty string (which signals a disconnection) when there is
# nothing left to read. # nothing left to read.
retFuture.complete("") retFuture.complete("")
# TODO: "For message-oriented sockets, where a zero byte message is often # TODO: "For message-oriented sockets, where a zero byte message is often
# allowable, a failure with an error code of WSAEDISCON is used to # allowable, a failure with an error code of WSAEDISCON is used to
# indicate graceful closure." # indicate graceful closure."
# ~ http://msdn.microsoft.com/en-us/library/ms741688%28v=vs.85%29.aspx # ~ http://msdn.microsoft.com/en-us/library/ms741688%28v=vs.85%29.aspx
else: else:
# Request to read completed immediately. # 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 # http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx
let ret = acceptEx(socket.SocketHandle, clientSock, addr lpOutputBuf[0], let ret = acceptEx(socket.SocketHandle, clientSock, addr lpOutputBuf[0],
dwReceiveDataLength, dwReceiveDataLength,
dwLocalAddressLength, dwLocalAddressLength,
dwRemoteAddressLength, dwRemoteAddressLength,
addr dwBytesReceived, cast[POVERLAPPED](ol)) addr dwBytesReceived, cast[POVERLAPPED](ol))
@ -803,7 +803,7 @@ else:
else: else:
from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK, from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK,
MSG_NOSIGNAL MSG_NOSIGNAL
type type
TAsyncFD* = distinct cint TAsyncFD* = distinct cint
TCallback = proc (fd: TAsyncFD): bool {.closure,gcsafe.} TCallback = proc (fd: TAsyncFD): bool {.closure,gcsafe.}
@ -849,7 +849,7 @@ else:
result = newRawSocket(domain, typ, protocol).TAsyncFD result = newRawSocket(domain, typ, protocol).TAsyncFD
result.SocketHandle.setBlocking(false) result.SocketHandle.setBlocking(false)
register(result) register(result)
proc closeSocket*(sock: TAsyncFD) = proc closeSocket*(sock: TAsyncFD) =
let disp = getGlobalDispatcher() let disp = getGlobalDispatcher()
sock.SocketHandle.close() sock.SocketHandle.close()
@ -864,14 +864,14 @@ else:
raise newException(ValueError, "File descriptor not registered.") raise newException(ValueError, "File descriptor not registered.")
p.selector[fd.SocketHandle].data.PData.readCBs.add(cb) p.selector[fd.SocketHandle].data.PData.readCBs.add(cb)
update(fd, p.selector[fd.SocketHandle].events + {EvRead}) update(fd, p.selector[fd.SocketHandle].events + {EvRead})
proc addWrite*(fd: TAsyncFD, cb: TCallback) = proc addWrite*(fd: TAsyncFD, cb: TCallback) =
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
if fd.SocketHandle notin p.selector: if fd.SocketHandle notin p.selector:
raise newException(ValueError, "File descriptor not registered.") raise newException(ValueError, "File descriptor not registered.")
p.selector[fd.SocketHandle].data.PData.writeCBs.add(cb) p.selector[fd.SocketHandle].data.PData.writeCBs.add(cb)
update(fd, p.selector[fd.SocketHandle].events + {EvWrite}) update(fd, p.selector[fd.SocketHandle].events + {EvWrite})
proc poll*(timeout = 500) = proc poll*(timeout = 500) =
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
for info in p.selector.select(timeout): for info in p.selector.select(timeout):
@ -892,7 +892,7 @@ else:
if not cb(data.fd): if not cb(data.fd):
# Callback wants to be called again. # Callback wants to be called again.
data.readCBs.add(cb) data.readCBs.add(cb)
if EvWrite in info.events: if EvWrite in info.events:
let currentCBs = data.writeCBs let currentCBs = data.writeCBs
data.writeCBs = @[] data.writeCBs = @[]
@ -900,7 +900,7 @@ else:
if not cb(data.fd): if not cb(data.fd):
# Callback wants to be called again. # Callback wants to be called again.
data.writeCBs.add(cb) data.writeCBs.add(cb)
if info.key in p.selector: if info.key in p.selector:
var newEvents: set[Event] var newEvents: set[Event]
if data.readCBs.len != 0: newEvents = {EvRead} if data.readCBs.len != 0: newEvents = {EvRead}
@ -913,16 +913,16 @@ else:
discard discard
processTimers(p) processTimers(p)
proc connect*(socket: TAsyncFD, address: string, port: Port, proc connect*(socket: TAsyncFD, address: string, port: Port,
af = AF_INET): Future[void] = af = AF_INET): Future[void] =
var retFuture = newFuture[void]("connect") var retFuture = newFuture[void]("connect")
proc cb(fd: TAsyncFD): bool = proc cb(fd: TAsyncFD): bool =
# We have connected. # We have connected.
retFuture.complete() retFuture.complete()
return true return true
var aiList = getAddrInfo(address, port, af) var aiList = getAddrInfo(address, port, af)
var success = false var success = false
var lastError: OSErrorCode var lastError: OSErrorCode
@ -952,7 +952,7 @@ else:
proc recv*(socket: TAsyncFD, size: int, proc recv*(socket: TAsyncFD, size: int,
flags = {SocketFlag.SafeDisconn}): Future[string] = flags = {SocketFlag.SafeDisconn}): Future[string] =
var retFuture = newFuture[string]("recv") var retFuture = newFuture[string]("recv")
var readBuffer = newString(size) var readBuffer = newString(size)
proc cb(sock: TAsyncFD): bool = proc cb(sock: TAsyncFD): bool =
@ -983,9 +983,9 @@ else:
proc send*(socket: TAsyncFD, data: string, proc send*(socket: TAsyncFD, data: string,
flags = {SocketFlag.SafeDisconn}): Future[void] = flags = {SocketFlag.SafeDisconn}): Future[void] =
var retFuture = newFuture[void]("send") var retFuture = newFuture[void]("send")
var written = 0 var written = 0
proc cb(sock: TAsyncFD): bool = proc cb(sock: TAsyncFD): bool =
result = true result = true
let netSize = data.len-written let netSize = data.len-written
@ -1222,7 +1222,7 @@ proc processBody(node, retFutureSym: PNimrodNode,
of nnkTryStmt: of nnkTryStmt:
# try: await x; except: ... # try: await x; except: ...
result = newNimNode(nnkStmtList, node) result = newNimNode(nnkStmtList, node)
template wrapInTry(n, tryBody: PNimrodNode) = template wrapInTry(n, tryBody: expr) =
var temp = n var temp = n
n[0] = tryBody n[0] = tryBody
tryBody = temp tryBody = temp
@ -1315,29 +1315,40 @@ macro async*(prc: stmt): stmt {.immediate.} =
if returnType.kind == nnkEmpty: newIdentNode("void") if returnType.kind == nnkEmpty: newIdentNode("void")
else: returnType[1] else: returnType[1]
outerProcBody.add( outerProcBody.add(
newVarStmt(retFutureSym, newVarStmt(retFutureSym,
newCall( newCall(
newNimNode(nnkBracketExpr, prc[6]).add( newNimNode(nnkBracketExpr, prc[6]).add(
newIdentNode(!"newFuture"), # TODO: Strange bug here? Remove the `!`. newIdentNode(!"newFuture"), # TODO: Strange bug here? Remove the `!`.
subRetType), subRetType),
newLit(prc[0].getName)))) # Get type from return type of this proc 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 # -> var result: T
# -> {.pop.}
# -> <proc_body> # -> <proc_body>
# -> complete(retFuture, result) # -> complete(retFuture, result)
var iteratorNameSym = genSym(nskIterator, $prc[0].getName & "Iter") var iteratorNameSym = genSym(nskIterator, $prc[0].getName & "Iter")
var procBody = prc[6].processBody(retFutureSym, subtypeIsVoid, nil) var procBody = prc[6].processBody(retFutureSym, subtypeIsVoid, nil)
if not subtypeIsVoid: 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 newIdentDefs(newIdentNode("result"), returnType[1]))) # -> var result: T
procBody.insert(2, newNimNode(nnkPragma).add(
newIdentNode("pop"))) # -> {.pop.})
procBody.add( procBody.add(
newCall(newIdentNode("complete"), newCall(newIdentNode("complete"),
retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result) retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result)
else: else:
# -> complete(retFuture) # -> complete(retFuture)
procBody.add(newCall(newIdentNode("complete"), retFutureSym)) procBody.add(newCall(newIdentNode("complete"), retFutureSym))
var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase")], var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase")],
procBody, nnkIteratorDef) procBody, nnkIteratorDef)
closureIterator[4] = newNimNode(nnkPragma, prc[6]).add(newIdentNode("closure")) closureIterator[4] = newNimNode(nnkPragma, prc[6]).add(newIdentNode("closure"))
@ -1351,7 +1362,7 @@ macro async*(prc: stmt): stmt {.immediate.} =
# -> return retFuture # -> return retFuture
outerProcBody.add newNimNode(nnkReturnStmt, prc[6][prc[6].len-1]).add(retFutureSym) outerProcBody.add newNimNode(nnkReturnStmt, prc[6][prc[6].len-1]).add(retFutureSym)
result = prc result = prc
# Remove the 'async' pragma. # 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 ## If a full line is read ``\r\L`` is not
## added to ``line``, however if solely ``\r\L`` is read then ``line`` ## added to ``line``, however if solely ``\r\L`` is read then ``line``
## will be set to it. ## will be set to it.
## ##
## If the socket is disconnected, ``line`` will be set to ``""``. ## If the socket is disconnected, ``line`` will be set to ``""``.
## ##
## If the socket is disconnected in the middle of a line (before ``\r\L`` ## 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 ## **Note**: This procedure is mostly used for testing. You likely want to
## use ``asyncnet.recvLine`` instead. ## use ``asyncnet.recvLine`` instead.
template addNLIfEmpty(): stmt = template addNLIfEmpty(): stmt =
if result.len == 0: if result.len == 0:
result.add("\c\L") result.add("\c\L")

View file

@ -7,7 +7,7 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## This module implements asynchronous file handling. ## This module implements asynchronous file reading and writing.
## ##
## .. code-block:: Nim ## .. code-block:: Nim
## import asyncfile, asyncdispatch, os ## import asyncfile, asyncdispatch, os
@ -24,17 +24,17 @@
import asyncdispatch, os import asyncdispatch, os
when defined(windows): when defined(windows) or defined(nimdoc):
import winlean import winlean
else: else:
import posix import posix
type type
AsyncFile = ref object AsyncFile* = ref object
fd: TAsyncFd fd: TAsyncFd
offset: int64 offset: int64
when defined(windows): when defined(windows) or defined(nimdoc):
proc getDesiredAccess(mode: FileMode): int32 = proc getDesiredAccess(mode: FileMode): int32 =
case mode case mode
of fmRead: of fmRead:
@ -70,7 +70,7 @@ else:
proc getFileSize(f: AsyncFile): int64 = proc getFileSize(f: AsyncFile): int64 =
## Retrieves the specified file's size. ## Retrieves the specified file's size.
when defined(windows): when defined(windows) or defined(nimdoc):
var high: DWord var high: DWord
let low = getFileSize(f.fd.THandle, addr high) let low = getFileSize(f.fd.THandle, addr high)
if low == INVALID_FILE_SIZE: 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 ## Opens a file specified by the path in ``filename`` using
## the specified ``mode`` asynchronously. ## the specified ``mode`` asynchronously.
new result new result
when defined(windows): when defined(windows) or defined(nimdoc):
let flags = FILE_FLAG_OVERLAPPED or FILE_ATTRIBUTE_NORMAL let flags = FILE_FLAG_OVERLAPPED or FILE_ATTRIBUTE_NORMAL
let desiredAccess = getDesiredAccess(mode) let desiredAccess = getDesiredAccess(mode)
let creationDisposition = getCreationDisposition(mode, filename) let creationDisposition = getCreationDisposition(mode, filename)
@ -120,7 +120,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
## returned. ## returned.
var retFuture = newFuture[string]("asyncfile.read") var retFuture = newFuture[string]("asyncfile.read")
when defined(windows): when defined(windows) or defined(nimdoc):
var buffer = alloc0(size) var buffer = alloc0(size)
var ol = PCustomOverlapped() 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 ## Sets the position of the file pointer that is used for read/write
## operations. The file's first byte has the index zero. ## operations. The file's first byte has the index zero.
f.offset = pos f.offset = pos
when not defined(windows): when not defined(windows) and not defined(nimdoc):
let ret = lseek(f.fd.cint, pos, SEEK_SET) let ret = lseek(f.fd.cint, pos, SEEK_SET)
if ret == -1: if ret == -1:
raiseOSError(osLastError()) raiseOSError(osLastError())
@ -245,7 +245,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
## specified file. ## specified file.
var retFuture = newFuture[void]("asyncfile.write") var retFuture = newFuture[void]("asyncfile.write")
var copy = data var copy = data
when defined(windows): when defined(windows) or defined(nimdoc):
var buffer = alloc0(data.len) var buffer = alloc0(data.len)
copyMem(buffer, addr copy[0], 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) = proc close*(f: AsyncFile) =
## Closes the file specified. ## Closes the file specified.
when defined(windows): when defined(windows) or defined(nimdoc):
if not closeHandle(f.fd.THandle).bool: if not closeHandle(f.fd.THandle).bool:
raiseOSError(osLastError()) raiseOSError(osLastError())
else: else:

View file

@ -47,6 +47,24 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
result[i] = itm result[i] = itm
inc(i) 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] = proc deduplicate*[T](seq1: seq[T]): seq[T] =
## Returns a new sequence without duplicates. ## 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) newSeq(result, m)
for i in 0 .. m-1: result[i] = (seq1[i], seq2[i]) 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. ## Splits and distributes a sequence `s` into `num` sub sequences.
## ##
## Returns a sequence of `num` sequences. For some input values this is the ## 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)[0] == @[1, 2]
## assert numbers.distribute(6)[5] == @[7] ## assert numbers.distribute(6)[5] == @[7]
assert(not s.isNil, "`s` can't be nil") assert(not s.isNil, "`s` can't be nil")
assert(num > 0, "`num` has to be greater than zero")
if num < 2: if num < 2:
result = @[s] result = @[s]
return 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. # Create the result and calculate the stride size and the remainder if any.
result = newSeq[seq[T]](num) result = newSeq[seq[T]](num)
var var
@ -587,4 +606,14 @@ when isMainModule:
seq2D[0][1] = true seq2D[0][1] = true
doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]] 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" echo "Finished doc tests"

View file

@ -196,7 +196,11 @@ proc mget*[A, B](t: var Table[A, B], key: A): var B =
var hc: THash var hc: THash
var index = rawGet(t, key, hc) var index = rawGet(t, key, hc)
if index >= 0: result = t.data[index].val 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 = 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`. ## iterates over any value in the table `t` that belongs to the given `key`.

View file

@ -365,9 +365,9 @@ proc polar*(z: Complex): tuple[r, phi: float] =
result.phi = phase(z) result.phi = phase(z)
proc rect*(r: float, phi: float): Complex = proc rect*(r: float, phi: float): Complex =
## Returns the complex number with poolar coordinates `r` and `phi`. ## Returns the complex number with polar coordinates `r` and `phi`.
result.re = r * cos(phi) result.re = r * cos(phi)
result.im = sin(phi) result.im = r * sin(phi)
proc `$`*(z: Complex): string = proc `$`*(z: Complex): string =
@ -438,5 +438,6 @@ when isMainModule:
assert( arccoth(a) =~ arctanh(1/a) ) assert( arccoth(a) =~ arctanh(1/a) )
assert( phase(a) == 1.1071487177940904 ) assert( phase(a) == 1.1071487177940904 )
assert( polar(a) =~ (2.23606797749979, 1.1071487177940904) ) var t = polar(a)
assert( rect(t.r, t.phi) =~ a )
assert( rect(1.0, 2.0) =~ (-0.4161468365471424, 0.9092974268256817) ) assert( rect(1.0, 2.0) =~ (-0.4161468365471424, 0.9092974268256817) )

View file

@ -33,8 +33,8 @@
## proc hash(x: Something): THash = ## proc hash(x: Something): THash =
## ## Computes a THash from `x`. ## ## Computes a THash from `x`.
## var h: THash = 0 ## var h: THash = 0
## h = h &! hash(x.foo) ## h = h !& hash(x.foo)
## h = h &! hash(x.bar) ## h = h !& hash(x.bar)
## result = !$h ## result = !$h
import import

View file

@ -390,8 +390,11 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
## server takes longer than specified an ETimeout exception will be raised. ## server takes longer than specified an ETimeout exception will be raised.
var r = if proxy == nil: parseUri(url) else: proxy.url var r = if proxy == nil: parseUri(url) else: proxy.url
var headers = substr(httpMethod, len("http")) var headers = substr(httpMethod, len("http"))
# TODO: Use generateHeaders further down once it supports proxies.
if proxy == nil: if proxy == nil:
headers.add(" " & r.path) headers.add ' '
if r.path[0] != '/': headers.add '/'
headers.add(r.path)
if r.query.len > 0: if r.query.len > 0:
headers.add("?" & r.query) headers.add("?" & r.query)
else: else:
@ -567,9 +570,12 @@ proc downloadFile*(url: string, outputFilename: string,
proc generateHeaders(r: Uri, httpMethod: string, proc generateHeaders(r: Uri, httpMethod: string,
headers: StringTableRef): string = headers: StringTableRef): string =
# TODO: Use this in the blocking HttpClient once it supports proxies.
result = substr(httpMethod, len("http")) result = substr(httpMethod, len("http"))
# TODO: Proxies # TODO: Proxies
result.add(" " & r.path) result.add ' '
if r.path[0] != '/': result.add '/'
result.add(r.path)
if r.query.len > 0: if r.query.len > 0:
result.add("?" & r.query) result.add("?" & r.query)
result.add(" HTTP/1.1\c\L") result.add(" HTTP/1.1\c\L")

View file

@ -816,7 +816,7 @@ proc copy*(p: JsonNode): JsonNode =
# ------------- pretty printing ---------------------------------------------- # ------------- pretty printing ----------------------------------------------
proc indent(s: var string, i: int) = proc indent(s: var string, i: int) =
s.add(repeatChar(i)) s.add(spaces(i))
proc newIndent(curr, indent: int, ml: bool): int = proc newIndent(curr, indent: int, ml: bool): int =
if ml: return curr + indent if ml: return curr + indent

View file

@ -165,5 +165,5 @@ proc getCurrentLine(L: BaseLexer, marker: bool = true): string =
inc(i) inc(i)
add(result, "\n") add(result, "\n")
if marker: if marker:
add(result, repeatChar(getColNumber(L, L.bufpos)) & "^\n") add(result, spaces(getColNumber(L, L.bufpos)) & "^\n")

View file

@ -81,6 +81,23 @@ type
TReadLineResult: ReadLineResult, TSOBool: SOBool, PSocket: Socket, TReadLineResult: ReadLineResult, TSOBool: SOBool, PSocket: Socket,
TSocketImpl: SocketImpl].} 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], proc isDisconnectionError*(flags: set[SocketFlag],
lastError: OSErrorCode): bool = lastError: OSErrorCode): bool =
## Determines whether ``lastError`` is a disconnection error. Only does this ## 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): when defined(ssl):
if socket.isSSL: 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) let ret = SSLConnect(socket.sslHandle)
socketError(socket, ret) socketError(socket, ret)
@ -969,20 +992,6 @@ proc isSsl*(socket: Socket): bool =
proc getFd*(socket: Socket): SocketHandle = return socket.fd proc getFd*(socket: Socket): SocketHandle = return socket.fd
## Returns the socket's file descriptor ## 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 = proc IPv4_any*(): TIpAddress =
## Returns the IPv4 any address, which can be used to listen on all available ## Returns the IPv4 any address, which can be used to listen on all available
## network adapters ## network adapters
@ -1241,7 +1250,7 @@ proc parseIPv6Address(address_str: string): TIpAddress =
raise newException(ValueError, raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups") "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 ## Parses an IP address
## Raises EInvalidValue on error ## Raises EInvalidValue on error
if address_str == nil: if address_str == nil:
@ -1250,3 +1259,13 @@ proc parseIpAddress*(address_str: string): TIpAddress =
return parseIPv6Address(address_str) return parseIPv6Address(address_str)
else: else:
return parseIPv4Address(address_str) 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

View file

@ -11,9 +11,12 @@
## It supports one convenience iterator over all command line options and some ## It supports one convenience iterator over all command line options and some
## lower-level features. ## lower-level features.
## ##
## **Deprecated since version 0.9.3:** Use the `parseopt2 <parseopt2.html>`_ ## Supported syntax:
## module instead as this version has issues with spaces in arguments. ##
{.deprecated.} ## 1. short options - ``-abcd``, where a, b, c, d are names
## 2. long option - ``--foo:bar``, ``--foo=bar`` or ``--foo``
## 3. argument - everything else
{.push debugger: off.} {.push debugger: off.}
include "system/inclrtl" include "system/inclrtl"

View file

@ -128,6 +128,7 @@ proc open*(my: var XmlParser, input: Stream, filename: string,
my.kind = xmlError my.kind = xmlError
my.a = "" my.a = ""
my.b = "" my.b = ""
my.c = nil
my.options = options my.options = options
proc close*(my: var XmlParser) {.inline.} = 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 ## returns the current event type for the XML parser
return my.kind return my.kind
proc charData*(my: XmlParser): string {.inline.} = template charData*(my: XmlParser): string =
## returns the character data for the events: ``xmlCharData``, ## returns the character data for the events: ``xmlCharData``,
## ``xmlWhitespace``, ``xmlComment``, ``xmlCData``, ``xmlSpecial`` ## ``xmlWhitespace``, ``xmlComment``, ``xmlCData``, ``xmlSpecial``
assert(my.kind in {xmlCharData, xmlWhitespace, xmlComment, xmlCData, assert(my.kind in {xmlCharData, xmlWhitespace, xmlComment, xmlCData,
xmlSpecial}) 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``, ## returns the element name for the events: ``xmlElementStart``,
## ``xmlElementEnd``, ``xmlElementOpen`` ## ``xmlElementEnd``, ``xmlElementOpen``
assert(my.kind in {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`` ## returns the entity name for the event: ``xmlEntity``
assert(my.kind == 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`` ## returns the attribute key for the event ``xmlAttribute``
assert(my.kind == 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`` ## returns the attribute value for the event ``xmlAttribute``
assert(my.kind == 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`` ## returns the processing instruction name for the event ``xmlPI``
assert(my.kind == 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`` ## returns the rest of the processing instruction for the event ``xmlPI``
assert(my.kind == xmlPI) assert(my.kind == xmlPI)
return my.b my.b
proc rawData*(my: XmlParser): string {.inline.} = proc rawData*(my: XmlParser): string {.inline.} =
## returns the underlying 'data' string by reference. ## returns the underlying 'data' string by reference.
@ -621,7 +622,7 @@ proc next*(my: var XmlParser) =
of stateEmptyElementTag: of stateEmptyElementTag:
my.state = stateNormal my.state = stateNormal
my.kind = xmlElementEnd my.kind = xmlElementEnd
if not isNil(my.c): if not my.c.isNil:
my.a = my.c my.a = my.c
of stateError: of stateError:
my.kind = xmlError my.kind = xmlError

View file

@ -29,7 +29,7 @@ when useUnicode:
const const
InlineThreshold = 5 ## number of leaves; -1 to disable inlining InlineThreshold = 5 ## number of leaves; -1 to disable inlining
MaxSubpatterns* = 20 ## defines the maximum number of subpatterns that 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 type
PegKind = enum PegKind = enum
@ -85,14 +85,14 @@ type
of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns] of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns]
else: sons: seq[TNode] else: sons: seq[TNode]
NonTerminal* = ref NonTerminalObj NonTerminal* = ref NonTerminalObj
Peg* = TNode ## type that represents a PEG Peg* = TNode ## type that represents a PEG
{.deprecated: [TPeg: Peg].} {.deprecated: [TPeg: Peg].}
proc term*(t: string): Peg {.nosideEffect, rtl, extern: "npegs$1Str".} = proc term*(t: string): Peg {.nosideEffect, rtl, extern: "npegs$1Str".} =
## constructs a PEG from a terminal string ## constructs a PEG from a terminal string
if t.len != 1: if t.len != 1:
result.kind = pkTerminal result.kind = pkTerminal
result.term = t result.term = t
else: else:
@ -116,7 +116,7 @@ proc term*(t: char): Peg {.nosideEffect, rtl, extern: "npegs$1Char".} =
assert t != '\0' assert t != '\0'
result.kind = pkChar result.kind = pkChar
result.ch = t result.ch = t
proc charSet*(s: set[char]): Peg {.nosideEffect, rtl, extern: "npegs$1".} = proc charSet*(s: set[char]): Peg {.nosideEffect, rtl, extern: "npegs$1".} =
## constructs a PEG from a character set `s` ## constructs a PEG from a character set `s`
assert '\0' notin 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) = proc addChoice(dest: var Peg, elem: Peg) =
var L = dest.len-1 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! # caution! Do not introduce false aliasing here!
case elem.kind case elem.kind
of pkCharChoice: of pkCharChoice:
dest.sons[L] = charSet(dest.sons[L].charChoice[] + elem.charChoice[]) dest.sons[L] = charSet(dest.sons[L].charChoice[] + elem.charChoice[])
of pkChar: of pkChar:
dest.sons[L] = charSet(dest.sons[L].charChoice[] + {elem.ch}) dest.sons[L] = charSet(dest.sons[L].charChoice[] + {elem.ch})
else: add(dest, elem) else: add(dest, elem)
else: add(dest, elem) else: add(dest, elem)
@ -158,12 +158,12 @@ proc `/`*(a: varargs[Peg]): Peg {.
proc addSequence(dest: var Peg, elem: Peg) = proc addSequence(dest: var Peg, elem: Peg) =
var L = dest.len-1 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! # caution! Do not introduce false aliasing here!
case elem.kind case elem.kind
of pkTerminal: of pkTerminal:
dest.sons[L] = term(dest.sons[L].term & elem.term) dest.sons[L] = term(dest.sons[L].term & elem.term)
of pkChar: of pkChar:
dest.sons[L] = term(dest.sons[L].term & elem.ch) dest.sons[L] = term(dest.sons[L].term & elem.ch)
else: add(dest, elem) else: add(dest, elem)
else: add(dest, elem) else: add(dest, elem)
@ -172,7 +172,7 @@ proc sequence*(a: varargs[Peg]): Peg {.
nosideEffect, rtl, extern: "npegs$1".} = nosideEffect, rtl, extern: "npegs$1".} =
## constructs a sequence with all the PEGs from `a` ## constructs a sequence with all the PEGs from `a`
multipleOp(pkSequence, addSequence) multipleOp(pkSequence, addSequence)
proc `?`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsOptional".} = proc `?`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsOptional".} =
## constructs an optional for the PEG `a` ## constructs an optional for the PEG `a`
if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar, if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar,
@ -207,7 +207,7 @@ proc `!*`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsSearch".} =
result.kind = pkSearch result.kind = pkSearch
result.sons = @[a] result.sons = @[a]
proc `!*\`*(a: Peg): Peg {.noSideEffect, rtl, proc `!*\`*(a: Peg): Peg {.noSideEffect, rtl,
extern: "npgegsCapturedSearch".} = extern: "npgegsCapturedSearch".} =
## constructs a "captured search" for the PEG `a` ## constructs a "captured search" for the PEG `a`
result.kind = pkCapturedSearch result.kind = pkCapturedSearch
@ -216,7 +216,7 @@ proc `!*\`*(a: Peg): Peg {.noSideEffect, rtl,
proc `+`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsGreedyPosRep".} = proc `+`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsGreedyPosRep".} =
## constructs a "greedy positive repetition" with the PEG `a` ## constructs a "greedy positive repetition" with the PEG `a`
return sequence(a, *a) return sequence(a, *a)
proc `&`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsAndPredicate".} = proc `&`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsAndPredicate".} =
## constructs an "and predicate" with the PEG `a` ## constructs an "and predicate" with the PEG `a`
result.kind = pkAndPredicate result.kind = pkAndPredicate
@ -239,33 +239,33 @@ proc newLine*: Peg {.inline.} =
## constructs the PEG `newline`:idx: (``\n``) ## constructs the PEG `newline`:idx: (``\n``)
result.kind = pkNewLine result.kind = pkNewLine
proc unicodeLetter*: Peg {.inline.} = proc unicodeLetter*: Peg {.inline.} =
## constructs the PEG ``\letter`` which matches any Unicode letter. ## constructs the PEG ``\letter`` which matches any Unicode letter.
result.kind = pkLetter result.kind = pkLetter
proc unicodeLower*: Peg {.inline.} =
## constructs the PEG ``\lower`` which matches any Unicode lowercase letter.
result.kind = pkLower
proc unicodeUpper*: Peg {.inline.} = proc unicodeLower*: Peg {.inline.} =
## constructs the PEG ``\lower`` which matches any Unicode lowercase letter.
result.kind = pkLower
proc unicodeUpper*: Peg {.inline.} =
## constructs the PEG ``\upper`` which matches any Unicode uppercase letter. ## constructs the PEG ``\upper`` which matches any Unicode uppercase letter.
result.kind = pkUpper result.kind = pkUpper
proc unicodeTitle*: Peg {.inline.} = proc unicodeTitle*: Peg {.inline.} =
## constructs the PEG ``\title`` which matches any Unicode title letter. ## constructs the PEG ``\title`` which matches any Unicode title letter.
result.kind = pkTitle result.kind = pkTitle
proc unicodeWhitespace*: Peg {.inline.} = proc unicodeWhitespace*: Peg {.inline.} =
## constructs the PEG ``\white`` which matches any Unicode ## constructs the PEG ``\white`` which matches any Unicode
## whitespace character. ## whitespace character.
result.kind = pkWhitespace result.kind = pkWhitespace
proc startAnchor*: Peg {.inline.} = proc startAnchor*: Peg {.inline.} =
## constructs the PEG ``^`` which matches the start of the input. ## constructs the PEG ``^`` which matches the start of the input.
result.kind = pkStartAnchor result.kind = pkStartAnchor
proc endAnchor*: Peg {.inline.} = proc endAnchor*: Peg {.inline.} =
## constructs the PEG ``$`` which matches the end of the input. ## constructs the PEG ``$`` which matches the end of the input.
result = !any() result = !any()
proc capture*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsCapture".} = 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] result.sons = @[a]
proc backref*(index: range[1..MaxSubpatterns]): Peg {. 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 ## constructs a back reference of the given `index`. `index` starts counting
## from 1. ## from 1.
result.kind = pkBackRef result.kind = pkBackRef
result.index = index-1 result.index = index-1
proc backrefIgnoreCase*(index: range[1..MaxSubpatterns]): Peg {. 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 ## constructs a back reference of the given `index`. `index` starts counting
## from 1. Ignores case for matching. ## from 1. Ignores case for matching.
result.kind = pkBackRefIgnoreCase result.kind = pkBackRefIgnoreCase
result.index = index-1 result.index = index-1
proc backrefIgnoreStyle*(index: range[1..MaxSubpatterns]): Peg {. 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 ## constructs a back reference of the given `index`. `index` starts counting
## from 1. Ignores style for matching. ## from 1. Ignores style for matching.
result.kind = pkBackRefIgnoreStyle result.kind = pkBackRefIgnoreStyle
@ -298,7 +298,7 @@ proc spaceCost(n: Peg): int =
case n.kind case n.kind
of pkEmpty: discard of pkEmpty: discard
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar,
pkGreedyRepChar, pkCharChoice, pkGreedyRepSet, pkGreedyRepChar, pkCharChoice, pkGreedyRepSet,
pkAny..pkWhitespace, pkGreedyAny: pkAny..pkWhitespace, pkGreedyAny:
result = 1 result = 1
of pkNonTerminal: of pkNonTerminal:
@ -310,7 +310,7 @@ proc spaceCost(n: Peg): int =
if result >= InlineThreshold: break if result >= InlineThreshold: break
proc nonterminal*(n: NonTerminal): Peg {. proc nonterminal*(n: NonTerminal): Peg {.
nosideEffect, rtl, extern: "npegs$1".} = nosideEffect, rtl, extern: "npegs$1".} =
## constructs a PEG that consists of the nonterminal symbol ## constructs a PEG that consists of the nonterminal symbol
assert n != nil assert n != nil
if ntDeclared in n.flags and spaceCost(n.rule) < InlineThreshold: 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 = template letters*: expr =
## expands to ``charset({'A'..'Z', 'a'..'z'})`` ## expands to ``charset({'A'..'Z', 'a'..'z'})``
charSet({'A'..'Z', 'a'..'z'}) charSet({'A'..'Z', 'a'..'z'})
template digits*: expr = template digits*: expr =
## expands to ``charset({'0'..'9'})`` ## expands to ``charset({'0'..'9'})``
charSet({'0'..'9'}) charSet({'0'..'9'})
@ -339,11 +339,11 @@ template digits*: expr =
template whitespace*: expr = template whitespace*: expr =
## expands to ``charset({' ', '\9'..'\13'})`` ## expands to ``charset({' ', '\9'..'\13'})``
charSet({' ', '\9'..'\13'}) charSet({' ', '\9'..'\13'})
template identChars*: expr = template identChars*: expr =
## expands to ``charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})`` ## expands to ``charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})``
charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'}) charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'})
template identStartChars*: expr = template identStartChars*: expr =
## expands to ``charset({'A'..'Z', 'a'..'z', '_'})`` ## expands to ``charset({'A'..'Z', 'a'..'z', '_'})``
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 ## same as ``[a-zA-Z_][a-zA-z_0-9]*``; standard identifier
sequence(charSet({'a'..'z', 'A'..'Z', '_'}), sequence(charSet({'a'..'z', 'A'..'Z', '_'}),
*charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'})) *charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'}))
template natural*: expr = template natural*: expr =
## same as ``\d+`` ## same as ``\d+``
+digits +digits
# ------------------------- debugging ----------------------------------------- # ------------------------- debugging -----------------------------------------
proc esc(c: char, reserved = {'\0'..'\255'}): string = proc esc(c: char, reserved = {'\0'..'\255'}): string =
case c case c
of '\b': result = "\\b" of '\b': result = "\\b"
of '\t': result = "\\t" 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 < ' ' or c >= '\128': result = '\\' & $ord(c)
elif c in reserved: result = '\\' & c elif c in reserved: result = '\\' & c
else: result = $c else: result = $c
proc singleQuoteEsc(c: char): string = return "'" & esc(c, {'\''}) & "'" proc singleQuoteEsc(c: char): string = return "'" & esc(c, {'\''}) & "'"
proc singleQuoteEsc(str: string): string = proc singleQuoteEsc(str: string): string =
result = "'" result = "'"
for c in items(str): add result, esc(c, {'\''}) for c in items(str): add result, esc(c, {'\''})
add result, '\'' add result, '\''
proc charSetEscAux(cc: set[char]): string = proc charSetEscAux(cc: set[char]): string =
const reserved = {'^', '-', ']'} const reserved = {'^', '-', ']'}
result = "" result = ""
var c1 = 0 var c1 = 0
while c1 <= 0xff: while c1 <= 0xff:
if chr(c1) in cc: if chr(c1) in cc:
var c2 = c1 var c2 = c1
while c2 < 0xff and chr(succ(c2)) in cc: inc(c2) while c2 < 0xff and chr(succ(c2)) in cc: inc(c2)
if c1 == c2: if c1 == c2:
add result, esc(chr(c1), reserved) add result, esc(chr(c1), reserved)
elif c2 == succ(c1): elif c2 == succ(c1):
add result, esc(chr(c1), reserved) & esc(chr(c2), reserved) add result, esc(chr(c1), reserved) & esc(chr(c2), reserved)
else: else:
add result, esc(chr(c1), reserved) & '-' & esc(chr(c2), reserved) add result, esc(chr(c1), reserved) & '-' & esc(chr(c2), reserved)
c1 = c2 c1 = c2
inc(c1) inc(c1)
proc charSetEsc(cc: set[char]): string = proc charSetEsc(cc: set[char]): string =
if card(cc) >= 128+64: if card(cc) >= 128+64:
result = "[^" & charSetEscAux({'\1'..'\xFF'} - cc) & ']' result = "[^" & charSetEscAux({'\1'..'\xFF'} - cc) & ']'
else: else:
result = '[' & charSetEscAux(cc) & ']' result = '[' & charSetEscAux(cc) & ']'
proc toStrAux(r: Peg, res: var string) = proc toStrAux(r: Peg, res: var string) =
case r.kind case r.kind
of pkEmpty: add(res, "()") of pkEmpty: add(res, "()")
of pkAny: add(res, '.') of pkAny: add(res, '.')
@ -469,25 +469,25 @@ proc toStrAux(r: Peg, res: var string) =
toStrAux(r.sons[0], res) toStrAux(r.sons[0], res)
of pkCapture: of pkCapture:
add(res, '{') add(res, '{')
toStrAux(r.sons[0], res) toStrAux(r.sons[0], res)
add(res, '}') add(res, '}')
of pkBackRef: of pkBackRef:
add(res, '$') add(res, '$')
add(res, $r.index) add(res, $r.index)
of pkBackRefIgnoreCase: of pkBackRefIgnoreCase:
add(res, "i$") add(res, "i$")
add(res, $r.index) add(res, $r.index)
of pkBackRefIgnoreStyle: of pkBackRefIgnoreStyle:
add(res, "y$") add(res, "y$")
add(res, $r.index) add(res, $r.index)
of pkRule: of pkRule:
toStrAux(r.sons[0], res) toStrAux(r.sons[0], res)
add(res, " <- ") add(res, " <- ")
toStrAux(r.sons[1], res) toStrAux(r.sons[1], res)
of pkList: of pkList:
for i in 0 .. high(r.sons): for i in 0 .. high(r.sons):
toStrAux(r.sons[i], res) toStrAux(r.sons[i], res)
add(res, "\n") add(res, "\n")
of pkStartAnchor: of pkStartAnchor:
add(res, '^') add(res, '^')
@ -506,8 +506,8 @@ type
{.deprecated: [TCaptures: Captures].} {.deprecated: [TCaptures: Captures].}
proc bounds*(c: Captures, proc bounds*(c: Captures,
i: range[0..MaxSubpatterns-1]): tuple[first, last: int] = i: range[0..MaxSubpatterns-1]): tuple[first, last: int] =
## returns the bounds ``[first..last]`` of the `i`'th capture. ## returns the bounds ``[first..last]`` of the `i`'th capture.
result = c.matches[i] result = c.matches[i]
@ -527,7 +527,7 @@ when not useUnicode:
proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {. proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
nosideEffect, rtl, extern: "npegs$1".} = 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 ## for maximum efficiency (every other PEG operation ends up calling this
## proc). ## proc).
## Returns -1 if it does not match, else the length of the match ## 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) result = runeLenAt(s, start)
else: else:
result = -1 result = -1
of pkLetter: of pkLetter:
if s[start] != '\0': if s[start] != '\0':
var a: Rune var a: Rune
result = start result = start
@ -550,7 +550,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
else: result = -1 else: result = -1
else: else:
result = -1 result = -1
of pkLower: of pkLower:
if s[start] != '\0': if s[start] != '\0':
var a: Rune var a: Rune
result = start result = start
@ -559,7 +559,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
else: result = -1 else: result = -1
else: else:
result = -1 result = -1
of pkUpper: of pkUpper:
if s[start] != '\0': if s[start] != '\0':
var a: Rune var a: Rune
result = start result = start
@ -568,16 +568,16 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
else: result = -1 else: result = -1
else: else:
result = -1 result = -1
of pkTitle: of pkTitle:
if s[start] != '\0': if s[start] != '\0':
var a: Rune var a: Rune
result = start result = start
fastRuneAt(s, result, a) fastRuneAt(s, result, a)
if isTitle(a): dec(result, start) if isTitle(a): dec(result, start)
else: result = -1 else: result = -1
else: else:
result = -1 result = -1
of pkWhitespace: of pkWhitespace:
if s[start] != '\0': if s[start] != '\0':
var a: Rune var a: Rune
result = start 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 when false: echo "leave: ", p.nt.name
if result < 0: c.ml = oldMl if result < 0: c.ml = oldMl
of pkSequence: of pkSequence:
var oldMl = c.ml var oldMl = c.ml
result = 0 result = 0
for i in 0..high(p.sons): for i in 0..high(p.sons):
var x = rawMatch(s, p.sons[i], start+result, c) 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: silently ignore the capture
else: else:
c.ml = idx c.ml = idx
of pkBackRef..pkBackRefIgnoreStyle: of pkBackRef..pkBackRefIgnoreStyle:
if p.index >= c.ml: return -1 if p.index >= c.ml: return -1
var (a, b) = c.matches[p.index] var (a, b) = c.matches[p.index]
var n: Peg 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) n.term = s.substr(a, b)
result = rawMatch(s, n, start, c) result = rawMatch(s, n, start, c)
of pkStartAnchor: 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 result = rawMatch(s, pattern, start, c) == len(s) - start
if result: fillMatches(s, matches, c) 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".} = start = 0): bool {.nosideEffect, rtl, extern: "npegs$1".} =
## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``. ## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``.
var c: Captures var c: Captures
@ -773,7 +773,7 @@ proc matchLen*(s: string, pattern: Peg, matches: var openArray[string],
result = rawMatch(s, pattern, start, c) result = rawMatch(s, pattern, start, c)
if result >= 0: fillMatches(s, matches, 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".} = start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} =
## the same as ``match``, but it returns the length of the match, ## 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 ## 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 i
return -1 return -1
# could also use the pattern here: (!P .)* P # could also use the pattern here: (!P .)* P
proc findBounds*(s: string, pattern: Peg, matches: var openArray[string], proc findBounds*(s: string, pattern: Peg, matches: var openArray[string],
start = 0): tuple[first, last: int] {. start = 0): tuple[first, last: int] {.
nosideEffect, rtl, extern: "npegs$1Capture".} = 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 ## and the captured
## substrings in the array ``matches``. If it does not match, nothing ## substrings in the array ``matches``. If it does not match, nothing
## is written into ``matches`` and (-1,0) is returned. ## 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) fillMatches(s, matches, c)
return (i, i+L-1) return (i, i+L-1)
return (-1, 0) return (-1, 0)
proc find*(s: string, pattern: Peg, proc find*(s: string, pattern: Peg,
start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} = start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} =
## returns the starting position of ``pattern`` in ``s``. If it does not ## returns the starting position of ``pattern`` in ``s``. If it does not
## match, -1 is returned. ## match, -1 is returned.
@ -824,8 +824,8 @@ proc find*(s: string, pattern: Peg,
for i in start .. s.len-1: for i in start .. s.len-1:
if rawMatch(s, pattern, i, c) >= 0: return i if rawMatch(s, pattern, i, c) >= 0: return i
return -1 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`. ## yields all matching *substrings* of `s` that match `pattern`.
var c: Captures var c: Captures
c.origStart = start c.origStart = start
@ -838,23 +838,23 @@ iterator findAll*(s: string, pattern: Peg, start = 0): string =
else: else:
yield substr(s, i, i+L-1) yield substr(s, i, i+L-1)
inc(i, L) inc(i, L)
proc findAll*(s: string, pattern: Peg, start = 0): seq[string] {. 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`. ## returns all matching *substrings* of `s` that match `pattern`.
## If it does not match, @[] is returned. ## If it does not match, @[] is returned.
accumulateResult(findAll(s, pattern, start)) accumulateResult(findAll(s, pattern, start))
when not defined(nimhygiene): when not defined(nimhygiene):
{.pragma: inject.} {.pragma: inject.}
template `=~`*(s: string, pattern: Peg): bool = template `=~`*(s: string, pattern: Peg): bool =
## This calls ``match`` with an implicit declared ``matches`` array that ## This calls ``match`` with an implicit declared ``matches`` array that
## can be used in the scope of the ``=~`` call: ## can be used in the scope of the ``=~`` call:
## ##
## .. code-block:: nim ## .. code-block:: nim
## ##
## if line =~ peg"\s* {\w+} \s* '=' \s* {\w+}": ## if line =~ peg"\s* {\w+} \s* '=' \s* {\w+}":
## # matches a key=value pair: ## # matches a key=value pair:
## echo("Key: ", matches[0]) ## echo("Key: ", matches[0])
## echo("Value: ", matches[1]) ## echo("Value: ", matches[1])
@ -865,7 +865,7 @@ template `=~`*(s: string, pattern: Peg): bool =
## echo("comment: ", matches[0]) ## echo("comment: ", matches[0])
## else: ## else:
## echo("syntax error") ## echo("syntax error")
## ##
bind MaxSubpatterns bind MaxSubpatterns
when not declaredInScope(matches): when not declaredInScope(matches):
var matches {.inject.}: array[0..MaxSubpatterns-1, string] 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: ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
## ##
## .. code-block:: nim ## .. 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: ## Results in:
## ##
@ -941,10 +941,10 @@ proc replace*(s: string, sub: Peg, by = ""): string {.
add(result, by) add(result, by)
inc(i, x) inc(i, x)
add(result, substr(s, i)) add(result, substr(s, i))
proc parallelReplace*(s: string, subs: varargs[ proc parallelReplace*(s: string, subs: varargs[
tuple[pattern: Peg, repl: string]]): string {. 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` ## Returns a modified copy of `s` with the substitutions in `subs`
## applied in parallel. ## applied in parallel.
result = "" result = ""
@ -964,8 +964,8 @@ proc parallelReplace*(s: string, subs: varargs[
add(result, s[i]) add(result, s[i])
inc(i) inc(i)
# copy the rest: # copy the rest:
add(result, substr(s, i)) add(result, substr(s, i))
proc transformFile*(infile, outfile: string, proc transformFile*(infile, outfile: string,
subs: varargs[tuple[pattern: Peg, repl: string]]) {. subs: varargs[tuple[pattern: Peg, repl: string]]) {.
rtl, extern: "npegs$1".} = rtl, extern: "npegs$1".} =
@ -974,7 +974,7 @@ proc transformFile*(infile, outfile: string,
## error occurs. This is supposed to be used for quick scripting. ## error occurs. This is supposed to be used for quick scripting.
var x = readFile(infile).string var x = readFile(infile).string
writeFile(outfile, x.parallelReplace(subs)) writeFile(outfile, x.parallelReplace(subs))
iterator split*(s: string, sep: Peg): string = iterator split*(s: string, sep: Peg): string =
## Splits the string `s` into substrings. ## Splits the string `s` into substrings.
## ##
@ -1049,14 +1049,14 @@ type
tkBackref, ## '$' tkBackref, ## '$'
tkDollar, ## '$' tkDollar, ## '$'
tkHat ## '^' tkHat ## '^'
TToken {.final.} = object ## a token TToken {.final.} = object ## a token
kind: TTokKind ## the type of the token kind: TTokKind ## the type of the token
modifier: TModifier modifier: TModifier
literal: string ## the parsed (string) literal literal: string ## the parsed (string) literal
charset: set[char] ## if kind == tkCharSet charset: set[char] ## if kind == tkCharSet
index: int ## if kind == tkBackref index: int ## if kind == tkBackref
PegLexer {.inheritable.} = object ## the lexer object. PegLexer {.inheritable.} = object ## the lexer object.
bufpos: int ## the current position within the buffer bufpos: int ## the current position within the buffer
buf: cstring ## the buffer itself buf: cstring ## the buffer itself
@ -1086,7 +1086,7 @@ proc handleLF(L: var PegLexer, pos: int): int =
result = pos+1 result = pos+1
L.lineStart = result 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.buf = input
L.bufpos = 0 L.bufpos = 0
L.lineNumber = line L.lineNumber = line
@ -1094,69 +1094,69 @@ proc init(L: var PegLexer, input, filename: string, line = 1, col = 0) =
L.lineStart = 0 L.lineStart = 0
L.filename = filename L.filename = filename
proc getColumn(L: PegLexer): int {.inline.} = proc getColumn(L: PegLexer): int {.inline.} =
result = abs(L.bufpos - L.lineStart) + L.colOffset result = abs(L.bufpos - L.lineStart) + L.colOffset
proc getLine(L: PegLexer): int {.inline.} = proc getLine(L: PegLexer): int {.inline.} =
result = L.lineNumber result = L.lineNumber
proc errorStr(L: PegLexer, msg: string, line = -1, col = -1): string = proc errorStr(L: PegLexer, msg: string, line = -1, col = -1): string =
var line = if line < 0: getLine(L) else: line var line = if line < 0: getLine(L) else: line
var col = if col < 0: getColumn(L) else: col var col = if col < 0: getColumn(L) else: col
result = "$1($2, $3) Error: $4" % [L.filename, $line, $col, msg] 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] case c.buf[c.bufpos]
of '0'..'9': of '0'..'9':
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0')) xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0'))
inc(c.bufpos) inc(c.bufpos)
of 'a'..'f': of 'a'..'f':
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10) xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10)
inc(c.bufpos) inc(c.bufpos)
of 'A'..'F': of 'A'..'F':
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10) xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
inc(c.bufpos) inc(c.bufpos)
else: discard else: discard
proc getEscapedChar(c: var PegLexer, tok: var TToken) = proc getEscapedChar(c: var PegLexer, tok: var TToken) =
inc(c.bufpos) inc(c.bufpos)
case c.buf[c.bufpos] case c.buf[c.bufpos]
of 'r', 'R', 'c', 'C': of 'r', 'R', 'c', 'C':
add(tok.literal, '\c') add(tok.literal, '\c')
inc(c.bufpos) inc(c.bufpos)
of 'l', 'L': of 'l', 'L':
add(tok.literal, '\L') add(tok.literal, '\L')
inc(c.bufpos) inc(c.bufpos)
of 'f', 'F': of 'f', 'F':
add(tok.literal, '\f') add(tok.literal, '\f')
inc(c.bufpos) inc(c.bufpos)
of 'e', 'E': of 'e', 'E':
add(tok.literal, '\e') add(tok.literal, '\e')
inc(c.bufpos) inc(c.bufpos)
of 'a', 'A': of 'a', 'A':
add(tok.literal, '\a') add(tok.literal, '\a')
inc(c.bufpos) inc(c.bufpos)
of 'b', 'B': of 'b', 'B':
add(tok.literal, '\b') add(tok.literal, '\b')
inc(c.bufpos) inc(c.bufpos)
of 'v', 'V': of 'v', 'V':
add(tok.literal, '\v') add(tok.literal, '\v')
inc(c.bufpos) inc(c.bufpos)
of 't', 'T': of 't', 'T':
add(tok.literal, '\t') add(tok.literal, '\t')
inc(c.bufpos) inc(c.bufpos)
of 'x', 'X': of 'x', 'X':
inc(c.bufpos) inc(c.bufpos)
var xi = 0 var xi = 0
handleHexChar(c, xi) handleHexChar(c, xi)
handleHexChar(c, xi) handleHexChar(c, xi)
if xi == 0: tok.kind = tkInvalid if xi == 0: tok.kind = tkInvalid
else: add(tok.literal, chr(xi)) else: add(tok.literal, chr(xi))
of '0'..'9': of '0'..'9':
var val = ord(c.buf[c.bufpos]) - ord('0') var val = ord(c.buf[c.bufpos]) - ord('0')
inc(c.bufpos) inc(c.bufpos)
var i = 1 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') val = val * 10 + ord(c.buf[c.bufpos]) - ord('0')
inc(c.bufpos) inc(c.bufpos)
inc(i) inc(i)
@ -1169,32 +1169,32 @@ proc getEscapedChar(c: var PegLexer, tok: var TToken) =
else: else:
add(tok.literal, c.buf[c.bufpos]) add(tok.literal, c.buf[c.bufpos])
inc(c.bufpos) inc(c.bufpos)
proc skip(c: var PegLexer) = proc skip(c: var PegLexer) =
var pos = c.bufpos var pos = c.bufpos
var buf = c.buf var buf = c.buf
while true: while true:
case buf[pos] case buf[pos]
of ' ', '\t': of ' ', '\t':
inc(pos) inc(pos)
of '#': of '#':
while not (buf[pos] in {'\c', '\L', '\0'}): inc(pos) while not (buf[pos] in {'\c', '\L', '\0'}): inc(pos)
of '\c': of '\c':
pos = handleCR(c, pos) pos = handleCR(c, pos)
buf = c.buf buf = c.buf
of '\L': of '\L':
pos = handleLF(c, pos) pos = handleLF(c, pos)
buf = c.buf buf = c.buf
else: else:
break # EndOfFile also leaves the loop break # EndOfFile also leaves the loop
c.bufpos = pos c.bufpos = pos
proc getString(c: var PegLexer, tok: var TToken) = proc getString(c: var PegLexer, tok: var TToken) =
tok.kind = tkStringLit tok.kind = tkStringLit
var pos = c.bufpos + 1 var pos = c.bufpos + 1
var buf = c.buf var buf = c.buf
var quote = buf[pos-1] var quote = buf[pos-1]
while true: while true:
case buf[pos] case buf[pos]
of '\\': of '\\':
c.bufpos = pos c.bufpos = pos
@ -1205,13 +1205,13 @@ proc getString(c: var PegLexer, tok: var TToken) =
break break
elif buf[pos] == quote: elif buf[pos] == quote:
inc(pos) inc(pos)
break break
else: else:
add(tok.literal, buf[pos]) add(tok.literal, buf[pos])
inc(pos) inc(pos)
c.bufpos = 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 pos = c.bufpos + 1
var buf = c.buf var buf = c.buf
if buf[pos] in {'0'..'9'}: if buf[pos] in {'0'..'9'}:
@ -1223,8 +1223,8 @@ proc getDollar(c: var PegLexer, tok: var TToken) =
else: else:
tok.kind = tkDollar tok.kind = tkDollar
c.bufpos = pos c.bufpos = pos
proc getCharSet(c: var PegLexer, tok: var TToken) = proc getCharSet(c: var PegLexer, tok: var TToken) =
tok.kind = tkCharSet tok.kind = tkCharSet
tok.charset = {} tok.charset = {}
var pos = c.bufpos + 1 var pos = c.bufpos + 1
@ -1247,7 +1247,7 @@ proc getCharSet(c: var PegLexer, tok: var TToken) =
of '\C', '\L', '\0': of '\C', '\L', '\0':
tok.kind = tkInvalid tok.kind = tkInvalid
break break
else: else:
ch = buf[pos] ch = buf[pos]
inc(pos) inc(pos)
incl(tok.charset, ch) incl(tok.charset, ch)
@ -1267,18 +1267,18 @@ proc getCharSet(c: var PegLexer, tok: var TToken) =
of '\C', '\L', '\0': of '\C', '\L', '\0':
tok.kind = tkInvalid tok.kind = tkInvalid
break break
else: else:
ch2 = buf[pos] ch2 = buf[pos]
inc(pos) inc(pos)
for i in ord(ch)+1 .. ord(ch2): for i in ord(ch)+1 .. ord(ch2):
incl(tok.charset, chr(i)) incl(tok.charset, chr(i))
c.bufpos = pos c.bufpos = pos
if caret: tok.charset = {'\1'..'\xFF'} - tok.charset 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 pos = c.bufpos
var buf = c.buf var buf = c.buf
while true: while true:
add(tok.literal, buf[pos]) add(tok.literal, buf[pos])
inc(pos) inc(pos)
if buf[pos] notin strutils.IdentChars: break if buf[pos] notin strutils.IdentChars: break
@ -1294,7 +1294,7 @@ proc getBuiltin(c: var PegLexer, tok: var TToken) =
tok.kind = tkEscaped tok.kind = tkEscaped
getEscapedChar(c, tok) # may set tok.kind to tkInvalid 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.kind = tkInvalid
tok.modifier = modNone tok.modifier = modNone
setLen(tok.literal, 0) setLen(tok.literal, 0)
@ -1309,11 +1309,11 @@ proc getTok(c: var PegLexer, tok: var TToken) =
else: else:
tok.kind = tkCurlyLe tok.kind = tkCurlyLe
add(tok.literal, '{') add(tok.literal, '{')
of '}': of '}':
tok.kind = tkCurlyRi tok.kind = tkCurlyRi
inc(c.bufpos) inc(c.bufpos)
add(tok.literal, '}') add(tok.literal, '}')
of '[': of '[':
getCharSet(c, tok) getCharSet(c, tok)
of '(': of '(':
tok.kind = tkParLe tok.kind = tkParLe
@ -1323,7 +1323,7 @@ proc getTok(c: var PegLexer, tok: var TToken) =
tok.kind = tkParRi tok.kind = tkParRi
inc(c.bufpos) inc(c.bufpos)
add(tok.literal, ')') add(tok.literal, ')')
of '.': of '.':
tok.kind = tkAny tok.kind = tkAny
inc(c.bufpos) inc(c.bufpos)
add(tok.literal, '.') add(tok.literal, '.')
@ -1331,16 +1331,16 @@ proc getTok(c: var PegLexer, tok: var TToken) =
tok.kind = tkAnyRune tok.kind = tkAnyRune
inc(c.bufpos) inc(c.bufpos)
add(tok.literal, '_') add(tok.literal, '_')
of '\\': of '\\':
getBuiltin(c, tok) getBuiltin(c, tok)
of '\'', '"': getString(c, tok) of '\'', '"': getString(c, tok)
of '$': getDollar(c, tok) of '$': getDollar(c, tok)
of '\0': of '\0':
tok.kind = tkEof tok.kind = tkEof
tok.literal = "[EOF]" tok.literal = "[EOF]"
of 'a'..'z', 'A'..'Z', '\128'..'\255': of 'a'..'z', 'A'..'Z', '\128'..'\255':
getSymbol(c, tok) 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'}: c.buf[c.bufpos] == '$' and c.buf[c.bufpos+1] in {'0'..'9'}:
case tok.literal case tok.literal
of "i": tok.modifier = modIgnoreCase of "i": tok.modifier = modIgnoreCase
@ -1388,7 +1388,7 @@ proc getTok(c: var PegLexer, tok: var TToken) =
tok.kind = tkAt tok.kind = tkAt
inc(c.bufpos) inc(c.bufpos)
add(tok.literal, '@') add(tok.literal, '@')
if c.buf[c.bufpos] == '@': if c.buf[c.bufpos] == '@':
tok.kind = tkCurlyAt tok.kind = tkCurlyAt
inc(c.bufpos) inc(c.bufpos)
add(tok.literal, '@') add(tok.literal, '@')
@ -1407,7 +1407,7 @@ proc arrowIsNextTok(c: PegLexer): bool =
result = c.buf[pos] == '<' and c.buf[pos+1] == '-' result = c.buf[pos] == '<' and c.buf[pos+1] == '-'
# ----------------------------- parser ---------------------------------------- # ----------------------------- parser ----------------------------------------
type type
EInvalidPeg* = object of ValueError ## raised if an invalid EInvalidPeg* = object of ValueError ## raised if an invalid
## PEG has been detected ## 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) e.msg = errorStr(p, msg, line, col)
raise e raise e
proc getTok(p: var PegParser) = proc getTok(p: var PegParser) =
getTok(p, p.tok) getTok(p, p.tok)
if p.tok.kind == tkInvalid: pegError(p, "invalid token") if p.tok.kind == tkInvalid: pegError(p, "invalid token")
@ -1475,7 +1475,7 @@ proc builtin(p: var PegParser): Peg =
of "white": result = unicodeWhitespace() of "white": result = unicodeWhitespace()
else: pegError(p, "unknown built-in: " & p.tok.literal) 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 if p.skip.kind == pkEmpty: result = terminal
else: result = sequence(p.skip, terminal) else: result = sequence(p.skip, terminal)
@ -1496,7 +1496,7 @@ proc primary(p: var PegParser): Peg =
else: discard else: discard
case p.tok.kind case p.tok.kind
of tkIdentifier: of tkIdentifier:
if p.identIsVerbatim: if p.identIsVerbatim:
var m = p.tok.modifier var m = p.tok.modifier
if m == modNone: m = p.modifier if m == modNone: m = p.modifier
result = modifiedTerm(p.tok.literal, m).token(p) result = modifiedTerm(p.tok.literal, m).token(p)
@ -1539,17 +1539,17 @@ proc primary(p: var PegParser): Peg =
of tkEscaped: of tkEscaped:
result = term(p.tok.literal[0]).token(p) result = term(p.tok.literal[0]).token(p)
getTok(p) getTok(p)
of tkDollar: of tkDollar:
result = endAnchor() result = endAnchor()
getTok(p) getTok(p)
of tkHat: of tkHat:
result = startAnchor() result = startAnchor()
getTok(p) getTok(p)
of tkBackref: of tkBackref:
var m = p.tok.modifier var m = p.tok.modifier
if m == modNone: m = p.modifier if m == modNone: m = p.modifier
result = modifiedBackref(p.tok.index, m).token(p) 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) pegError(p, "invalid back reference index: " & $p.tok.index)
getTok(p) getTok(p)
else: else:
@ -1573,7 +1573,7 @@ proc seqExpr(p: var PegParser): Peg =
while true: while true:
case p.tok.kind case p.tok.kind
of tkAmp, tkNot, tkAt, tkStringLit, tkCharSet, tkParLe, tkCurlyLe, of tkAmp, tkNot, tkAt, tkStringLit, tkCharSet, tkParLe, tkCurlyLe,
tkAny, tkAnyRune, tkBuiltin, tkEscaped, tkDollar, tkBackref, tkAny, tkAnyRune, tkBuiltin, tkEscaped, tkDollar, tkBackref,
tkHat, tkCurlyAt: tkHat, tkCurlyAt:
result = sequence(result, primary(p)) result = sequence(result, primary(p))
of tkIdentifier: of tkIdentifier:
@ -1587,7 +1587,7 @@ proc parseExpr(p: var PegParser): Peg =
while p.tok.kind == tkBar: while p.tok.kind == tkBar:
getTok(p) getTok(p)
result = result / seqExpr(p) result = result / seqExpr(p)
proc parseRule(p: var PegParser): NonTerminal = proc parseRule(p: var PegParser): NonTerminal =
if p.tok.kind == tkIdentifier and arrowIsNextTok(p): if p.tok.kind == tkIdentifier and arrowIsNextTok(p):
result = getNonTerminal(p, p.tok.literal) 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 incl(result.flags, ntDeclared) # NOW inlining may be attempted
else: else:
pegError(p, "rule expected, but found: " & p.tok.literal) pegError(p, "rule expected, but found: " & p.tok.literal)
proc rawParse(p: var PegParser): Peg = proc rawParse(p: var PegParser): Peg =
## parses a rule or a PEG expression ## parses a rule or a PEG expression
while p.tok.kind == tkBuiltin: 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_L", peg"\y 'while'"))
assert(not match("W_HI_Le", peg"\y v'while'")) assert(not match("W_HI_Le", peg"\y v'while'"))
assert match("W_HI_Le", peg"y'while'") assert match("W_HI_Le", peg"y'while'")
assert($ +digits == $peg"\d+") assert($ +digits == $peg"\d+")
assert "0158787".match(peg"\d+") assert "0158787".match(peg"\d+")
assert "ABC 0232".match(peg"\w+\s+\d+") assert "ABC 0232".match(peg"\w+\s+\d+")
@ -1693,14 +1693,14 @@ when isMainModule:
var pattern = sequence(ident, *whitespace, term('='), *whitespace, ident) var pattern = sequence(ident, *whitespace, term('='), *whitespace, ident)
assert matchLen("key1= cal9", pattern) == 11 assert matchLen("key1= cal9", pattern) == 11
var ws = newNonTerminal("ws", 1, 1) var ws = newNonTerminal("ws", 1, 1)
ws.rule = *whitespace ws.rule = *whitespace
var expr = newNonTerminal("expr", 1, 1) var expr = newNonTerminal("expr", 1, 1)
expr.rule = sequence(capture(ident), *sequence( expr.rule = sequence(capture(ident), *sequence(
nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr))) nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr)))
var c: Captures var c: Captures
var s = "a+b + c +d+e+f" var s = "a+b + c +d+e+f"
assert rawMatch(s, expr.rule, 0, c) == len(s) assert rawMatch(s, expr.rule, 0, c) == len(s)
@ -1722,7 +1722,7 @@ when isMainModule:
assert matches[0] == "abc" assert matches[0] == "abc"
else: else:
assert false assert false
var g2 = peg"""S <- A B / C D var g2 = peg"""S <- A B / C D
A <- 'a'+ A <- 'a'+
B <- 'b'+ B <- 'b'+
@ -1753,13 +1753,13 @@ when isMainModule:
for x in findAll("abcdef", peg"^{.}", 3): for x in findAll("abcdef", peg"^{.}", 3):
assert x == "d" assert x == "d"
if "f(a, b)" =~ peg"{[0-9]+} / ({\ident} '(' {@} ')')": if "f(a, b)" =~ peg"{[0-9]+} / ({\ident} '(' {@} ')')":
assert matches[0] == "f" assert matches[0] == "f"
assert matches[1] == "a, b" assert matches[1] == "a, b"
else: else:
assert false assert false
assert match("eine übersicht und außerdem", peg"(\letter \white*)+") assert match("eine übersicht und außerdem", peg"(\letter \white*)+")
# ß is not a lower cased letter?! # ß is not a lower cased letter?!
assert match("eine übersicht und auerdem", peg"(\lower \white*)+") assert match("eine übersicht und auerdem", peg"(\lower \white*)+")

View file

@ -798,6 +798,22 @@ proc zunionstore*(r: Redis, destination: string, numkeys: string,
return r.readInteger() 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 # Pub/Sub

View file

@ -224,10 +224,12 @@ proc ssReadData(s: Stream, buffer: pointer, bufLen: int): int =
proc ssWriteData(s: Stream, buffer: pointer, bufLen: int) = proc ssWriteData(s: Stream, buffer: pointer, bufLen: int) =
var s = StringStream(s) var s = StringStream(s)
if bufLen > 0: if bufLen <= 0:
setLen(s.data, s.data.len + bufLen) return
copyMem(addr(s.data[s.pos]), buffer, bufLen) if s.pos + bufLen > s.data.len:
inc(s.pos, bufLen) setLen(s.data, s.pos + bufLen)
copyMem(addr(s.data[s.pos]), buffer, bufLen)
inc(s.pos, bufLen)
proc ssClose(s: Stream) = proc ssClose(s: Stream) =
var s = StringStream(s) var s = StringStream(s)

View file

@ -499,26 +499,47 @@ proc parseEnum*[T: enum](s: string, default: T): T =
return e return e
result = default result = default
proc repeatChar*(count: int, c: char = ' '): string {.noSideEffect, proc repeat*(c: char, count: int): string {.noSideEffect,
rtl, extern: "nsuRepeatChar".} = rtl, extern: "nsuRepeatChar".} =
## Returns a string of length `count` consisting only of ## Returns a string of length `count` consisting only of
## the character `c`. You can use this proc to left align strings. Example: ## the character `c`. You can use this proc to left align strings. Example:
## ##
## .. code-block:: nim ## .. code-block:: nim
## proc tabexpand(indent: int, text: string, tabsize: int = 4) =
## echo '\t'.repeat(indent div tabsize), ' '.repeat(indent mod tabsize), text
##
## tabexpand(4, "At four")
## tabexpand(5, "At five")
## tabexpand(6, "At six")
result = newString(count)
for i in 0..count-1: result[i] = c
proc repeat*(s: string, n: int): string {.noSideEffect,
rtl, extern: "nsuRepeatStr".} =
## Returns String `s` concatenated `n` times. Example:
##
## .. code-block:: nim
## echo "+++ STOP ".repeat(4), "+++"
result = newStringOfCap(n * s.len)
for i in 1..n: result.add(s)
template spaces*(n: int): string = repeat(' ',n)
## Returns a String with `n` space characters. You can use this proc
## to left align strings. Example:
##
## .. code-block:: nim
## let ## let
## width = 15 ## width = 15
## text1 = "Hello user!" ## text1 = "Hello user!"
## text2 = "This is a very long string" ## text2 = "This is a very long string"
## echo text1 & repeatChar(max(0, width - text1.len)) & "|" ## echo text1 & spaces(max(0, width - text1.len)) & "|"
## echo text2 & repeatChar(max(0, width - text2.len)) & "|" ## echo text2 & spaces(max(0, width - text2.len)) & "|"
result = newString(count)
for i in 0..count-1: result[i] = c
proc repeatStr*(count: int, s: string): string {.noSideEffect, proc repeatChar*(count: int, c: char = ' '): string {.deprecated.} = repeat(c, count)
rtl, extern: "nsuRepeatStr".} = ## deprecated: use repeat() or spaces()
## Returns `s` concatenated `count` times.
result = newStringOfCap(count*s.len) proc repeatStr*(count: int, s: string): string {.deprecated.} = repeat(s, count)
for i in 0..count-1: result.add(s) ## deprecated: use repeat(string, count) or string.repeat(count)
proc align*(s: string, count: int, padding = ' '): string {. proc align*(s: string, count: int, padding = ' '): string {.
noSideEffect, rtl, extern: "nsuAlignString".} = noSideEffect, rtl, extern: "nsuAlignString".} =

View file

@ -45,7 +45,6 @@ when defined(windows):
var var
oldAttr = getAttributes() oldAttr = getAttributes()
proc winGetch(): cint {.header: "<conio.h>", importc: "_getch".}
else: else:
import termios, unsigned import termios, unsigned
@ -344,7 +343,7 @@ proc isatty*(f: File): bool =
else: else:
proc isatty(fildes: FileHandle): cint {. proc isatty(fildes: FileHandle): cint {.
importc: "_isatty", header: "<io.h>".} importc: "_isatty", header: "<io.h>".}
result = isatty(getFileHandle(f)) != 0'i32 result = isatty(getFileHandle(f)) != 0'i32
proc styledEchoProcessArg(s: string) = write stdout, s 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"write", bindSym"stdout", newStrLitNode("\n")))
result.add(newCall(bindSym"resetAttributes")) result.add(newCall(bindSym"resetAttributes"))
proc getch*(): char = when not defined(windows):
## Read a single character from the terminal, blocking until it is entered. proc getch*(): char =
## The character is not printed to the terminal. ## Read a single character from the terminal, blocking until it is entered.
when defined(windows): ## The character is not printed to the terminal. This is not available for
result = winGetch().char ## Windows.
else:
let fd = getFileHandle(stdin) let fd = getFileHandle(stdin)
var oldMode: Termios var oldMode: Termios
discard fd.tcgetattr(addr oldMode) discard fd.tcgetattr(addr oldMode)
@ -387,5 +385,5 @@ when isMainModule:
setForeGroundColor(fgBlue) setForeGroundColor(fgBlue)
writeln(stdout, "ordinary text") writeln(stdout, "ordinary text")
styledEcho("styled text ", {styleBright, styleBlink, styleUnderscore}) styledEcho("styled text ", {styleBright, styleBlink, styleUnderscore})

View file

@ -628,25 +628,25 @@ proc formatToken(info: TimeInfo, token: string, buf: var string) =
var fr = ($info.year).len()-2 var fr = ($info.year).len()-2
if fr < 0: fr = 0 if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1] var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 2: fyear = repeatChar(2-fyear.len(), '0') & fyear if fyear.len != 2: fyear = repeat('0', 2-fyear.len()) & fyear
buf.add(fyear) buf.add(fyear)
of "yyy": of "yyy":
var fr = ($info.year).len()-3 var fr = ($info.year).len()-3
if fr < 0: fr = 0 if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1] var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 3: fyear = repeatChar(3-fyear.len(), '0') & fyear if fyear.len != 3: fyear = repeat('0', 3-fyear.len()) & fyear
buf.add(fyear) buf.add(fyear)
of "yyyy": of "yyyy":
var fr = ($info.year).len()-4 var fr = ($info.year).len()-4
if fr < 0: fr = 0 if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1] var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 4: fyear = repeatChar(4-fyear.len(), '0') & fyear if fyear.len != 4: fyear = repeat('0', 4-fyear.len()) & fyear
buf.add(fyear) buf.add(fyear)
of "yyyyy": of "yyyyy":
var fr = ($info.year).len()-5 var fr = ($info.year).len()-5
if fr < 0: fr = 0 if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1] var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 5: fyear = repeatChar(5-fyear.len(), '0') & fyear if fyear.len != 5: fyear = repeat('0', 5-fyear.len()) & fyear
buf.add(fyear) buf.add(fyear)
of "z": of "z":
let hrs = (info.timezone div 60) div 60 let hrs = (info.timezone div 60) div 60

View file

@ -118,21 +118,35 @@ proc toUTF8*(c: Rune): string {.rtl, extern: "nuc$1".} =
elif i <=% 0x07FF: elif i <=% 0x07FF:
result = newString(2) result = newString(2)
result[0] = chr((i shr 6) or 0b110_00000) 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: elif i <=% 0xFFFF:
result = newString(3) result = newString(3)
result[0] = chr(i shr 12 or 0b1110_0000) result[0] = chr(i shr 12 or 0b1110_0000)
result[1] = chr(i shr 6 and ones(6) or 0b10_0000_00) result[1] = chr(i shr 6 and ones(6) or 0b10_0000_00)
result[2] = chr(i 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 = newString(4)
result[0] = chr(i shr 18 or 0b1111_0000) result[0] = chr(i shr 18 or 0b1111_0000)
result[1] = chr(i shr 12 and ones(6) or 0b10_0000_00) 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[2] = chr(i shr 6 and ones(6) or 0b10_0000_00)
result[3] = chr(i 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: else:
result = newString(1) discard # error, exception?
result[0] = chr(i)
proc `$`*(rune: Rune): string = proc `$`*(rune: Rune): string =
## converts a rune to a string ## converts a rune to a string

View file

@ -285,6 +285,16 @@ proc `$`*(u: Uri): string =
result.add(u.anchor) result.add(u.anchor)
when isMainModule: 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: block:
let str = "http://localhost:8080/test" let str = "http://localhost:8080/test"
let test = parseUri(str) let test = parseUri(str)

View file

@ -1083,7 +1083,7 @@ proc addEscaped(s: string): string =
else: result.add(c) else: result.add(c)
proc nodeToXml(n: PNode, indent: int = 0): string = proc nodeToXml(n: PNode, indent: int = 0): string =
result = repeatChar(indent, ' ') & "<" & n.nodeName result = spaces(indent) & "<" & n.nodeName
if not isNil(n.attributes): if not isNil(n.attributes):
for i in items(n.attributes): for i in items(n.attributes):
result.add(" " & i.name & "=\"" & addEscaped(i.value) & "\"") result.add(" " & i.name & "=\"" & addEscaped(i.value) & "\"")
@ -1098,23 +1098,23 @@ proc nodeToXml(n: PNode, indent: int = 0): string =
of ElementNode: of ElementNode:
result.add(nodeToXml(i, indent + 2)) result.add(nodeToXml(i, indent + 2))
of TextNode: of TextNode:
result.add(repeatChar(indent * 2, ' ')) result.add(spaces(indent * 2))
result.add(addEscaped(i.nodeValue)) result.add(addEscaped(i.nodeValue))
of CDataSectionNode: of CDataSectionNode:
result.add(repeatChar(indent * 2, ' ')) result.add(spaces(indent * 2))
result.add("<![CDATA[" & i.nodeValue & "]]>") result.add("<![CDATA[" & i.nodeValue & "]]>")
of ProcessingInstructionNode: of ProcessingInstructionNode:
result.add(repeatChar(indent * 2, ' ')) result.add(spaces(indent * 2))
result.add("<?" & PProcessingInstruction(i).target & " " & result.add("<?" & PProcessingInstruction(i).target & " " &
PProcessingInstruction(i).data & " ?>") PProcessingInstruction(i).data & " ?>")
of CommentNode: of CommentNode:
result.add(repeatChar(indent * 2, ' ')) result.add(spaces(indent * 2))
result.add("<!-- " & i.nodeValue & " -->") result.add("<!-- " & i.nodeValue & " -->")
else: else:
continue continue
result.add("\n") result.add("\n")
# Add the ending tag - </tag> # Add the ending tag - </tag>
result.add(repeatChar(indent, ' ') & "</" & n.nodeName & ">") result.add(spaces(indent) & "</" & n.nodeName & ">")
proc `$`*(doc: PDocument): string = proc `$`*(doc: PDocument): string =
## Converts a PDocument object into a string representation of it's XML ## Converts a PDocument object into a string representation of it's XML

View file

@ -89,7 +89,7 @@ type
SomeOrdinal* = int|int8|int16|int32|int64|bool|enum|uint8|uint16|uint32 SomeOrdinal* = int|int8|int16|int32|int64|bool|enum|uint8|uint16|uint32
## type class matching all ordinal types; however this includes enums with ## type class matching all ordinal types; however this includes enums with
## holes. ## holes.
SomeReal* = float|float32|float64 SomeReal* = float|float32|float64
## type class matching all floating point number types ## 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 ## 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 ## the object! This means that for each object of type `T` the finalizer
## will be called! ## will be called!
proc reset*[T](obj: var T) {.magic: "Reset", noSideEffect.} proc reset*[T](obj: var T) {.magic: "Reset", noSideEffect.}
## resets an object `obj` to its initial (binary zero) value. This needs to ## resets an object `obj` to its initial (binary zero) value. This needs to
## be called before any possible `object branch transition`:idx:. ## be called before any possible `object branch transition`:idx:.
@ -348,7 +348,7 @@ type
## This field is filled automatically in the ## This field is filled automatically in the
## ``raise`` statement. ## ``raise`` statement.
msg* {.exportc: "message".}: string ## the exception's message. Not msg* {.exportc: "message".}: string ## the exception's message. Not
## providing an exception message ## providing an exception message
## is bad style. ## is bad style.
trace: string trace: string
@ -483,7 +483,7 @@ type
E_Base: Exception, ESystem: SystemError, EIO: IOError, E_Base: Exception, ESystem: SystemError, EIO: IOError,
EOS: OSError, EInvalidLibrary: LibraryError, EOS: OSError, EInvalidLibrary: LibraryError,
EResourceExhausted: ResourceExhaustedError, EResourceExhausted: ResourceExhaustedError,
EArithmetic: ArithmeticError, EDivByZero: DivByZeroError, EArithmetic: ArithmeticError, EDivByZero: DivByZeroError,
EOverflow: OverflowError, EAccessViolation: AccessViolationError, EOverflow: OverflowError, EAccessViolation: AccessViolationError,
EAssertionFailed: AssertionError, EInvalidValue: ValueError, EAssertionFailed: AssertionError, EInvalidValue: ValueError,
@ -494,7 +494,7 @@ type
EInvalidObjectAssignment: ObjectAssignmentError, EInvalidObjectAssignment: ObjectAssignmentError,
EInvalidObjectConversion: ObjectConversionError, EInvalidObjectConversion: ObjectConversionError,
EDeadThread: DeadThreadError, EDeadThread: DeadThreadError,
EFloatInexact: FloatInexactError, EFloatInexact: FloatInexactError,
EFloatUnderflow: FloatUnderflowError, EFloatUnderflow: FloatUnderflowError,
EFloatingPoint: FloatingPointError, EFloatingPoint: FloatingPointError,
EFloatInvalidOp: FloatInvalidOpError, EFloatInvalidOp: FloatInvalidOpError,
@ -511,11 +511,11 @@ proc sizeof*[T](x: T): Natural {.magic: "SizeOf", noSideEffect.}
proc `<`*[T](x: Ordinal[T]): T {.magic: "UnaryLt", noSideEffect.} proc `<`*[T](x: Ordinal[T]): T {.magic: "UnaryLt", noSideEffect.}
## unary ``<`` that can be used for nice looking excluding ranges: ## unary ``<`` that can be used for nice looking excluding ranges:
## ##
## .. code-block:: nim ## .. code-block:: nim
## for i in 0 .. <10: echo i ## 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.} 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 ## 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 ## 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 ## exist, ``EOutOfRange`` is raised or a compile time error occurs. This is a
## short notation for: ``x = pred(x, y)``. ## short notation for: ``x = pred(x, y)``.
proc newSeq*[T](s: var seq[T], len: int) {.magic: "NewSeq", noSideEffect.} proc newSeq*[T](s: var seq[T], len: int) {.magic: "NewSeq", noSideEffect.}
## creates a new sequence of type ``seq[T]`` with length ``len``. ## creates a new sequence of type ``seq[T]`` with length ``len``.
## This is equivalent to ``s = @[]; setlen(s, len)``, but more ## 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.} proc toU8*(x: int): int8 {.magic: "ToU8", noSideEffect.}
## treats `x` as unsigned and converts it to a byte by taking the last 8 bits ## 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.} proc toU16*(x: int): int16 {.magic: "ToU16", noSideEffect.}
## treats `x` as unsigned and converts it to an ``int16`` by taking the last ## treats `x` as unsigned and converts it to an ``int16`` by taking the last
## 16 bits from `x`. ## 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. ## The result is truncated to fit into the result.
## This implements modulo arithmetic. ## This implements modulo arithmetic.
## No overflow errors are possible. ## No overflow errors are possible.
proc `<=%` *(x, y: IntMax32): bool {.magic: "LeU", noSideEffect.} proc `<=%` *(x, y: IntMax32): bool {.magic: "LeU", noSideEffect.}
proc `<=%` *(x, y: int64): bool {.magic: "LeU64", noSideEffect.} proc `<=%` *(x, y: int64): bool {.magic: "LeU64", noSideEffect.}
## treats `x` and `y` as unsigned and compares them. ## 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.} proc `is` *[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.}
## Checks if T is of the same type as S ## Checks if T is of the same type as S
## ##
## .. code-block:: Nim ## .. code-block:: Nim
## proc test[T](a: T): int = ## proc test[T](a: T): int =
## when (T is 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] {. proc `@` * [IDX, T](a: array[IDX, T]): seq[T] {.
magic: "ArrToSeq", nosideeffect.} magic: "ArrToSeq", nosideeffect.}
## turns an array into a sequence. This most often useful for constructing ## 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]``. ## ``seq[int]``, while ``[1, 2, 3]`` has the type ``array[0..2, int]``.
proc setLen*[T](s: var seq[T], newlen: 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. ## ``T`` may be any sequence type.
## If the current length is greater than the new length, ## If the current length is greater than the new length,
## ``s`` will be truncated. `s` cannot be nil! To initialize a sequence with ## ``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) {. proc setLen*(s: var string, newlen: int) {.
magic: "SetLengthStr", noSideEffect.} magic: "SetLengthStr", noSideEffect.}
## sets the length of `s` to `newlen`. ## sets the length of `s` to `newlen`.
## If the current length is greater than the new length, ## If the current length is greater than the new length,
## ``s`` will be truncated. `s` cannot be nil! To initialize a string with ## ``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 {. proc newString*(len: int): string {.
magic: "NewString", importc: "mnewString", noSideEffect.} magic: "NewString", importc: "mnewString", noSideEffect.}
@ -953,7 +953,7 @@ proc newString*(len: int): string {.
proc newStringOfCap*(cap: int): string {. proc newStringOfCap*(cap: int): string {.
magic: "NewStringOfCap", importc: "rawNewString", noSideEffect.} magic: "NewStringOfCap", importc: "rawNewString", noSideEffect.}
## returns a new string of length ``0`` but with capacity `cap`.This ## 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``. ## be achieved with the ``&`` operator or with ``add``.
proc `&` * (x: string, y: char): string {. proc `&` * (x: string, y: char): string {.
@ -982,7 +982,7 @@ proc `&` * (x: char, y: string): string {.
## assert('a' & "bc" == "abc") ## assert('a' & "bc" == "abc")
# implementation note: These must all have the same magic value "ConStrStr" so # 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.} proc add*(x: var string, y: char) {.magic: "AppendStrCh", noSideEffect.}
## Appends `y` to `x` in place ## 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: ## can be used to determine an on|off compile-time option. Example:
## ##
## .. code-block:: nim ## .. code-block:: nim
## when compileOption("floatchecks"): ## when compileOption("floatchecks"):
## echo "compiled with floating point NaN and Inf checks" ## echo "compiled with floating point NaN and Inf checks"
proc compileOption*(option, arg: string): bool {. proc compileOption*(option, arg: string): bool {.
magic: "CompileOptionArg", noSideEffect.} magic: "CompileOptionArg", noSideEffect.}
## can be used to determine an enum compile-time option. Example: ## can be used to determine an enum compile-time option. Example:
## ##
## .. code-block:: nim ## .. 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" ## echo "compiled with optimization for size and uses Boehm's GC"
const const
@ -1056,16 +1056,16 @@ const
taintMode = compileOption("taintmode") taintMode = compileOption("taintmode")
when 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 ## is `tainted`:idx:. It is an alias for
## ``string`` if the taint mode is not ## ``string`` if the taint mode is not
## turned on. Use the ``-d:taintMode`` ## turned on. Use the ``-d:taintMode``
## command line switch to turn the taint ## command line switch to turn the taint
## mode on. ## mode on.
proc len*(s: TaintedString): int {.borrow.} proc len*(s: TaintedString): int {.borrow.}
else: 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 ## is `tainted`:idx:. It is an alias for
## ``string`` if the taint mode is not ## ``string`` if the taint mode is not
## turned on. Use the ``-d:taintMode`` ## 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".} proc shallowCopy*[T](x: var T, y: T) {.noSideEffect, magic: "ShallowCopy".}
## use this instead of `=` for a `shallow copy`:idx:. The shallow copy ## use this instead of `=` for a `shallow copy`:idx:. The shallow copy
## only changes the semantics for sequences and strings (and types which ## 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 ## is a reason why the default assignment does a deep copy of sequences
## and strings. ## 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`. ## deletes the item at index `i` by putting ``x[high(x)]`` into position `i`.
## This is an O(1) operation. ## This is an O(1) operation.
let xl = x.len let xl = x.len
shallowCopy(x[i], x[xl-1]) shallowCopy(x[i], x[xl-1])
setLen(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. ## deletes the item at index `i` by moving ``x[i+1..]`` by one position.
## This is an O(n) operation. ## This is an O(n) operation.
let xl = x.len 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) 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`. ## inserts `item` into `x` at position `i`.
let xl = x.len let xl = x.len
setLen(x, xl+1) 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 ## This is binary compatible to the type ``char**`` in *C*. The array's
## high value is large enough to disable bounds checking in practice. ## high value is large enough to disable bounds checking in practice.
## Use `cstringArrayToSeq` to convert it into a ``seq[string]``. ## Use `cstringArrayToSeq` to convert it into a ``seq[string]``.
PFloat32* = ptr float32 ## an alias for ``ptr float32`` PFloat32* = ptr float32 ## an alias for ``ptr float32``
PFloat64* = ptr float64 ## an alias for ``ptr float64`` PFloat64* = ptr float64 ## an alias for ``ptr float64``
PInt64* = ptr int64 ## an alias for ``ptr int64`` PInt64* = ptr int64 ## an alias for ``ptr int64``
@ -1280,7 +1280,7 @@ proc addQuitProc*(QuitProc: proc() {.noconv.}) {.
proc copy*(s: string, first = 0): string {. proc copy*(s: string, first = 0): string {.
magic: "CopyStr", importc: "copyStr", noSideEffect, deprecated.} magic: "CopyStr", importc: "copyStr", noSideEffect, deprecated.}
proc copy*(s: string, first, last: int): string {. proc copy*(s: string, first, last: int): string {.
magic: "CopyStrLast", importc: "copyStrLast", noSideEffect, magic: "CopyStrLast", importc: "copyStrLast", noSideEffect,
deprecated.} deprecated.}
## copies a slice of `s` into a new string and returns this new ## copies a slice of `s` into a new string and returns this new
## string. The bounds `first` and `last` denote the indices of ## 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! ## The allocated memory belongs to its allocating thread!
## Use `createShared` to allocate from a shared heap. ## Use `createShared` to allocate from a shared heap.
cast[ptr T](alloc0(T.sizeof * size)) 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.} benign.}
## grows or shrinks a given memory block. If p is **nil** then a new ## 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 ## 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 ## ``realloc``. This procedure is dangerous! If one forgets to
## free the memory a leak occurs; if one tries to access freed ## free the memory a leak occurs; if one tries to access freed
## memory (or just freeing it twice!) a core dump may happen ## 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! ## The freed memory must belong to its allocating thread!
## Use `deallocShared` to deallocate from a shared heap. ## Use `deallocShared` to deallocate from a shared heap.
proc free*[T](p: ptr T) {.inline, benign.} = 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 ## allocates a new memory block on the shared heap with at
## least ``size`` bytes. The block has to be freed with ## least ``size`` bytes. The block has to be freed with
## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block ## ``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! ## undefined behaviour!
proc createSharedU*(T: typedesc, size = 1.Positive): ptr T {.inline, proc createSharedU*(T: typedesc, size = 1.Positive): ptr T {.inline,
benign.} = benign.} =
## 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 ## least ``T.sizeof * size`` bytes. The block has to be freed with
## ``resizeShared(block, 0)`` or ``freeShared(block)``. The block ## ``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! ## undefined behaviour!
cast[ptr T](allocShared(T.sizeof * size)) cast[ptr T](allocShared(T.sizeof * size))
proc allocShared0*(size: int): pointer {.noconv, rtl, benign.} 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 ## least ``size`` bytes. The block has to be freed with
## ``reallocShared(block, 0)`` or ``deallocShared(block)``. ## ``reallocShared(block, 0)`` or ``deallocShared(block)``.
## The block is initialized with all bytes ## The block is initialized with all bytes
## containing zero, so it is somewhat safer than ``allocShared``. ## containing zero, so it is somewhat safer than ``allocShared``.
proc createShared*(T: typedesc, size = 1.Positive): ptr T {.inline.} = 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 ## least ``T.sizeof * size`` bytes. The block has to be freed with
## ``resizeShared(block, 0)`` or ``freeShared(block)``. ## ``resizeShared(block, 0)`` or ``freeShared(block)``.
## The block is initialized with all bytes ## The block is initialized with all bytes
## containing zero, so it is somewhat safer than ``createSharedU``. ## containing zero, so it is somewhat safer than ``createSharedU``.
cast[ptr T](allocShared0(T.sizeof * size)) 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.} benign.}
## grows or shrinks a given memory block on the heap. If p is **nil** ## 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 ## then a new memory block is returned. In either way the block has at
@ -1525,7 +1525,7 @@ const
NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch
## is the version of Nim as a string. ## is the version of Nim as a string.
{.deprecated: [TEndian: Endianness, NimrodVersion: NimVersion, {.deprecated: [TEndian: Endianness, NimrodVersion: NimVersion,
NimrodMajor: NimMajor, NimrodMinor: NimMinor, NimrodPatch: NimPatch].} NimrodMajor: NimMajor, NimrodMinor: NimMinor, NimrodPatch: NimPatch].}
# GC interface: # GC interface:
@ -1805,7 +1805,7 @@ proc `==` *[I, T](x, y: array[I, T]): bool =
return return
result = true 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 ## 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 ## a fixed length array into a sequence as it always copies every element
## of `a`. ## of `a`.
@ -1853,7 +1853,7 @@ when not defined(NimrodVM):
else: else:
proc seqToPtr[T](x: seq[T]): pointer {.asmNoStackFrame, nosideeffect.} = proc seqToPtr[T](x: seq[T]): pointer {.asmNoStackFrame, nosideeffect.} =
asm """return `x`""" asm """return `x`"""
proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} = proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} =
## Generic equals operator for sequences: relies on a equals operator for ## Generic equals operator for sequences: relies on a equals operator for
## the element type `T`. ## the element type `T`.
@ -1879,7 +1879,7 @@ proc contains*[T](a: openArray[T], item: T): bool {.inline.}=
## for ``find(a, item) >= 0``. ## for ``find(a, item) >= 0``.
return 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 ## returns the last item of `s` and decreases ``s.len`` by one. This treats
## `s` as a stack and implements the common *pop* operation. ## `s` as a stack and implements the common *pop* operation.
var L = s.len-1 var L = s.len-1
@ -1941,7 +1941,7 @@ iterator fields*[T: tuple|object](x: T): RootObj {.
iterator fields*[S:tuple|object, T:tuple|object](x: S, y: T): tuple[a,b: expr] {. iterator fields*[S:tuple|object, T:tuple|object](x: S, y: T): tuple[a,b: expr] {.
magic: "Fields", noSideEffect.} magic: "Fields", noSideEffect.}
## iterates over every field of `x` and `y`. ## 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 ## The current implementation also has a bug that affects symbol binding
## in the loop body. ## in the loop body.
iterator fieldPairs*[T: tuple|object](x: T): RootObj {. iterator fieldPairs*[T: tuple|object](x: T): RootObj {.
@ -1982,18 +1982,18 @@ iterator fieldPairs*[S: tuple|object, T: tuple|object](x: S, y: T): tuple[
a, b: expr] {. a, b: expr] {.
magic: "FieldPairs", noSideEffect.} magic: "FieldPairs", noSideEffect.}
## iterates over every field of `x` and `y`. ## 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 ## The current implementation also has a bug that affects symbol binding
## in the loop body. ## 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 ## generic ``==`` operator for tuples that is lifted from the components
## of `x` and `y`. ## of `x` and `y`.
for a, b in fields(x, y): for a, b in fields(x, y):
if a != b: return false if a != b: return false
return true 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 ## generic ``<=`` operator for tuples that is lifted from the components
## of `x` and `y`. This implementation uses `cmp`. ## of `x` and `y`. This implementation uses `cmp`.
for a, b in fields(x, y): for a, b in fields(x, y):
@ -2002,7 +2002,7 @@ proc `<=`*[T: tuple](x, y: T): bool =
if c > 0: return false if c > 0: return false
return true 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 ## generic ``<`` operator for tuples that is lifted from the components
## of `x` and `y`. This implementation uses `cmp`. ## of `x` and `y`. This implementation uses `cmp`.
for a, b in fields(x, y): for a, b in fields(x, y):
@ -2011,7 +2011,7 @@ proc `<`*[T: tuple](x, y: T): bool =
if c > 0: return false if c > 0: return false
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 ## generic ``$`` operator for tuples that is lifted from the components
## of `x`. Example: ## of `x`. Example:
## ##
@ -2021,13 +2021,13 @@ proc `$`*[T: tuple|object](x: T): string =
result = "(" result = "("
var firstElement = true var firstElement = true
for name, value in fieldPairs(x): for name, value in fieldPairs(x):
if not(firstElement): result.add(", ") if not firstElement: result.add(", ")
result.add(name) result.add(name)
result.add(": ") result.add(": ")
result.add($value) result.add($value)
firstElement = false firstElement = false
result.add(")") result.add(")")
proc collectionToString[T](x: T, b, e: string): string = proc collectionToString[T](x: T, b, e: string): string =
result = b result = b
var firstElement = true var firstElement = true
@ -2037,7 +2037,7 @@ proc collectionToString[T](x: T, b, e: string): string =
firstElement = false firstElement = false
result.add(e) 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 ## generic ``$`` operator for sets that is lifted from the components
## of `x`. Example: ## of `x`. Example:
## ##
@ -2045,7 +2045,7 @@ proc `$`*[T](x: set[T]): string =
## ${23, 45} == "{23, 45}" ## ${23, 45} == "{23, 45}"
collectionToString(x, "{", "}") collectionToString(x, "{", "}")
proc `$`*[T](x: seq[T]): string = proc `$`*[T](x: seq[T]): string =
## generic ``$`` operator for seqs that is lifted from the components ## generic ``$`` operator for seqs that is lifted from the components
## of `x`. Example: ## of `x`. Example:
## ##
@ -2056,7 +2056,7 @@ proc `$`*[T](x: seq[T]): string =
when false: when false:
# causes bootstrapping to fail as we use array of chars and cstring should # causes bootstrapping to fail as we use array of chars and cstring should
# match better ... # match better ...
proc `$`*[T, IDX](x: array[IDX, T]): string = proc `$`*[T, IDX](x: array[IDX, T]): string =
collectionToString(x, "[", "]") collectionToString(x, "[", "]")
# ----------------- GC interface --------------------------------------------- # ----------------- GC interface ---------------------------------------------
@ -2098,14 +2098,14 @@ when not defined(nimrodVM) and hostOS != "standalone":
proc GC_getStatistics*(): string {.rtl, benign.} proc GC_getStatistics*(): string {.rtl, benign.}
## returns an informative string about the GC's activity. This may be useful ## returns an informative string about the GC's activity. This may be useful
## for tweaking. ## for tweaking.
proc GC_ref*[T](x: ref T) {.magic: "GCref", benign.} proc GC_ref*[T](x: ref T) {.magic: "GCref", benign.}
proc GC_ref*[T](x: seq[T]) {.magic: "GCref", benign.} proc GC_ref*[T](x: seq[T]) {.magic: "GCref", benign.}
proc GC_ref*(x: string) {.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 ## 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`, ## 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: ref T) {.magic: "GCunref", benign.}
proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", benign.} proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", benign.}
proc GC_unref*(x: string) {.magic: "GCunref", benign.} proc GC_unref*(x: string) {.magic: "GCunref", benign.}
@ -2141,19 +2141,19 @@ var
## application code should never set this hook! You better know what you ## application code should never set this hook! You better know what you
## do when setting this. If ``localRaiseHook`` returns false, the exception ## do when setting this. If ``localRaiseHook`` returns false, the exception
## is caught and does not propagate further through the call stack. ## is caught and does not propagate further through the call stack.
outOfMemHook*: proc () {.nimcall, tags: [], benign.} 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 ## in case of an `out of memory`:idx: event. The standard handler
## writes an error message and terminates the program. `outOfMemHook` can ## writes an error message and terminates the program. `outOfMemHook` can
## be used to raise an exception in case of OOM like so: ## be used to raise an exception in case of OOM like so:
## ##
## .. code-block:: nim ## .. code-block:: nim
## ##
## var gOutOfMem: ref EOutOfMemory ## var gOutOfMem: ref EOutOfMemory
## new(gOutOfMem) # need to be allocated *before* OOM really happened! ## new(gOutOfMem) # need to be allocated *before* OOM really happened!
## gOutOfMem.msg = "out of memory" ## gOutOfMem.msg = "out of memory"
## ##
## proc handleOOM() = ## proc handleOOM() =
## raise gOutOfMem ## raise gOutOfMem
## ##
@ -2210,7 +2210,7 @@ proc echo*(x: varargs[expr, `$`]) {.magic: "Echo", tags: [WriteIOEffect],
## <manual.html#nosideeffect-pragma>`_ you can use `debugEcho <#debugEcho>`_ ## <manual.html#nosideeffect-pragma>`_ you can use `debugEcho <#debugEcho>`_
## instead. ## instead.
proc debugEcho*(x: varargs[expr, `$`]) {.magic: "Echo", noSideEffect, proc debugEcho*(x: varargs[expr, `$`]) {.magic: "Echo", noSideEffect,
tags: [], raises: [].} tags: [], raises: [].}
## Same as `echo <#echo>`_, but as a special semantic rule, ``debugEcho`` ## 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 ## pretends to be free of side effects, so that it can be used for debugging
@ -2262,7 +2262,7 @@ proc abs*(x: int16): int16 {.magic: "AbsI", noSideEffect.} =
proc abs*(x: int32): int32 {.magic: "AbsI", noSideEffect.} = proc abs*(x: int32): int32 {.magic: "AbsI", noSideEffect.} =
if x < 0: -x else: x if x < 0: -x else: x
proc abs*(x: int64): int64 {.magic: "AbsI64", noSideEffect.} = 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 ## is -MININT for its type), an overflow exception is thrown (if overflow
## checking is turned on). ## checking is turned on).
if x < 0: -x else: x if x < 0: -x else: x
@ -2318,14 +2318,14 @@ when not defined(JS): #and not defined(NimrodVM):
# we use binary mode in Windows: # we use binary mode in Windows:
setmode(fileno(c_stdin), O_BINARY) setmode(fileno(c_stdin), O_BINARY)
setmode(fileno(c_stdout), O_BINARY) setmode(fileno(c_stdout), O_BINARY)
when defined(endb): when defined(endb):
proc endbStep() proc endbStep()
# ----------------- IO Part ------------------------------------------------ # ----------------- IO Part ------------------------------------------------
when hostOS != "standalone": when hostOS != "standalone":
type type
CFile {.importc: "FILE", header: "<stdio.h>", CFile {.importc: "FILE", header: "<stdio.h>",
final, incompletestruct.} = object final, incompletestruct.} = object
File* = ptr CFile ## The type representing a file handle. File* = ptr CFile ## The type representing a file handle.
@ -2375,9 +2375,9 @@ when not defined(JS): #and not defined(NimrodVM):
## Creates a ``TFile`` from a `filehandle` with given `mode`. ## Creates a ``TFile`` from a `filehandle` with given `mode`.
## ##
## Default mode is readonly. Returns true iff the file could be opened. ## Default mode is readonly. Returns true iff the file could be opened.
proc open*(filename: string, 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`. ## Opens a file named `filename` with given `mode`.
## ##
## Default mode is readonly. Raises an ``IO`` exception if the file ## Default mode is readonly. Raises an ``IO`` exception if the file
@ -2387,7 +2387,7 @@ when not defined(JS): #and not defined(NimrodVM):
proc reopen*(f: File, filename: string, mode: FileMode = fmRead): bool {. proc reopen*(f: File, filename: string, mode: FileMode = fmRead): bool {.
tags: [], benign.} 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` ## is often used to redirect the `stdin`, `stdout` or `stderr`
## file variables. ## file variables.
## ##
@ -2398,7 +2398,7 @@ when not defined(JS): #and not defined(NimrodVM):
proc endOfFile*(f: File): bool {.tags: [], benign.} proc endOfFile*(f: File): bool {.tags: [], benign.}
## Returns true iff `f` is at the end. ## Returns true iff `f` is at the end.
proc readChar*(f: File): char {. proc readChar*(f: File): char {.
importc: "fgetc", header: "<stdio.h>", tags: [ReadIOEffect].} importc: "fgetc", header: "<stdio.h>", tags: [ReadIOEffect].}
## Reads a single character from the stream `f`. ## Reads a single character from the stream `f`.
@ -2411,7 +2411,7 @@ when not defined(JS): #and not defined(NimrodVM):
## ##
## Raises an IO exception in case of an error. It is an error if the ## 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. ## current file position is not at the beginning of the file.
proc readFile*(filename: string): TaintedString {.tags: [ReadIOEffect], benign.} proc readFile*(filename: string): TaintedString {.tags: [ReadIOEffect], benign.}
## Opens a file named `filename` for reading. ## Opens a file named `filename` for reading.
## ##
@ -2440,8 +2440,8 @@ when not defined(JS): #and not defined(NimrodVM):
## reads a line of text from the file `f`. May throw an IO exception. ## 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 ## A line of text may be delimited by ``CR``, ``LF`` or
## ``CRLF``. The newline character(s) are not part of the returned string. ## ``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.} benign.}
## reads a line of text from the file `f` into `line`. `line` must not be ## reads a line of text from the file `f` into `line`. `line` must not be
## ``nil``! May throw an IO exception. ## ``nil``! May throw an IO exception.
@ -2450,7 +2450,7 @@ when not defined(JS): #and not defined(NimrodVM):
## Returns ``false`` if the end of the file has been reached, ``true`` ## Returns ``false`` if the end of the file has been reached, ``true``
## otherwise. If ``false`` is returned `line` contains no new data. ## 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.} tags: [WriteIOEffect], benign.}
## writes the values `x` to `f` and then writes "\n". ## writes the values `x` to `f` and then writes "\n".
## May throw an IO exception. ## May throw an IO exception.
@ -2544,11 +2544,11 @@ when not defined(JS): #and not defined(NimrodVM):
dealloc(a) dealloc(a)
when not defined(NimrodVM): 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.} discardable, benign.}
## atomic increment of `memLoc`. Returns the value after the operation. ## 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.} discardable, benign.}
## atomic decrement of `memLoc`. Returns the value after the operation. ## atomic decrement of `memLoc`. Returns the value after the operation.
@ -2562,7 +2562,7 @@ when not defined(JS): #and not defined(NimrodVM):
context: C_JmpBuf context: C_JmpBuf
hasRaiseAction: bool hasRaiseAction: bool
raiseAction: proc (e: ref Exception): bool {.closure.} raiseAction: proc (e: ref Exception): bool {.closure.}
when declared(initAllocator): when declared(initAllocator):
initAllocator() initAllocator()
when hasThreadSupport: when hasThreadSupport:
@ -2576,7 +2576,7 @@ when not defined(JS): #and not defined(NimrodVM):
proc setControlCHook*(hook: proc () {.noconv.} not nil) proc setControlCHook*(hook: proc () {.noconv.} not nil)
## allows you to override the behaviour of your application when CTRL+C ## allows you to override the behaviour of your application when CTRL+C
## is pressed. Only one such hook is supported. ## is pressed. Only one such hook is supported.
proc writeStackTrace*() {.tags: [WriteIOEffect].} proc writeStackTrace*() {.tags: [WriteIOEffect].}
## writes the current stack trace to ``stderr``. This is only works ## writes the current stack trace to ``stderr``. This is only works
## for debug builds. ## for debug builds.
@ -2587,20 +2587,20 @@ when not defined(JS): #and not defined(NimrodVM):
proc getStackTrace*(e: ref Exception): string proc getStackTrace*(e: ref Exception): string
## gets the stack trace associated with `e`, which is the stack that ## gets the stack trace associated with `e`, which is the stack that
## lead to the ``raise`` statement. This only works for debug builds. ## lead to the ``raise`` statement. This only works for debug builds.
{.push stack_trace: off, profiler:off.} {.push stack_trace: off, profiler:off.}
when hostOS == "standalone": when hostOS == "standalone":
include "system/embedded" include "system/embedded"
else: else:
include "system/excpt" include "system/excpt"
include "system/chcks" include "system/chcks"
# we cannot compile this with stack tracing on # we cannot compile this with stack tracing on
# as it would recurse endlessly! # as it would recurse endlessly!
include "system/arithm" include "system/arithm"
{.pop.} # stack trace {.pop.} # stack trace
{.pop.} # stack trace {.pop.} # stack trace
when hostOS != "standalone" and not defined(NimrodVM): when hostOS != "standalone" and not defined(NimrodVM):
include "system/dyncalls" include "system/dyncalls"
when not defined(NimrodVM): when not defined(NimrodVM):
@ -2608,7 +2608,7 @@ when not defined(JS): #and not defined(NimrodVM):
const const
GenericSeqSize = (2 * sizeof(int)) GenericSeqSize = (2 * sizeof(int))
proc getDiscriminant(aa: pointer, n: ptr TNimNode): int = proc getDiscriminant(aa: pointer, n: ptr TNimNode): int =
sysAssert(n.kind == nkCase, "getDiscriminant: node != nkCase") sysAssert(n.kind == nkCase, "getDiscriminant: node != nkCase")
var d: int var d: int
@ -2728,7 +2728,7 @@ when not defined(JS): #and not defined(NimrodVM):
## process(value) ## process(value)
## else: ## else:
## echo "Value too big!" ## echo "Value too big!"
proc unlikely*(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.} proc unlikely*(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.}
## Hints the optimizer that `val` is likely going to be false. ## Hints the optimizer that `val` is likely going to be false.
## ##
@ -2742,7 +2742,7 @@ when not defined(JS): #and not defined(NimrodVM):
## echo "Value too big!" ## echo "Value too big!"
## else: ## else:
## process(value) ## process(value)
proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} = proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} =
## retrieves the raw proc pointer of the closure `x`. This is ## retrieves the raw proc pointer of the closure `x`. This is
## useful for interfacing closures with C. ## useful for interfacing closures with C.
@ -2774,7 +2774,7 @@ elif defined(JS):
proc GC_enableMarkAndSweep() = discard proc GC_enableMarkAndSweep() = discard
proc GC_disableMarkAndSweep() = discard proc GC_disableMarkAndSweep() = discard
proc GC_getStatistics(): string = return "" proc GC_getStatistics(): string = return ""
proc getOccupiedMem(): int = return -1 proc getOccupiedMem(): int = return -1
proc getFreeMem(): int = return -1 proc getFreeMem(): int = return -1
proc getTotalMem(): int = return -1 proc getTotalMem(): int = return -1
@ -2797,7 +2797,7 @@ elif defined(JS):
if x == y: return 0 if x == y: return 0
if x < y: return -1 if x < y: return -1
return 1 return 1
when defined(nimffi): when defined(nimffi):
include "system/sysio" include "system/sysio"
@ -2831,14 +2831,14 @@ template spliceImpl(s, a, L, b: expr): stmt {.immediate.} =
# cut down: # cut down:
setLen(s, newLen) setLen(s, newLen)
# fill the hole: # 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": when hostOS != "standalone":
proc `[]`*(s: string, x: Slice[int]): string {.inline.} = proc `[]`*(s: string, x: Slice[int]): string {.inline.} =
## slice operation for strings. Negative indexes are supported. ## slice operation for strings. Negative indexes are supported.
result = s.substr(x.a-|s, x.b-|s) 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 ## slice assignment for strings. Negative indexes are supported. If
## ``b.len`` is not exactly the number of elements that are referred to ## ``b.len`` is not exactly the number of elements that are referred to
## by `x`, a `splice`:idx: is performed: ## by `x`, a `splice`:idx: is performed:
@ -2880,7 +2880,7 @@ proc `[]`*[Idx, T](a: array[Idx, T], x: Slice[Idx]): seq[T] =
var L = ord(x.b) - ord(x.a) + 1 var L = ord(x.b) - ord(x.a) + 1
newSeq(result, L) newSeq(result, L)
var j = x.a var j = x.a
for i in 0.. <L: for i in 0.. <L:
result[i] = a[j] result[i] = a[j]
inc(j) inc(j)
@ -2890,23 +2890,23 @@ proc `[]=`*[Idx, T](a: var array[Idx, T], x: Slice[Idx], b: openArray[T]) =
var L = ord(x.b) - ord(x.a) + 1 var L = ord(x.b) - ord(x.a) + 1
if L == b.len: if L == b.len:
var j = x.a var j = x.a
for i in 0 .. <L: for i in 0 .. <L:
a[j] = b[i] a[j] = b[i]
inc(j) inc(j)
else: else:
sysFatal(RangeError, "different lengths for slice assignment") sysFatal(RangeError, "different lengths for slice assignment")
proc `[]`*[T](s: seq[T], x: Slice[int]): seq[T] = proc `[]`*[T](s: seq[T], x: Slice[int]): seq[T] =
## slice operation for sequences. Negative indexes are supported. ## slice operation for sequences. Negative indexes are supported.
var a = x.a-|s var a = x.a-|s
var L = x.b-|s - a + 1 var L = x.b-|s - a + 1
newSeq(result, L) newSeq(result, L)
for i in 0.. <L: result[i] = s[i + a] for i in 0.. <L: result[i] = s[i + a]
proc `[]=`*[T](s: var seq[T], x: Slice[int], b: openArray[T]) = proc `[]=`*[T](s: var seq[T], x: Slice[int], b: openArray[T]) =
## slice assignment for sequences. Negative indexes are supported. If ## slice assignment for sequences. Negative indexes are supported. If
## ``b.len`` is not exactly the number of elements that are referred to ## ``b.len`` is not exactly the number of elements that are referred to
## by `x`, a `splice`:idx: is performed. ## by `x`, a `splice`:idx: is performed.
var a = x.a-|s var a = x.a-|s
var L = x.b-|s - a + 1 var L = x.b-|s - a + 1
if L == b.len: if L == b.len:
@ -2937,7 +2937,7 @@ proc staticExec*(command: string, input = ""): string {.
## to the executed program. ## to the executed program.
## ##
## .. code-block:: nim ## .. code-block:: nim
## const buildInfo = "Revision " & staticExec("git rev-parse HEAD") & ## const buildInfo = "Revision " & staticExec("git rev-parse HEAD") &
## "\nCompiled on " & staticExec("uname -v") ## "\nCompiled on " & staticExec("uname -v")
## ##
## `gorge <#gorge>`_ is an alias for ``staticExec``. Note that you can use ## `gorge <#gorge>`_ is an alias for ``staticExec``. Note that you can use
@ -2979,7 +2979,7 @@ proc `&=`* (x: var string, y: string) {.magic: "AppendStrStr", noSideEffect.}
proc astToStr*[T](x: T): string {.magic: "AstToStr", noSideEffect.} proc astToStr*[T](x: T): string {.magic: "AstToStr", noSideEffect.}
## converts the AST of `x` into a string representation. This is very useful ## converts the AST of `x` into a string representation. This is very useful
## for debugging. ## for debugging.
proc instantiationInfo*(index = -1, fullPaths = false): tuple[ proc instantiationInfo*(index = -1, fullPaths = false): tuple[
filename: string, line: int] {. magic: "InstantiationInfo", noSideEffect.} filename: string, line: int] {. magic: "InstantiationInfo", noSideEffect.}
## provides access to the compiler's instantiation stack line information. ## provides access to the compiler's instantiation stack line information.
@ -3090,16 +3090,16 @@ template onFailedAssert*(msg: expr, code: stmt): stmt {.dirty, immediate.} =
## Sets an assertion failure handler that will intercept any assert ## Sets an assertion failure handler that will intercept any assert
## statements following `onFailedAssert` in the current lexical scope. ## statements following `onFailedAssert` in the current lexical scope.
## Can be defined multiple times in a single function. ## Can be defined multiple times in a single function.
## ##
## .. code-block:: nim ## .. code-block:: nim
## ##
## proc example(x: int): TErrorCode = ## proc example(x: int): TErrorCode =
## onFailedAssert(msg): ## onFailedAssert(msg):
## log msg ## log msg
## return E_FAIL ## return E_FAIL
## ##
## assert(...) ## assert(...)
## ##
## onFailedAssert(msg): ## onFailedAssert(msg):
## raise newException(EMyException, msg) ## raise newException(EMyException, msg)
## ##
@ -3111,7 +3111,7 @@ template onFailedAssert*(msg: expr, code: stmt): stmt {.dirty, immediate.} =
proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} = proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} =
## marks a sequence `s` as `shallow`:idx:. Subsequent assignments will not ## 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. ## purposes.
when not defined(JS) and not defined(NimrodVM): when not defined(JS) and not defined(NimrodVM):
var s = cast[PGenericSeq](s) var s = cast[PGenericSeq](s)
@ -3119,7 +3119,7 @@ proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} =
proc shallow*(s: var string) {.noSideEffect, inline.} = proc shallow*(s: var string) {.noSideEffect, inline.} =
## marks a string `s` as `shallow`:idx:. Subsequent assignments will not ## 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. ## purposes.
when not defined(JS) and not defined(NimrodVM): when not defined(JS) and not defined(NimrodVM):
var s = cast[PGenericSeq](s) var s = cast[PGenericSeq](s)
@ -3141,13 +3141,13 @@ else:
when false: when false:
template eval*(blk: stmt): stmt = template eval*(blk: stmt): stmt =
## executes a block of code at compile time just as if it was a macro ## 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 ## eval expression
macro payload: stmt {.gensym.} = blk macro payload: stmt {.gensym.} = blk
payload() payload()
when hostOS != "standalone": 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`. ## inserts `item` into `x` at position `i`.
var xl = x.len var xl = x.len
setLen(x, xl+item.len) setLen(x, xl+item.len)

View file

@ -34,9 +34,9 @@ proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode) {.benign.} =
proc copyDeepString(src: NimString): NimString {.inline.} = proc copyDeepString(src: NimString): NimString {.inline.} =
if src != nil: if src != nil:
result = rawNewString(src.space) result = rawNewStringNoInit(src.len)
result.len = 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) = proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) =
var var

View file

@ -1,7 +1,7 @@
# #
# #
# Nim's Runtime Library # Nim's Runtime Library
# (c) Copyright 2013 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -48,7 +48,7 @@ type
TWalkOp = enum TWalkOp = enum
waMarkGlobal, # part of the backup/debug mark&sweep waMarkGlobal, # part of the backup/debug mark&sweep
waMarkPrecise, # 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 waCollectWhite #, waDebug
TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.} TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.}
@ -61,9 +61,9 @@ type
maxThreshold: int # max threshold that has been set maxThreshold: int # max threshold that has been set
maxStackSize: int # max stack size maxStackSize: int # max stack size
maxStackCells: int # max stack cells in ``decStack`` 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 maxPause: int64 # max measured GC pause in nanoseconds
TGcHeap {.final, pure.} = object # this contains the zero count and TGcHeap {.final, pure.} = object # this contains the zero count and
# non-zero count table # non-zero count table
stackBottom: pointer stackBottom: pointer
@ -88,11 +88,11 @@ var
when not defined(useNimRtl): when not defined(useNimRtl):
instantiateForRegion(gch.region) instantiateForRegion(gch.region)
template acquire(gch: TGcHeap) = template acquire(gch: TGcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
acquireSys(HeapLock) acquireSys(HeapLock)
template release(gch: TGcHeap) = template release(gch: TGcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
releaseSys(HeapLock) releaseSys(HeapLock)
@ -117,7 +117,7 @@ proc usrToCell(usr: pointer): PCell {.inline.} =
# convert pointer to userdata to object (=pointer to refcount) # convert pointer to userdata to object (=pointer to refcount)
result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(TCell))) 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 result = ntfAcyclic notin c.typ.flags
proc extGetCellType(c: pointer): PNimType {.compilerproc.} = 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): expr = atomicDec(x, rcIncrement) <% rcIncrement
template `++`(x: expr): stmt = discard atomicInc(x, rcIncrement) template `++`(x: expr): stmt = discard atomicInc(x, rcIncrement)
else: else:
template `--`(x: expr): expr = template `--`(x: expr): expr =
dec(x, rcIncrement) dec(x, rcIncrement)
x <% rcIncrement x <% rcIncrement
template `++`(x: expr): stmt = inc(x, rcIncrement) template `++`(x: expr): stmt = inc(x, rcIncrement)
@ -181,7 +181,7 @@ proc prepareDealloc(cell: PCell) =
(cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell)) (cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell))
dec(gch.recGcLock) 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! # we MUST access gch as a global here, because this crosses DLL boundaries!
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
acquireSys(HeapLock) acquireSys(HeapLock)
@ -211,7 +211,7 @@ proc decRef(c: PCell) {.inline.} =
rtlAddCycleRoot(c) rtlAddCycleRoot(c)
#writeCell("decRef", c) #writeCell("decRef", c)
proc incRef(c: PCell) {.inline.} = proc incRef(c: PCell) {.inline.} =
gcAssert(isAllocatedPtr(gch.region, c), "incRef: interiorPtr") gcAssert(isAllocatedPtr(gch.region, c), "incRef: interiorPtr")
c.refcount = c.refcount +% rcIncrement c.refcount = c.refcount +% rcIncrement
# and not colorMask # and not colorMask
@ -246,12 +246,12 @@ proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} =
dest[] = src dest[] = src
proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerProc, inline.} = 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. # cycle is possible.
if src != nil: if src != nil:
var c = usrToCell(src) var c = usrToCell(src)
++c.refcount ++c.refcount
if dest[] != nil: if dest[] != nil:
var c = usrToCell(dest[]) var c = usrToCell(dest[])
if --c.refcount: if --c.refcount:
rtlAddZCT(c) rtlAddZCT(c)
@ -269,7 +269,7 @@ proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerProc.} =
if cast[int](dest[]) >=% PageSize: decRef(usrToCell(dest[])) if cast[int](dest[]) >=% PageSize: decRef(usrToCell(dest[]))
else: else:
# can't be an interior pointer if it's a stack location! # 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") "stack loc AND interior pointer")
dest[] = src dest[] = src
@ -321,7 +321,7 @@ when useMarkForDebug or useBackupGc:
echo "[GC] cannot register global variable; too many global variables" echo "[GC] cannot register global variable; too many global variables"
quit 1 quit 1
proc cellsetReset(s: var TCellSet) = proc cellsetReset(s: var TCellSet) =
deinit(s) deinit(s)
init(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}: if n.sons[i].typ.kind in {tyRef, tyString, tySequence}:
doOperation(cast[PPointer](d +% n.sons[i].offset)[], op) doOperation(cast[PPointer](d +% n.sons[i].offset)[], op)
else: else:
forAllChildrenAux(cast[pointer](d +% n.sons[i].offset), forAllChildrenAux(cast[pointer](d +% n.sons[i].offset),
n.sons[i].typ, op) n.sons[i].typ, op)
else: else:
forAllSlotsAux(dest, n.sons[i], op) 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. # 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 # 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. # out of this small linear search so that ``newObj`` is not slowed down.
# #
# Slots to try cache hit # Slots to try cache hit
# 1 32% # 1 32%
# 4 59% # 4 59%
@ -461,6 +461,9 @@ proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap): pointer =
{.pop.} {.pop.}
proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
result = rawNewObj(typ, size, gch)
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} = proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
result = rawNewObj(typ, size, gch) result = rawNewObj(typ, size, gch)
zeroMem(result, size) zeroMem(result, size)
@ -481,7 +484,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1") gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
collectCT(gch) collectCT(gch)
sysAssert(allocInv(gch.region), "newObjRC1 after collectCT") sysAssert(allocInv(gch.region), "newObjRC1 after collectCT")
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(TCell))) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(TCell)))
sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc") sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc")
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
@ -510,7 +513,7 @@ proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
cast[PGenericSeq](result).len = len cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len cast[PGenericSeq](result).reserved = len
when defined(memProfiler): nimProfile(size) when defined(memProfiler): nimProfile(size)
proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer = proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
acquire(gch) acquire(gch)
collectCT(gch) collectCT(gch)
@ -522,7 +525,7 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(TCell))) var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(TCell)))
var elemSize = 1 var elemSize = 1
if ol.typ.kind != tyString: elemSize = ol.typ.base.size if ol.typ.kind != tyString: elemSize = ol.typ.base.size
var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
copyMem(res, ol, oldsize + sizeof(TCell)) copyMem(res, ol, oldsize + sizeof(TCell))
zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(TCell)), zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(TCell)),
@ -536,7 +539,7 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
writeCell("growObj new cell", res) writeCell("growObj new cell", res)
gcTrace(ol, csZctFreed) gcTrace(ol, csZctFreed)
gcTrace(res, csAllocated) gcTrace(res, csAllocated)
when reallyDealloc: when reallyDealloc:
sysAssert(allocInv(gch.region), "growObj before dealloc") sysAssert(allocInv(gch.region), "growObj before dealloc")
if ol.refcount shr rcShift <=% 1: if ol.refcount shr rcShift <=% 1:
# free immediately to save space: # free immediately to save space:
@ -580,7 +583,7 @@ proc freeCyclicCell(gch: var TGcHeap, c: PCell) =
prepareDealloc(c) prepareDealloc(c)
gcTrace(c, csCycFreed) gcTrace(c, csCycFreed)
when logGC: writeCell("cycle collector dealloc cell", c) when logGC: writeCell("cycle collector dealloc cell", c)
when reallyDealloc: when reallyDealloc:
sysAssert(allocInv(gch.region), "free cyclic cell") sysAssert(allocInv(gch.region), "free cyclic cell")
rawDealloc(gch.region, c) rawDealloc(gch.region, c)
else: else:
@ -767,7 +770,7 @@ proc collectCycles(gch: var TGcHeap) =
gcAssert isAllocatedPtr(gch.region, c), "addBackStackRoots" gcAssert isAllocatedPtr(gch.region, c), "addBackStackRoots"
gcAssert c.refcount >=% rcIncrement, "addBackStackRoots: dead cell" gcAssert c.refcount >=% rcIncrement, "addBackStackRoots: dead cell"
if canBeCycleRoot(c): if canBeCycleRoot(c):
#if c notin gch.cycleRoots: #if c notin gch.cycleRoots:
inc cycleRootsLen inc cycleRootsLen
incl(gch.cycleRoots, c) incl(gch.cycleRoots, c)
gcAssert c.typ != nil, "addBackStackRoots 2" gcAssert c.typ != nil, "addBackStackRoots 2"
@ -794,12 +797,12 @@ proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} =
add(gch.decStack, cell) add(gch.decStack, cell)
sysAssert(allocInv(gch.region), "gcMark end") sysAssert(allocInv(gch.region), "gcMark end")
proc markThreadStacks(gch: var TGcHeap) = proc markThreadStacks(gch: var TGcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
{.error: "not fully implemented".} {.error: "not fully implemented".}
var it = threadList var it = threadList
while it != nil: while it != nil:
# mark registers: # mark registers:
for i in 0 .. high(it.registers): gcMark(gch, it.registers[i]) for i in 0 .. high(it.registers): gcMark(gch, it.registers[i])
var sp = cast[TAddress](it.stackBottom) var sp = cast[TAddress](it.stackBottom)
var max = cast[TAddress](it.stackTop) var max = cast[TAddress](it.stackTop)
@ -933,7 +936,7 @@ else:
while sp <=% max: while sp <=% max:
gcMark(gch, cast[PPointer](sp)[]) gcMark(gch, cast[PPointer](sp)[])
sp = sp +% sizeof(pointer) sp = sp +% sizeof(pointer)
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} =
forEachStackSlot(gch, gcMark) forEachStackSlot(gch, gcMark)
@ -946,13 +949,13 @@ when useMarkForDebug or useBackupGc:
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
proc collectZCT(gch: var TGcHeap): bool = proc collectZCT(gch: var TGcHeap): bool =
# Note: Freeing may add child objects to the ZCT! So essentially we do # Note: Freeing may add child objects to the ZCT! So essentially we do
# deep freeing, which is bad for incremental operation. In order to # deep freeing, which is bad for incremental operation. In order to
# avoid a deep stack, we move objects to keep the ZCT small. # avoid a deep stack, we move objects to keep the ZCT small.
# This is performance critical! # This is performance critical!
const workPackage = 100 const workPackage = 100
var L = addr(gch.zct.len) var L = addr(gch.zct.len)
when withRealTime: when withRealTime:
var steps = workPackage var steps = workPackage
var t0: TTicks var t0: TTicks
@ -962,15 +965,15 @@ proc collectZCT(gch: var TGcHeap): bool =
sysAssert(isAllocatedPtr(gch.region, c), "CollectZCT: isAllocatedPtr") sysAssert(isAllocatedPtr(gch.region, c), "CollectZCT: isAllocatedPtr")
# remove from ZCT: # remove from ZCT:
gcAssert((c.refcount and ZctFlag) == ZctFlag, "collectZCT") gcAssert((c.refcount and ZctFlag) == ZctFlag, "collectZCT")
c.refcount = c.refcount and not ZctFlag c.refcount = c.refcount and not ZctFlag
gch.zct.d[0] = gch.zct.d[L[] - 1] gch.zct.d[0] = gch.zct.d[L[] - 1]
dec(L[]) dec(L[])
when withRealTime: dec steps 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 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 # 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. # as this might be too slow.
# In any case, it should be removed from the ZCT. But not # In any case, it should be removed from the ZCT. But not
# freed. **KEEP THIS IN MIND WHEN MAKING THIS INCREMENTAL!** # freed. **KEEP THIS IN MIND WHEN MAKING THIS INCREMENTAL!**
@ -983,7 +986,7 @@ proc collectZCT(gch: var TGcHeap): bool =
# access invalid memory. This is done by prepareDealloc(): # access invalid memory. This is done by prepareDealloc():
prepareDealloc(c) prepareDealloc(c)
forAllChildren(c, waZctDecRef) forAllChildren(c, waZctDecRef)
when reallyDealloc: when reallyDealloc:
sysAssert(allocInv(gch.region), "collectZCT: rawDealloc") sysAssert(allocInv(gch.region), "collectZCT: rawDealloc")
rawDealloc(gch.region, c) rawDealloc(gch.region, c)
else: else:
@ -994,7 +997,7 @@ proc collectZCT(gch: var TGcHeap): bool =
steps = workPackage steps = workPackage
if gch.maxPause > 0: if gch.maxPause > 0:
let duration = getticks() - t0 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 # (stack unmarking), so subtract some short amount of time in
# order to miss deadlines less often: # order to miss deadlines less often:
if duration >= gch.maxPause - 50_000: if duration >= gch.maxPause - 50_000:
@ -1017,7 +1020,7 @@ proc collectCTBody(gch: var TGcHeap) =
when withRealTime: when withRealTime:
let t0 = getticks() let t0 = getticks()
sysAssert(allocInv(gch.region), "collectCT: begin") sysAssert(allocInv(gch.region), "collectCT: begin")
gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize()) gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize())
sysAssert(gch.decStack.len == 0, "collectCT") sysAssert(gch.decStack.len == 0, "collectCT")
prepareForInteriorPointerChecking(gch.region) prepareForInteriorPointerChecking(gch.region)
@ -1036,7 +1039,7 @@ proc collectCTBody(gch: var TGcHeap) =
gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold) gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
unmarkStackAndRegisters(gch) unmarkStackAndRegisters(gch)
sysAssert(allocInv(gch.region), "collectCT: end") sysAssert(allocInv(gch.region), "collectCT: end")
when withRealTime: when withRealTime:
let duration = getticks() - t0 let duration = getticks() - t0
gch.stat.maxPause = max(gch.stat.maxPause, duration) gch.stat.maxPause = max(gch.stat.maxPause, duration)
@ -1050,8 +1053,12 @@ when useMarkForDebug or useBackupGc:
markGlobals(gch) markGlobals(gch)
proc collectCT(gch: var TGcHeap) = proc collectCT(gch: var TGcHeap) =
if (gch.zct.len >= ZctThreshold or (cycleGC and # stackMarkCosts prevents some pathological behaviour: Stack marking
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and # 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: gch.recGcLock == 0:
when useMarkForDebug: when useMarkForDebug:
prepareForInteriorPointerChecking(gch.region) prepareForInteriorPointerChecking(gch.region)
@ -1070,7 +1077,7 @@ when withRealTime:
acquire(gch) acquire(gch)
gch.maxPause = us.toNano gch.maxPause = us.toNano
if (gch.zct.len >= ZctThreshold or (cycleGC and 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: strongAdvice:
collectCTBody(gch) collectCTBody(gch)
release(gch) release(gch)
@ -1078,13 +1085,13 @@ when withRealTime:
proc GC_step*(us: int, strongAdvice = false) = GC_step(gch, us, strongAdvice) proc GC_step*(us: int, strongAdvice = false) = GC_step(gch, us, strongAdvice)
when not defined(useNimRtl): when not defined(useNimRtl):
proc GC_disable() = proc GC_disable() =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
discard atomicInc(gch.recGcLock, 1) discard atomicInc(gch.recGcLock, 1)
else: else:
inc(gch.recGcLock) inc(gch.recGcLock)
proc GC_enable() = proc GC_enable() =
if gch.recGcLock > 0: if gch.recGcLock > 0:
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
discard atomicDec(gch.recGcLock, 1) discard atomicDec(gch.recGcLock, 1)
else: else:

View file

@ -593,7 +593,7 @@ proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} =
return return
add(gch.zct, res) 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 # generates a new object and sets its reference counter to 0
acquire(gch) acquire(gch)
sysAssert(allocInv(gch.region), "rawNewObj begin") sysAssert(allocInv(gch.region), "rawNewObj begin")

View file

@ -7,13 +7,13 @@
# distribution, for details about the copyright. # 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. # symbol ``gcUseBitvectors`` to generate a variant of this GC.
{.push profiler:off.} {.push profiler:off.}
const const
InitialThreshold = 4*1024*1024 # X MB because marking&sweeping is slow 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 # bitvectors are significantly faster for GC-bench, but slower for
# bootstrapping and use more memory # bootstrapping and use more memory
rcWhite = 0 rcWhite = 0
@ -29,21 +29,21 @@ type
TWalkOp = enum TWalkOp = enum
waMarkGlobal, # we need to mark conservatively for global marker procs waMarkGlobal, # we need to mark conservatively for global marker procs
# as these may refer to a global var and not to a thread # as these may refer to a global var and not to a thread
# local # local
waMarkPrecise # fast precise marking waMarkPrecise # fast precise marking
TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.} TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.}
# A ref type can have a finalizer that is called before the object's # A ref type can have a finalizer that is called before the object's
# storage is freed. # storage is freed.
TGlobalMarkerProc = proc () {.nimcall, benign.} TGlobalMarkerProc = proc () {.nimcall, benign.}
TGcStat = object TGcStat = object
collections: int # number of performed full collections collections: int # number of performed full collections
maxThreshold: int # max threshold that has been set maxThreshold: int # max threshold that has been set
maxStackSize: int # max stack size 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 TGcHeap = object # this contains the zero count and
# non-zero count table # non-zero count table
stackBottom: pointer stackBottom: pointer
@ -64,11 +64,11 @@ var
when not defined(useNimRtl): when not defined(useNimRtl):
instantiateForRegion(gch.region) instantiateForRegion(gch.region)
template acquire(gch: TGcHeap) = template acquire(gch: TGcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
acquireSys(HeapLock) acquireSys(HeapLock)
template release(gch: TGcHeap) = template release(gch: TGcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
releaseSys(HeapLock) releaseSys(HeapLock)
@ -134,7 +134,7 @@ proc prepareDealloc(cell: PCell) =
(cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell)) (cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell))
dec(gch.recGcLock) 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: # we keep it from being collected by pretending it's not even allocated:
when false: when false:
when withBitvectors: excl(gch.allocated, usrToCell(p)) when withBitvectors: excl(gch.allocated, usrToCell(p))
@ -261,6 +261,10 @@ proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
zeroMem(result, size) zeroMem(result, size)
when defined(memProfiler): nimProfile(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.} = proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
# `newObj` 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) 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) result = rawNewObj(typ, size, gch)
zeroMem(result, size) zeroMem(result, size)
when defined(memProfiler): nimProfile(size) when defined(memProfiler): nimProfile(size)
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} = proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
let size = addInt(mulInt(len, typ.base.size), GenericSeqSize) let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
result = newObj(typ, size) result = newObj(typ, size)
cast[PGenericSeq](result).len = len cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len cast[PGenericSeq](result).reserved = len
when defined(memProfiler): nimProfile(size) when defined(memProfiler): nimProfile(size)
proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer = proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
acquire(gch) acquire(gch)
collectCT(gch) collectCT(gch)
var ol = usrToCell(old) var ol = usrToCell(old)
sysAssert(ol.typ != nil, "growObj: 1") sysAssert(ol.typ != nil, "growObj: 1")
gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2") gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(TCell))) var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(TCell)))
var elemSize = 1 var elemSize = 1
if ol.typ.kind != tyString: elemSize = ol.typ.base.size if ol.typ.kind != tyString: elemSize = ol.typ.base.size
var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
copyMem(res, ol, oldsize + sizeof(TCell)) copyMem(res, ol, oldsize + sizeof(TCell))
zeroMem(cast[pointer](cast[ByteAddress](res)+% 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)) var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell))
if objStart != nil: if objStart != nil:
mark(gch, objStart) mark(gch, objStart)
# ----------------- stack management -------------------------------------- # ----------------- stack management --------------------------------------
# inspired from Smart Eiffel # inspired from Smart Eiffel
@ -536,7 +540,7 @@ proc collectCTBody(gch: var TGcHeap) =
markStackAndRegisters(gch) markStackAndRegisters(gch)
markGlobals(gch) markGlobals(gch)
sweep(gch) sweep(gch)
inc(gch.stat.collections) inc(gch.stat.collections)
when withBitvectors: when withBitvectors:
deinit(gch.marked) deinit(gch.marked)
@ -544,19 +548,19 @@ proc collectCTBody(gch: var TGcHeap) =
gch.cycleThreshold = max(InitialThreshold, getOccupiedMem().mulThreshold) gch.cycleThreshold = max(InitialThreshold, getOccupiedMem().mulThreshold)
gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold) gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
sysAssert(allocInv(gch.region), "collectCT: end") sysAssert(allocInv(gch.region), "collectCT: end")
proc collectCT(gch: var TGcHeap) = proc collectCT(gch: var TGcHeap) =
if getOccupiedMem(gch.region) >= gch.cycleThreshold and gch.recGcLock == 0: if getOccupiedMem(gch.region) >= gch.cycleThreshold and gch.recGcLock == 0:
collectCTBody(gch) collectCTBody(gch)
when not defined(useNimRtl): when not defined(useNimRtl):
proc GC_disable() = proc GC_disable() =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
atomicInc(gch.recGcLock, 1) atomicInc(gch.recGcLock, 1)
else: else:
inc(gch.recGcLock) inc(gch.recGcLock)
proc GC_enable() = proc GC_enable() =
if gch.recGcLock > 0: if gch.recGcLock > 0:
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
atomicDec(gch.recGcLock, 1) atomicDec(gch.recGcLock, 1)
else: else:

View file

@ -11,7 +11,7 @@ when declared(NimString):
# we are in system module: # we are in system module:
{.pragma: codegenType, compilerproc.} {.pragma: codegenType, compilerproc.}
else: else:
{.pragma: codegenType.} {.pragma: codegenType, importc.}
type type
# This should be he same as ast.TTypeKind # This should be he same as ast.TTypeKind
@ -65,7 +65,7 @@ type
tyBigNum, tyBigNum,
TNimNodeKind = enum nkNone, nkSlot, nkList, nkCase TNimNodeKind = enum nkNone, nkSlot, nkList, nkCase
TNimNode {.codegenType, final.} = object TNimNode {.codegenType.} = object
kind: TNimNodeKind kind: TNimNodeKind
offset: int offset: int
typ: ptr TNimType typ: ptr TNimType
@ -78,7 +78,7 @@ type
ntfAcyclic = 1, # type cannot form a cycle ntfAcyclic = 1, # type cannot form a cycle
ntfEnumHole = 2 # enum has holes and thus `$` for them needs the slow ntfEnumHole = 2 # enum has holes and thus `$` for them needs the slow
# version # version
TNimType {.codegenType, final.} = object TNimType {.codegenType.} = object
size: int size: int
kind: TNimKind kind: TNimKind
flags: set[TNimTypeFlag] flags: set[TNimTypeFlag]

View file

@ -1,7 +1,7 @@
# #
# #
# Nim's Runtime Library # Nim's Runtime Library
# (c) Copyright 2013 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -23,7 +23,7 @@ const
leakDetector = false leakDetector = false
overwriteFree = false overwriteFree = false
trackAllocationSource = leakDetector trackAllocationSource = leakDetector
cycleGC = true # (de)activate the cycle GC cycleGC = true # (de)activate the cycle GC
reallyDealloc = true # for debugging purposes this can be set to false reallyDealloc = true # for debugging purposes this can be set to false
reallyOsDealloc = true reallyOsDealloc = true
@ -71,13 +71,13 @@ when defined(boehmgc):
const boehmLib = "libgc.dylib" const boehmLib = "libgc.dylib"
else: else:
const boehmLib = "/usr/lib/libgc.so.1" const boehmLib = "/usr/lib/libgc.so.1"
proc boehmGCinit {.importc: "GC_init", dynlib: boehmLib.} proc boehmGCinit {.importc: "GC_init", dynlib: boehmLib.}
proc boehmGC_disable {.importc: "GC_disable", dynlib: boehmLib.} proc boehmGC_disable {.importc: "GC_disable", dynlib: boehmLib.}
proc boehmGC_enable {.importc: "GC_enable", dynlib: boehmLib.} proc boehmGC_enable {.importc: "GC_enable", dynlib: boehmLib.}
proc boehmGCincremental {. proc boehmGCincremental {.
importc: "GC_enable_incremental", dynlib: boehmLib.} importc: "GC_enable_incremental", dynlib: boehmLib.}
proc boehmGCfullCollect {.importc: "GC_gcollect", dynlib: boehmLib.} proc boehmGCfullCollect {.importc: "GC_gcollect", dynlib: boehmLib.}
proc boehmAlloc(size: int): pointer {. proc boehmAlloc(size: int): pointer {.
importc: "GC_malloc", dynlib: boehmLib.} importc: "GC_malloc", dynlib: boehmLib.}
proc boehmAllocAtomic(size: int): pointer {. proc boehmAllocAtomic(size: int): pointer {.
@ -85,7 +85,7 @@ when defined(boehmgc):
proc boehmRealloc(p: pointer, size: int): pointer {. proc boehmRealloc(p: pointer, size: int): pointer {.
importc: "GC_realloc", dynlib: boehmLib.} importc: "GC_realloc", dynlib: boehmLib.}
proc boehmDealloc(p: pointer) {.importc: "GC_free", dynlib: boehmLib.} proc boehmDealloc(p: pointer) {.importc: "GC_free", dynlib: boehmLib.}
proc boehmGetHeapSize: int {.importc: "GC_get_heap_size", dynlib: boehmLib.} proc boehmGetHeapSize: int {.importc: "GC_get_heap_size", dynlib: boehmLib.}
## Return the number of bytes in the heap. Excludes collector private ## Return the number of bytes in the heap. Excludes collector private
## data structures. Includes empty blocks and fragmentation loss. ## data structures. Includes empty blocks and fragmentation loss.
@ -108,7 +108,7 @@ when defined(boehmgc):
zeroMem(result, size) zeroMem(result, size)
when not defined(useNimRtl): when not defined(useNimRtl):
proc alloc(size: int): pointer = proc alloc(size: int): pointer =
result = boehmAlloc(size) result = boehmAlloc(size)
if result == nil: raiseOutOfMem() if result == nil: raiseOutOfMem()
@ -119,7 +119,7 @@ when defined(boehmgc):
result = boehmRealloc(p, newsize) result = boehmRealloc(p, newsize)
if result == nil: raiseOutOfMem() if result == nil: raiseOutOfMem()
proc dealloc(p: pointer) = boehmDealloc(p) proc dealloc(p: pointer) = boehmDealloc(p)
proc allocShared(size: int): pointer = proc allocShared(size: int): pointer =
result = boehmAlloc(size) result = boehmAlloc(size)
if result == nil: raiseOutOfMem() if result == nil: raiseOutOfMem()
@ -148,14 +148,14 @@ when defined(boehmgc):
proc GC_enableMarkAndSweep() = discard proc GC_enableMarkAndSweep() = discard
proc GC_disableMarkAndSweep() = discard proc GC_disableMarkAndSweep() = discard
proc GC_getStatistics(): string = return "" proc GC_getStatistics(): string = return ""
proc getOccupiedMem(): int = return boehmGetHeapSize()-boehmGetFreeBytes() proc getOccupiedMem(): int = return boehmGetHeapSize()-boehmGetFreeBytes()
proc getFreeMem(): int = return boehmGetFreeBytes() proc getFreeMem(): int = return boehmGetFreeBytes()
proc getTotalMem(): int = return boehmGetHeapSize() proc getTotalMem(): int = return boehmGetHeapSize()
proc setStackBottom(theStackBottom: pointer) = discard proc setStackBottom(theStackBottom: pointer) = discard
proc initGC() = proc initGC() =
when defined(macosx): boehmGCinit() when defined(macosx): boehmGCinit()
proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} = proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
@ -171,7 +171,7 @@ when defined(boehmgc):
proc nimGCref(p: pointer) {.compilerproc, inline.} = discard proc nimGCref(p: pointer) {.compilerproc, inline.} = discard
proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard
proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
dest[] = src dest[] = src
proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
@ -181,20 +181,20 @@ when defined(boehmgc):
type type
TMemRegion = object {.final, pure.} TMemRegion = object {.final, pure.}
proc alloc(r: var TMemRegion, size: int): pointer = proc alloc(r: var TMemRegion, size: int): pointer =
result = boehmAlloc(size) result = boehmAlloc(size)
if result == nil: raiseOutOfMem() if result == nil: raiseOutOfMem()
proc alloc0(r: var TMemRegion, size: int): pointer = proc alloc0(r: var TMemRegion, size: int): pointer =
result = alloc(size) result = alloc(size)
zeroMem(result, 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(r: var TMemRegion) {.inline.} = discard
proc deallocOsPages() {.inline.} = discard proc deallocOsPages() {.inline.} = discard
include "system/cellsets" include "system/cellsets"
elif defined(nogc) and defined(useMalloc): elif defined(nogc) and defined(useMalloc):
when not defined(useNimRtl): when not defined(useNimRtl):
proc alloc(size: int): pointer = proc alloc(size: int): pointer =
result = cmalloc(size) result = cmalloc(size)
@ -206,7 +206,7 @@ elif defined(nogc) and defined(useMalloc):
result = crealloc(p, newsize) result = crealloc(p, newsize)
if result == nil: raiseOutOfMem() if result == nil: raiseOutOfMem()
proc dealloc(p: pointer) = cfree(p) proc dealloc(p: pointer) = cfree(p)
proc allocShared(size: int): pointer = proc allocShared(size: int): pointer =
result = cmalloc(size) result = cmalloc(size)
if result == nil: raiseOutOfMem() if result == nil: raiseOutOfMem()
@ -225,11 +225,11 @@ elif defined(nogc) and defined(useMalloc):
proc GC_enableMarkAndSweep() = discard proc GC_enableMarkAndSweep() = discard
proc GC_disableMarkAndSweep() = discard proc GC_disableMarkAndSweep() = discard
proc GC_getStatistics(): string = return "" proc GC_getStatistics(): string = return ""
proc getOccupiedMem(): int = discard proc getOccupiedMem(): int = discard
proc getFreeMem(): int = discard proc getFreeMem(): int = discard
proc getTotalMem(): int = discard proc getTotalMem(): int = discard
proc setStackBottom(theStackBottom: pointer) = discard proc setStackBottom(theStackBottom: pointer) = discard
proc initGC() = discard proc initGC() = discard
@ -240,13 +240,15 @@ elif defined(nogc) and defined(useMalloc):
result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize)) result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
cast[PGenericSeq](result).len = len cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len cast[PGenericSeq](result).reserved = len
proc newObjNoInit(typ: PNimType, size: int): pointer =
result = alloc(size)
proc growObj(old: pointer, newsize: int): pointer = proc growObj(old: pointer, newsize: int): pointer =
result = realloc(old, newsize) result = realloc(old, newsize)
proc nimGCref(p: pointer) {.compilerproc, inline.} = discard proc nimGCref(p: pointer) {.compilerproc, inline.} = discard
proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard
proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
dest[] = src dest[] = src
proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
@ -256,7 +258,7 @@ elif defined(nogc) and defined(useMalloc):
type type
TMemRegion = object {.final, pure.} TMemRegion = object {.final, pure.}
proc alloc(r: var TMemRegion, size: int): pointer = proc alloc(r: var TMemRegion, size: int): pointer =
result = alloc(size) result = alloc(size)
proc alloc0(r: var TMemRegion, size: int): pointer = 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 # object, because C does not support this operation... Even though every
# possible implementation has to have a way to determine the object's size. # possible implementation has to have a way to determine the object's size.
# C just sucks. # C just sucks.
when appType == "lib": when appType == "lib":
{.warning: "nogc in a library context may not work".} {.warning: "nogc in a library context may not work".}
include "system/alloc" include "system/alloc"
proc initGC() = discard proc initGC() = discard
@ -285,10 +287,14 @@ elif defined(nogc):
proc GC_enableMarkAndSweep() = discard proc GC_enableMarkAndSweep() = discard
proc GC_disableMarkAndSweep() = discard proc GC_disableMarkAndSweep() = discard
proc GC_getStatistics(): string = return "" proc GC_getStatistics(): string = return ""
proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} = proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
result = alloc0(size) result = alloc0(size)
proc newObjNoInit(typ: PNimType, size: int): pointer =
result = alloc(size)
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} = proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize)) result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
cast[PGenericSeq](result).len = len cast[PGenericSeq](result).len = len
@ -299,7 +305,7 @@ elif defined(nogc):
proc setStackBottom(theStackBottom: pointer) = discard proc setStackBottom(theStackBottom: pointer) = discard
proc nimGCref(p: pointer) {.compilerproc, inline.} = discard proc nimGCref(p: pointer) {.compilerproc, inline.} = discard
proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard
proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
dest[] = src dest[] = src
proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
@ -327,6 +333,6 @@ else:
include "system/gc" include "system/gc"
else: else:
include "system/gc" include "system/gc"
{.pop.} {.pop.}

View file

@ -145,8 +145,8 @@ proc readAllFile(file: File): string =
proc readAll(file: File): TaintedString = proc readAll(file: File): TaintedString =
# Separate handling needed because we need to buffer when we # Separate handling needed because we need to buffer when we
# don't know the overall length of the File. # don't know the overall length of the File.
var len = rawFileSize(file) let len = if file != stdin: rawFileSize(file) else: -1
if len >= 0: if len > 0:
result = readAllFile(file, len).TaintedString result = readAllFile(file, len).TaintedString
else: else:
result = readAllBuffer(file).TaintedString result = readAllBuffer(file).TaintedString

View file

@ -30,18 +30,30 @@ proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
if a == b: return true if a == b: return true
if a == nil or b == nil: return false if a == nil or b == nil: return false
return a.len == b.len and 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): when declared(allocAtomic):
template allocStr(size: expr): expr = template allocStr(size: expr): expr =
cast[NimString](allocAtomic(size)) cast[NimString](allocAtomic(size))
template allocStrNoInit(size: expr): expr =
cast[NimString](boehmAllocAtomic(size))
else: else:
template allocStr(size: expr): expr = template allocStr(size: expr): expr =
cast[NimString](newObj(addr(strDesc), size)) 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.} = proc rawNewString(space: int): NimString {.compilerProc.} =
var s = space var s = space
if s < 8: s = 7 if s < 7: s = 7
result = allocStr(sizeof(TGenericSeq) + s + 1) result = allocStr(sizeof(TGenericSeq) + s + 1)
result.reserved = s result.reserved = s
@ -53,10 +65,10 @@ proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.} =
var start = max(start, 0) var start = max(start, 0)
var len = min(last, s.len-1) - start + 1 var len = min(last, s.len-1) - start + 1
if len > 0: if len > 0:
result = rawNewString(len) result = rawNewStringNoInit(len)
result.len = len result.len = len
c_memcpy(result.data, addr(s.data[start]), len * sizeof(char)) c_memcpy(result.data, addr(s.data[start]), len)
#result.data[len] = '\0' result.data[len] = '\0'
else: else:
result = rawNewString(len) result = rawNewString(len)
@ -64,10 +76,9 @@ proc copyStr(s: NimString, start: int): NimString {.compilerProc.} =
result = copyStrLast(s, start, s.len-1) result = copyStrLast(s, start, s.len-1)
proc toNimStr(str: cstring, len: int): NimString {.compilerProc.} = proc toNimStr(str: cstring, len: int): NimString {.compilerProc.} =
result = rawNewString(len) result = rawNewStringNoInit(len)
result.len = len result.len = len
c_memcpy(result.data, str, (len+1) * sizeof(char)) c_memcpy(result.data, str, len + 1)
#result.data[len] = '\0' # readline relies on this!
proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} = proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} =
result = toNimStr(str, c_strlen(str)) result = toNimStr(str, c_strlen(str))
@ -77,23 +88,24 @@ proc copyString(src: NimString): NimString {.compilerRtl.} =
if (src.reserved and seqShallowFlag) != 0: if (src.reserved and seqShallowFlag) != 0:
result = src result = src
else: else:
result = rawNewString(src.space) result = rawNewStringNoInit(src.len)
result.len = 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.} = proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
if src != nil: if src != nil:
var s = src.space
if s < 8: s = 7
when declared(newObjRC1): when declared(newObjRC1):
var s = src.len
if s < 7: s = 7
result = cast[NimString](newObjRC1(addr(strDesc), sizeof(TGenericSeq) + result = cast[NimString](newObjRC1(addr(strDesc), sizeof(TGenericSeq) +
s+1)) s+1))
result.reserved = s
else: else:
result = allocStr(sizeof(TGenericSeq) + s + 1) result = rawNewStringNoInit(src.len)
result.reserved = s
result.len = src.len result.len = src.len
c_memcpy(result.data, src.data, src.len + 1) c_memcpy(result.data, src.data, src.len + 1)
proc hashString(s: string): int {.compilerproc.} = proc hashString(s: string): int {.compilerproc.} =
# the compiler needs exactly the same hash function! # the compiler needs exactly the same hash function!
# this used to be used for efficient generation of string case statements # 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: if result.len >= result.space:
result.reserved = resize(result.space) result.reserved = resize(result.space)
result = cast[NimString](growObj(result, 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] = c
result.data[result.len+1] = '\0' result.data[result.len+1] = '\0'
inc(result.len) 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 = cast[NimString](growObj(dest, sizeof(TGenericSeq) + sp + 1))
result.reserved = sp result.reserved = sp
#result = rawNewString(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 # DO NOT UPDATE LEN YET: dest.len = newLen
proc appendString(dest, src: NimString) {.compilerproc, inline.} = proc appendString(dest, src: NimString) {.compilerproc, inline.} =
@ -203,7 +215,7 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
GenericSeqSize)) GenericSeqSize))
elif newLen < result.len: elif newLen < result.len:
# we need to decref here, otherwise the GC leaks! # 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): not defined(gcMarkAndSweep):
when compileOption("gc", "v2"): when compileOption("gc", "v2"):
for i in newLen..result.len-1: 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) +% forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +%
GenericSeqSize +% (i*%elemSize)), GenericSeqSize +% (i*%elemSize)),
extGetCellType(result).base, waZctDecRef) extGetCellType(result).base, waZctDecRef)
# XXX: zeroing out the memory can still result in crashes if a wiped-out # 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). # 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 # This is a tought problem, because even if we don't zeroMem here, in the
@ -258,13 +270,23 @@ proc nimFloatToStr(f: float): string {.compilerproc.} =
if buf[i] == ',': if buf[i] == ',':
buf[i] = '.' buf[i] = '.'
hasDot = true hasDot = true
elif buf[i] in {'a'..'z', 'A'..'Z', '.'}: elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
hasDot = true hasDot = true
if not hasDot: if not hasDot:
buf[n] = '.' buf[n] = '.'
buf[n+1] = '0' buf[n+1] = '0'
buf[n+2] = '\0' buf[n+2] = '\0'
result = $buf # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' are produced.
# We want to get rid of these here:
if buf[n-1] == 'N':
result = "nan"
elif buf[n-1] == 'F':
if buf[0] == '-':
result = "-inf"
else:
result = "inf"
else:
result = $buf
proc strtod(buf: cstring, endptr: ptr cstring): float64 {.importc, proc strtod(buf: cstring, endptr: ptr cstring): float64 {.importc,
header: "<stdlib.h>", noSideEffect.} header: "<stdlib.h>", noSideEffect.}
@ -299,7 +321,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
template addToBuf(c) = template addToBuf(c) =
if ti < t.high: if ti < t.high:
t[ti] = c; inc(ti) t[ti] = c; inc(ti)
# Sign? # Sign?
if s[i] == '+' or s[i] == '-': if s[i] == '+' or s[i] == '-':
if s[i] == '-': if s[i] == '-':
@ -320,7 +342,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
if s[i] == 'I' or s[i] == 'i': if s[i] == 'I' or s[i] == 'i':
if s[i+1] == 'N' or s[i+1] == 'n': if s[i+1] == 'N' or s[i+1] == 'n':
if s[i+2] == 'F' or s[i+2] == 'f': 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 number = Inf*sign
return i+3 - start return i+3 - start
return 0 return 0

View file

@ -50,7 +50,7 @@ when useWinVersion:
from winlean import SocketHandle from winlean import SocketHandle
else: else:
const 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): when defined(macosx):
const const
DLLSSLName = "libssl" & versions & ".dylib" DLLSSLName = "libssl" & versions & ".dylib"
@ -141,6 +141,14 @@ const
SSL_CTRL_GET_MAX_CERT_LIST* = 50 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 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): * # * 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 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 # * (buffer contents must stay the same!); this is not the default to avoid
# * the misconception that non-blocking SSL_write() behaves like # * the misconception that non-blocking SSL_write() behaves like
@ -296,9 +304,41 @@ proc CRYPTO_malloc_init*() =
proc SSL_CTX_ctrl*(ctx: SslCtx, cmd: cInt, larg: int, parg: pointer): int{. proc SSL_CTX_ctrl*(ctx: SslCtx, cmd: cInt, larg: int, parg: pointer): int{.
cdecl, dynlib: DLLSSLName, importc.} 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 = proc SSLCTXSetMode*(ctx: SslCtx, mode: int): int =
result = SSL_CTX_ctrl(ctx, SSL_CTRL_MODE, mode, nil) 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 bioNew*(b: PBIO_METHOD): BIO{.cdecl, dynlib: DLLUtilName, importc: "BIO_new".}
proc bioFreeAll*(b: BIO){.cdecl, dynlib: DLLUtilName, importc: "BIO_free_all".} proc bioFreeAll*(b: BIO){.cdecl, dynlib: DLLUtilName, importc: "BIO_free_all".}
proc bioSMem*(): PBIO_METHOD{.cdecl, dynlib: DLLUtilName, importc: "BIO_s_mem".} proc bioSMem*(): PBIO_METHOD{.cdecl, dynlib: DLLUtilName, importc: "BIO_s_mem".}
@ -341,8 +381,6 @@ else:
dynlib: DLLSSLName, importc.} dynlib: DLLSSLName, importc.}
proc SslSetFd*(s: PSSL, fd: cInt): cInt{.cdecl, 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{. proc SslCTXCtrl*(ctx: PSSL_CTX, cmd: cInt, larg: int, parg: Pointer): int{.
cdecl, dynlib: DLLSSLName, importc.} cdecl, dynlib: DLLSSLName, importc.}

View file

@ -1,5 +1,5 @@
# Nim Compiler # 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. documentation.
## Compiling ## 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 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). 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 The compiler currently supports the following platform and architecture
[Nimbuild](http://build.nim-lang.org/). Your platform however may not
currently be built for.
The compiler currently supports the following platform and architecture
combinations: combinations:
* Windows (Windows XP or greater) - x86 and x86_64 * Windows (Windows XP or greater) - x86 and x86_64
* Linux (most, if not all, distributions) - x86, x86_64, ppc64 and armv6l * Linux (most, if not all, distributions) - x86, x86_64, ppc64 and armv6l
* Mac OS X 10.04 or higher - x86, x86_64 and ppc64 * 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. In reality a lot more are supported, however they are not tested regularly.
To build from source you will need: To build from source you will need:
@ -39,8 +35,7 @@ $ bin/nim c koch
$ ./koch boot -d:release $ ./koch boot -d:release
``` ```
``koch install [dir]`` may then be used to install Nim, or you can simply ``koch install [dir]`` may then be used to install Nim, but lots of things don't work then so don't do that. Add it to your PATH instead. More ``koch`` related options are documented in
add it to your PATH. More ``koch`` related options are documented in
[doc/koch.txt](doc/koch.txt). [doc/koch.txt](doc/koch.txt).
The above steps can be performed on Windows in a similar fashion, the The above steps can be performed on Windows in a similar fashion, the
@ -55,9 +50,9 @@ questions, and you can also get help in the IRC channel on
tag](http://stackoverflow.com/questions/tagged/nim). tag](http://stackoverflow.com/questions/tagged/nim).
## License ## License
The compiler and the standard library are licensed under the MIT license, The compiler and the standard library are licensed under the MIT license,
except for some modules where the documentation suggests otherwise. This means except for some modules where the documentation suggests otherwise. This means
that you can use any license for your own programs developed with Nim, that you can use any license for your own programs developed with Nim,
allowing you to create commercial applications. allowing you to create commercial applications.
Read copying.txt for more details. Read copying.txt for more details.

View file

@ -1,6 +1,5 @@
discard """ discard """
line: 21 output: "action 3 arg"
errormsg: "invalid type: 'Table[string, proc (string){.gcsafe.}]'"
""" """
import tables import tables

View file

@ -3,14 +3,14 @@ discard """
WARNING: false first assertion from bar WARNING: false first assertion from bar
ERROR: false second assertion from bar ERROR: false second assertion from bar
-1 -1
tests/assert/tfailedassert.nim:27 false assertion from foo tfailedassert.nim:27 false assertion from foo
''' '''
""" """
type type
TLineInfo = tuple[filename: string, line: int] TLineInfo = tuple[filename: string, line: int]
TMyError = object of E_Base TMyError = object of Exception
lineinfo: TLineInfo lineinfo: TLineInfo
EMyError = ref TMyError EMyError = ref TMyError

View file

@ -0,0 +1,30 @@
discard """
output: '''255
1 1
0.5'''
"""
# bug #1181
type
TFoo = object
x: int32
proc mainowar =
var foo: TFoo
foo.x = 0xff
var arr1 = cast[ptr array[4, uint8]](addr foo)[] # Fails.
echo arr1[when cpuEndian == littleEndian: 0 else: 3]
var i = 1i32
let x = addr i
var arr2 = cast[ptr array[4, uint8]](x)[] # Fails.
echo arr2[when cpuEndian == littleEndian: 0 else: 3], " ", i
# bug #1715
var a: array[2, float32] = [0.5'f32, 0.7]
let p = addr a
var b = p[]
echo b[0]
mainowar()

View file

@ -0,0 +1,15 @@
discard """
disabled: "true"
"""
# Now the compiler fails with OOM. yay.
# 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

View file

@ -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()

View file

@ -0,0 +1,8 @@
discard """
file: "tconsttypemismatch.nim"
line: 7
errormsg: "type mismatch"
"""
# bug #2252
const foo: int = 1000 / 30

View file

@ -1,5 +1,5 @@
discard """ discard """
cmd: "nim cpp $target" cmd: "nim cpp $file"
""" """
import rawsockets import rawsockets

View file

@ -1,5 +1,5 @@
discard """ discard """
cmd: "nim cpp $target" cmd: "nim cpp $file"
""" """
import typeinfo import typeinfo

View file

@ -1,11 +1,9 @@
discard """ discard """
line: 9 nimout: "a is deprecated [Deprecated]"
errormsg: "'a' is deprecated [Deprecated]"
""" """
var var
a {.deprecated.}: array[0..11, int] a {.deprecated.}: array[0..11, int]
a[8] = 1 a[8] = 1

View file

@ -1,6 +1,6 @@
discard """ discard """
line: 20 line: 23
errormsg: " usage of a type with a destructor in a non destructible context" nimout: " usage of a type with a destructor in a non destructible context"
""" """
{.experimental.} {.experimental.}
@ -19,5 +19,9 @@ proc open: TMyObj =
proc `$`(x: TMyObj): string = $x.y proc `$`(x: TMyObj): string = $x.y
echo open() proc foo =
discard open()
# XXX doesn't trigger this yet:
#echo open()

View file

@ -1,6 +1,6 @@
discard """ discard """
line: 7 line: 7
errormsg: "expression 'items' cannot be called" errormsg: "undeclared identifier: 'items'"
""" """
type a = enum b,c,d type a = enum b,c,d

View file

@ -0,0 +1,18 @@
discard """
output: '''hi
hi'''
"""
# bug #1742
template test(): expr =
let a = 0
defer: echo "hi"
a
let i = test()
import strutils
let x = try: parseInt("133a")
except: -1
finally: echo "hi"

View file

@ -0,0 +1,6 @@
discard """
nimout: "Special variable 'result' is shadowed. [ResultShadowed]"
"""
proc test(): string =
var result = "foo"

View file

@ -188,7 +188,7 @@ proc traceTree[T,D](root: PNode[T,D]) =
write stdout, space write stdout, space
proc doTrace(n: PNode[T,D], level: int) = proc doTrace(n: PNode[T,D], level: int) =
var space = repeatChar(2 * level) var space = spaces(2 * level)
traceln(space) traceln(space)
write stdout, "node: " write stdout, "node: "
if n == nil: if n == nil:

View file

@ -1,5 +1,11 @@
# Bug #2022 # Bug #2022
discard """
output: '''@[97, 45]
@[true, false]
@[false, false]'''
"""
## The goal of this snippet is to provide and test a construct for general- ## 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 ## purpose, random-access mapping. I use an AST-manipulation-based approach
## because it's more efficient than using procedure pointers and less ## because it's more efficient than using procedure pointers and less
@ -31,6 +37,7 @@ type Mapped[Input; predicate: static[string]] = object
input: Input input: Input
macro map(input, predicate: expr): expr = macro map(input, predicate: expr): expr =
let predicate = callsite()[2]
newNimNode(nnkObjConstr).add( newNimNode(nnkObjConstr).add(
newNimNode(nnkBracketExpr).add( newNimNode(nnkBracketExpr).add(
ident"Mapped", ident"Mapped",

View file

@ -1,6 +1,6 @@
discard """ discard """
file: "toop1.nim"
output: "in globalaux2: 10\ntotal globals: 2\nint value: 100\nstring value: second" output: "in globalaux2: 10\ntotal globals: 2\nint value: 100\nstring value: second"
disabled: "true"
""" """
import globalaux, globalaux2 import globalaux, globalaux2

54
tests/init/tuninit2.nim Normal file
View file

@ -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()

View file

@ -471,7 +471,7 @@ proc build_help*(expected: seq[Tparameter_specification] = @[],
let width = prefixes.map(proc (x: string): int = 3 + len(x)).max let width = prefixes.map(proc (x: string): int = 3 + len(x)).max
for line in zip(prefixes, helps): for line in zip(prefixes, helps):
result.add(line.a & repeatChar(width - line.a.len) & line.b) result.add(line.a & spaces(width - line.a.len) & line.b)
proc echo_help*(expected: seq[Tparameter_specification] = @[], proc echo_help*(expected: seq[Tparameter_specification] = @[],

View file

@ -1,5 +1,5 @@
import streams import streams
from strutils import repeatChar from strutils import repeat
proc readPaddedStr*(s: PStream, length: int, padChar = '\0'): TaintedString = proc readPaddedStr*(s: PStream, length: int, padChar = '\0'): TaintedString =
var lastChr = length var lastChr = length
@ -10,7 +10,7 @@ proc readPaddedStr*(s: PStream, length: int, padChar = '\0'): TaintedString =
proc writePaddedStr*(s: PStream, str: string, length: int, padChar = '\0') = proc writePaddedStr*(s: PStream, str: string, length: int, padChar = '\0') =
if str.len < length: if str.len < length:
s.write(str) s.write(str)
s.write(repeatChar(length - str.len, padChar)) s.write(repeat(padChar, length - str.len))
elif str.len > length: elif str.len > length:
s.write(str.substr(0, length - 1)) s.write(str.substr(0, length - 1))
else: else:
@ -37,7 +37,7 @@ when isMainModule:
testStream.setPosition 0 testStream.setPosition 0
testStream.writePaddedStr("Sup", 10) testStream.writePaddedStr("Sup", 10)
echo(repr(testStream), testStream.data.len) echo(repr(testStream), testStream.data.len)
doAssert testStream.data == "Sup"&repeatChar(7, '\0') doAssert testStream.data == "Sup"&repeat('\0', 7)
testStream.setPosition 0 testStream.setPosition 0
res = testStream.readPaddedStr(10) res = testStream.readPaddedStr(10)

View file

@ -144,7 +144,7 @@ when isMainModule:
discard """block: discard """block:
var var
TestFile: FileChallengePair TestFile: FileChallengePair
contents = repeatStr(2, "abcdefghijklmnopqrstuvwxyz") contents = repeat("abcdefghijklmnopqrstuvwxyz", 2)
testFile.challenge = newScFileChallenge("foobar.test", FZoneCfg, contents.len.int32) testFile.challenge = newScFileChallenge("foobar.test", FZoneCfg, contents.len.int32)
testFile.file = checksumStr(contents) testFile.file = checksumStr(contents)
myAssets.add testFile""" myAssets.add testFile"""

View file

@ -113,7 +113,7 @@ when defined(recordMode):
isRecording = false isRecording = false
proc zeroPad*(s: string; minLen: int): string = proc zeroPad*(s: string; minLen: int): string =
if s.len < minLen: if s.len < minLen:
result = repeatChar(minLen - s.len, '0') result = repeat(0, minLen - s.len)
result.add s result.add s
else: else:
result = s result = s

View file

@ -27,9 +27,9 @@ when isMainModule:
var res = t.map(proc(z: int): int = result = z * 10) var res = t.map(proc(z: int): int = result = z * 10)
dumpSeq res dumpSeq res
from strutils import toHex, repeatStr from strutils import toHex
var foocakes = t.map(proc(z: int): string = var foocakes = t.map(proc(z: int): string =
result = toHex((z * 23).biggestInt, 4)) result = toHex((z * 23).BiggestInt, 4))
dumpSeq foocakes dumpSeq foocakes
t.mapInPlace(proc(z: int): int = result = z * 30) t.mapInPlace(proc(z: int): int = result = z * 30)

Some files were not shown because too many files have changed in this diff Show more