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

View file

@ -203,9 +203,9 @@ proc mustRehash(length, counter: int): bool =
assert(length > counter)
result = (length * 2 < counter * 3) or (length - counter < 4)
proc spaces(x: int): PRope =
proc rspaces(x: int): PRope =
# returns x spaces
result = toRope(repeatChar(x))
result = toRope(spaces(x))
proc toYamlChar(c: char): string =
case c
@ -253,7 +253,7 @@ proc typeToYamlAux(n: PType, marker: var IntSet,
indent, maxRecDepth: int): PRope
proc strTableToYaml(n: TStrTable, marker: var IntSet, indent: int,
maxRecDepth: int): PRope =
var istr = spaces(indent + 2)
var istr = rspaces(indent + 2)
result = toRope("[")
var mycount = 0
for i in countup(0, high(n.data)):
@ -262,20 +262,20 @@ proc strTableToYaml(n: TStrTable, marker: var IntSet, indent: int,
appf(result, "$N$1$2",
[istr, symToYamlAux(n.data[i], marker, indent + 2, maxRecDepth - 1)])
inc(mycount)
if mycount > 0: appf(result, "$N$1", [spaces(indent)])
if mycount > 0: appf(result, "$N$1", [rspaces(indent)])
app(result, "]")
assert(mycount == n.counter)
proc ropeConstr(indent: int, c: openArray[PRope]): PRope =
# array of (name, value) pairs
var istr = spaces(indent + 2)
var istr = rspaces(indent + 2)
result = toRope("{")
var i = 0
while i <= high(c):
if i > 0: app(result, ",")
appf(result, "$N$1\"$2\": $3", [istr, c[i], c[i + 1]])
inc(i, 2)
appf(result, "$N$1}", [spaces(indent)])
appf(result, "$N$1}", [rspaces(indent)])
proc symToYamlAux(n: PSym, marker: var IntSet, indent: int,
maxRecDepth: int): PRope =
@ -310,9 +310,9 @@ proc typeToYamlAux(n: PType, marker: var IntSet, indent: int,
result = toRope("[")
for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ",")
appf(result, "$N$1$2", [spaces(indent + 4), typeToYamlAux(n.sons[i],
appf(result, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(n.sons[i],
marker, indent + 4, maxRecDepth - 1)])
appf(result, "$N$1]", [spaces(indent + 2)])
appf(result, "$N$1]", [rspaces(indent + 2)])
else:
result = toRope("null")
result = ropeConstr(indent, [toRope("kind"),
@ -331,7 +331,7 @@ proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int,
if n == nil:
result = toRope("null")
else:
var istr = spaces(indent + 2)
var istr = rspaces(indent + 2)
result = ropef("{$N$1\"kind\": $2", [istr, makeYamlString($n.kind)])
if maxRecDepth != 0:
appf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)])
@ -359,12 +359,12 @@ proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int,
appf(result, ",$N$1\"sons\": [", [istr])
for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ",")
appf(result, "$N$1$2", [spaces(indent + 4), treeToYamlAux(n.sons[i],
appf(result, "$N$1$2", [rspaces(indent + 4), treeToYamlAux(n.sons[i],
marker, indent + 4, maxRecDepth - 1)])
appf(result, "$N$1]", [istr])
appf(result, ",$N$1\"typ\": $2",
[istr, typeToYamlAux(n.typ, marker, indent + 2, maxRecDepth)])
appf(result, "$N$1}", [spaces(indent)])
appf(result, "$N$1}", [rspaces(indent)])
proc treeToYaml(n: PNode, indent: int = 0, maxRecDepth: int = - 1): PRope =
var marker = initIntSet()
@ -408,7 +408,7 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int;
if n == nil:
result = toRope("null")
else:
var istr = spaces(indent + 2)
var istr = rspaces(indent + 2)
result = ropef("{$N$1\"kind\": $2",
[istr, makeYamlString($n.kind)])
if maxRecDepth != 0:
@ -440,11 +440,11 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int;
appf(result, ",$N$1\"sons\": [", [istr])
for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ",")
appf(result, "$N$1$2", [spaces(indent + 4), debugTree(n.sons[i],
appf(result, "$N$1$2", [rspaces(indent + 4), debugTree(n.sons[i],
indent + 4, maxRecDepth - 1, renderType)])
appf(result, "$N$1]", [istr])
appf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)])
appf(result, "$N$1}", [spaces(indent)])
appf(result, "$N$1}", [rspaces(indent)])
proc debug(n: PSym) =
if n == nil:
@ -681,9 +681,8 @@ proc initIdentIter(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym =
else: result = nextIdentIter(ti, tab)
proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym =
var h, start: THash
h = ti.h and high(tab.data)
start = h
var h = ti.h and high(tab.data)
var start = h
result = tab.data[h]
while result != nil:
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):
initLocExpr(p, n, a)
result = addrLoc(a)
elif p.module.compileToCpp and param.typ.kind == tyVar and
elif p.module.compileToCpp and param.typ.kind == tyVar and
n.kind == nkHiddenAddr:
initLocExprSingleUse(p, n.sons[0], a)
# if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still
# means '*T'. See posix.nim for lots of examples that do that in the wild.
let callee = call.sons[0]
if callee.kind == nkSym and
{sfImportC, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportC} and
{sfImportC, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportC} and
{lfHeader, lfNoDecl} * callee.sym.loc.flags != {}:
result = addrLoc(a)
else:
@ -192,7 +192,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
var op: TLoc
initLocExpr(p, ri.sons[0], op)
var pl: PRope
var typ = skipTypes(ri.sons[0].typ, abstractInst)
assert(typ.kind == tyProc)
var length = sonsLen(ri)
@ -204,7 +204,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
else:
app(pl, genArgNoParam(p, ri.sons[i]))
if i < length - 1: app(pl, ~", ")
template genCallPattern {.dirty.} =
lineF(p, cpsStmts, callPattern & ";$n", op.r, pl, pl.addComma, rawProc)
@ -339,7 +339,8 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): PRope =
let typ = skipTypes(ri.sons[0].typ, abstractInst)
if pat[i+1] == '+': result.app genArgNoParam(p, ri.sons[0])
result.app(~"(")
result.app genOtherArg(p, ri, 1, typ)
if 1 < ri.len:
result.app genOtherArg(p, ri, 1, typ)
for k in j+1 .. < ri.len:
result.app(~", ")
result.app genOtherArg(p, ri, k, typ)

View file

@ -56,11 +56,6 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): PRope =
case skipTypes(ty, abstractVarRange).kind
of tyChar, tyNil:
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:
if n.intVal != 0: result = ~"NIM_TRUE"
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)
if id == gBackendId:
# string literal not found in the cache:
result = ropecg(p.module, "((#NimStringDesc*) &$1)",
result = ropecg(p.module, "((#NimStringDesc*) &$1)",
[getStrLit(p.module, n.strVal)])
else:
result = ropecg(p.module, "((#NimStringDesc*) &TMP$1)", [toRope(id)])
@ -158,7 +153,7 @@ proc getStorageLoc(n: PNode): TStorageLoc =
of skVar, skForVar, skResult, skLet:
if sfGlobal in n.sym.flags: result = OnHeap
else: result = OnStack
of skConst:
of skConst:
if sfGlobal in n.sym.flags: result = OnHeap
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:
let t = t.sons[i]
let field = ropef("Field$1", i.toRope)
genAssignment(p, optAsgnLoc(dest, t, field),
genAssignment(p, optAsgnLoc(dest, t, field),
optAsgnLoc(src, t, field), newflags)
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
of nkSym:
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)
of nkRecList:
for child in items(t): genOptAsgnObject(p, dest, src, newflags, child)
else: discard
proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
# Consider:
# Consider:
# type TMyFastString {.shallow.} = string
# 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
# here for this flag, where it is reasonably safe to do so
# (for objects, etc.):
if needToCopy notin flags or
if needToCopy notin flags or
tfShallow in skipTypes(dest.t, abstractVarRange).flags:
if dest.s == OnStack or not usesNativeGC():
useStringh(p.module)
@ -510,7 +505,7 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
var storage: PRope
var size = getSize(t)
if size < platform.intSize:
storage = toRope("NI")
storage = toRope("NI")
else:
storage = getTypeDesc(p.module, t)
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))", # MulF64
"(($4)($1) / ($4)($2))", # DivF64
"($4)((NU$3)($1) >> (NU$3)($2))", # ShrI
"($4)((NU$3)($1) << (NU$3)($2))", # ShlI
"($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[2], b)
if a.t.callConv == ccClosure:
putIntoDest(p, d, e.typ,
putIntoDest(p, d, e.typ,
ropef("($1.ClPrc == $2.ClPrc && $1.ClEnv == $2.ClEnv)", [
rdLoc(a), rdLoc(b)]))
else:
@ -721,7 +716,7 @@ template inheritLocation(d: var TLoc, a: TLoc) =
if d.k == locNone: d.s = a.s
if d.heapRoot == nil:
d.heapRoot = if a.heapRoot != nil: a.heapRoot else: a.r
proc genRecordFieldAux(p: BProc, e: PNode, d, a: var TLoc): PType =
initLocExpr(p, e.sons[0], a)
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)
appf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)])
linefmt(p, cpsStmts, "printf($1$2);$n",
makeCString(repeatStr(n.len, "%s") & tnl), args)
makeCString(repeat("%s", n.len) & tnl), args)
proc gcUsage(n: PNode) =
if gSelectedGC == gcNone: message(n.info, warnGcMem, n.renderTree)
@ -1061,7 +1056,7 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
genAssignment(p, dest, b, {needToCopy, afDestIsNil})
gcUsage(e)
proc genReset(p: BProc, n: PNode) =
proc genReset(p: BProc, n: PNode) =
var a: TLoc
initLocExpr(p, n.sons[1], a)
linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
@ -1120,14 +1115,14 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: PRope) =
else:
call.r = ropecg(p.module, "($1) #newSeq($2, $3)", args)
genAssignment(p, dest, call, {needToKeepAlive})
proc genNewSeq(p: BProc, e: PNode) =
var a, b: TLoc
initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], b)
genNewSeqAux(p, a, b.rdLoc)
gcUsage(e)
proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
var tmp: TLoc
var t = e.typ.skipTypes(abstractInst)
@ -1168,7 +1163,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
d = tmp
else:
genAssignment(p, d, tmp, {})
proc genSeqConstr(p: BProc, t: PNode, d: var TLoc) =
var arr: TLoc
if d.k == locNone:
@ -1192,7 +1187,7 @@ proc genArrToSeq(p: BProc, t: PNode, d: var TLoc) =
getTemp(p, t.typ, d)
# generate call to newSeq before adding the elements per hand:
var L = int(lengthOrd(t.sons[1].typ))
genNewSeqAux(p, d, intLiteral(L))
initLocExpr(p, t.sons[1], a)
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)
arr.r = rfmt(nil, "$1[$2]", rdLoc(a), intLiteral(i))
genAssignment(p, elem, arr, {afDestIsNil, needToCopy})
proc genNewFinalize(p: BProc, e: PNode) =
var
a, b, f: TLoc
@ -1258,7 +1253,7 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
app(r, ~".Sup")
t = skipTypes(t.sons[0], typedescInst)
if isObjLackingTypeField(t):
globalError(x.info, errGenerated,
globalError(x.info, errGenerated,
"no 'of' operator available for pure objects")
if nilCheck != nil:
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)
case t.kind
of tyInt..tyInt64, tyUInt..tyUInt64:
putIntoDest(p, d, e.typ,
putIntoDest(p, d, e.typ,
ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)]))
of tyFloat..tyFloat128:
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:
putIntoDest(p, b, e.typ, ropef("$1, $1Len0", [rdLoc(a)]))
of tyString, tySequence:
putIntoDest(p, b, e.typ,
putIntoDest(p, b, e.typ,
ropef("$1->data, $1->$2", [rdLoc(a), lenField(p)]))
of tyArray, tyArrayConstr:
putIntoDest(p, b, e.typ,
ropef("$1, $2", [rdLoc(a), toRope(lengthOrd(a.t))]))
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),
genTypeInfo(p.module, elemType(t))]))
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
# so, we skip the unnecessary range check: This is a semantical extension
# 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]
else:
e.sons[2]
@ -1518,7 +1513,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
initLocExpr(p, e.sons[2], b)
if d.k == locNone: getTemp(p, a.t, d)
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", [
rdLoc(i), toRope(size), rdLoc(d), rdLoc(a), rdLoc(b),
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) =
const floatTypes = {tyFloat..tyFloat128}
let
let
destt = skipTypes(e.typ, abstractRange)
srct = skipTypes(e.sons[1].typ, abstractRange)
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 mGetTypeInfo: genGetTypeInfo(p, e, d)
of mSwap: genSwap(p, e, d)
of mUnaryLt:
of mUnaryLt:
if optOverflowCheck notin p.options: unaryExpr(p, e, d, "($1 - 1)")
else: unaryExpr(p, e, d, "#subInt($1, 1)")
of mPred:
@ -1830,10 +1825,10 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
proc isConstClosure(n: PNode): bool {.inline.} =
result = n.sons[0].kind == nkSym and isRoutine(n.sons[0].sym) and
n.sons[1].kind == nkNilLit
proc genClosure(p: BProc, n: PNode, d: var TLoc) =
assert n.kind == nkClosure
if isConstClosure(n):
inc(p.labels)
var tmp = con("LOC", toRope(p.labels))
@ -1920,7 +1915,7 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) =
if isRef:
# 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
# (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
# this by ensuring the destination is also a pointer:
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
var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId)
var tmp = con("TMP", toRope(id))
if id == gBackendId:
# expression not found in the cache:
inc(gBackendId)
appf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n",
[getTypeDesc(p.module, t), tmp, genConstExpr(p, n)])
if d.k == locNone:
fillLoc(d, locData, t, tmp, OnHeap)
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
if sfThread in sym.flags:
accessThreadLocalVar(p, sym)
if emulatedThreadVars():
if emulatedThreadVars():
putIntoDest(p, d, sym.loc.t, con("NimTV->", sym.loc.r))
else:
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
#debug p.prc.typ.n
#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)
else: internalError(n.info, "expr(" & $sym.kind & "); unknown symbol")
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
# separate compilation:
genTypeSection(p.module, n)
of nkCommentStmt, nkIteratorDef, nkIncludeStmt,
nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
nkFromStmt, nkTemplateDef, nkMacroDef:
of nkCommentStmt, nkIteratorDef, nkIncludeStmt,
nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
nkFromStmt, nkTemplateDef, nkMacroDef:
discard
of nkPragma: genPragma(p, n)
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
({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or
(sfExportc in prc.flags and lfExportLib in prc.loc.flags) or
(prc.kind == skMethod):
# we have not only the header:
(prc.kind == skMethod):
# we have not only the header:
if prc.getBody.kind != nkEmpty or lfDynamicLib in prc.loc.flags:
genProc(p.module, prc)
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 =
var data = ropef("{{$1, $1}", n.len.toRope)
if n.len > 0:
if n.len > 0:
# array part needs extra curlies:
data.app(", {")
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("}")
data.app("}")
inc(gBackendId)
result = con("CNSTSEQ", gBackendId.toRope)
appcg(p.module, cfsData,
"NIM_CONST struct {$n" &
"NIM_CONST struct {$n" &
" #TGenericSeq Sup;$n" &
" $1 data[$2];$n" &
" $1 data[$2];$n" &
"} $3 = $4;$n", [
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.
import
ast, astalgo, ropes, lists, hashes, strutils, types, msgs, wordrecg,
import
ast, astalgo, ropes, lists, hashes, strutils, types, msgs, wordrecg,
platform, trees
proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
case n.kind
of nkStmtList:
for i in 0 .. < n.len:
of nkStmtList:
for i in 0 .. < n.len:
result = getPragmaStmt(n[i], w)
if result != nil: break
of nkPragma:
for i in 0 .. < n.len:
for i in 0 .. < n.len:
if whichPragma(n[i]) == w: return n[i]
else: discard
proc stmtsContainPragma*(n: PNode, w: TSpecialWord): bool =
result = getPragmaStmt(n, w) != nil
proc hashString*(s: string): BiggestInt =
proc hashString*(s: string): BiggestInt =
# has to be the same algorithm as system.hashString!
if CPU[targetCPU].bit == 64:
if CPU[targetCPU].bit == 64:
# we have to use the same bitwidth
# as the target CPU
var b = 0'i64
for i in countup(0, len(s) - 1):
for i in countup(0, len(s) - 1):
b = b +% ord(s[i])
b = b +% `shl`(b, 10)
b = b xor `shr`(b, 6)
@ -41,9 +41,9 @@ proc hashString*(s: string): BiggestInt =
b = b xor `shr`(b, 11)
b = b +% `shl`(b, 15)
result = b
else:
else:
var a = 0'i32
for i in countup(0, len(s) - 1):
for i in countup(0, len(s) - 1):
a = a +% ord(s[i]).int32
a = a +% `shl`(a, 10'i32)
a = a xor `shr`(a, 6'i32)
@ -52,11 +52,11 @@ proc hashString*(s: string): BiggestInt =
a = a +% `shl`(a, 15'i32)
result = a
var
var
gTypeTable: array[TTypeKind, TIdTable]
gCanonicalTypes: array[TTypeKind, PType]
proc initTypeTables() =
proc initTypeTables() =
for i in countup(low(TTypeKind), high(TTypeKind)): initIdTable(gTypeTable[i])
proc resetCaches* =
@ -67,7 +67,7 @@ proc resetCaches* =
when false:
proc echoStats*() =
for i in countup(low(TTypeKind), high(TTypeKind)):
for i in countup(low(TTypeKind), high(TTypeKind)):
echo i, " ", gTypeTable[i].counter
proc slowSearch(key: PType; k: TTypeKind): PType =
@ -78,28 +78,28 @@ proc slowSearch(key: PType; k: TTypeKind): PType =
if idTableHasObjectAsKey(gTypeTable[k], key): return key
for h in countup(0, high(gTypeTable[k].data)):
var t = PType(gTypeTable[k].data[h].key)
if t != nil and sameBackendType(t, key):
if t != nil and sameBackendType(t, key):
return t
idTablePut(gTypeTable[k], key, key)
result = key
proc getUniqueType*(key: PType): PType =
proc getUniqueType*(key: PType): PType =
# this is a hotspot in the compiler!
if key == nil: return
if key == nil: return
var k = key.kind
case k
of tyBool, tyChar, tyInt..tyUInt64:
# no canonicalization for integral types, so that e.g. ``pid_t`` is
# produced instead of ``NI``.
result = key
of tyEmpty, tyNil, tyExpr, tyStmt, tyPointer, tyString,
of tyEmpty, tyNil, tyExpr, tyStmt, tyPointer, tyString,
tyCString, tyNone, tyBigNum:
result = gCanonicalTypes[k]
if result == nil:
gCanonicalTypes[k] = key
result = key
of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr, tyFieldAccessor:
internalError("GetUniqueType")
internalError("getUniqueType")
of tyDistinct:
if key.deepCopy != nil: result = key
else: result = getUniqueType(lastSon(key))
@ -127,21 +127,21 @@ proc getUniqueType*(key: PType): PType =
if tfFromGeneric notin key.flags:
# fast case; lookup per id suffices:
result = PType(idTableGet(gTypeTable[k], key))
if result == nil:
if result == nil:
idTablePut(gTypeTable[k], key, key)
result = key
else:
# ugly slow case: need to compare by structure
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)
if t != nil and sameType(t, key):
if t != nil and sameBackendType(t, key):
return t
idTablePut(gTypeTable[k], key, key)
result = key
result = key
of tyEnum:
result = PType(idTableGet(gTypeTable[k], key))
if result == nil:
if result == nil:
idTablePut(gTypeTable[k], key, key)
result = key
of tyProc:
@ -151,16 +151,16 @@ proc getUniqueType*(key: PType): PType =
# ugh, we need the canon here:
result = slowSearch(key, k)
proc tableGetType*(tab: TIdTable, key: PType): RootRef =
proc tableGetType*(tab: TIdTable, key: PType): RootRef =
# returns nil if we need to declare this type
result = idTableGet(tab, key)
if (result == nil) and (tab.counter > 0):
if (result == nil) and (tab.counter > 0):
# we have to do a slow linear search because types may need
# to be compared by their structure:
for h in countup(0, high(tab.data)):
for h in countup(0, high(tab.data)):
var t = PType(tab.data[h].key)
if t != nil:
if sameType(t, key):
if t != nil:
if sameType(t, key):
return tab.data[h].val
proc makeSingleLineCString*(s: string): string =
@ -193,16 +193,16 @@ proc mangle*(name: string): string =
else:
add(result, "HEX" & toHex(ord(c), 2))
proc makeLLVMString*(s: string): PRope =
proc makeLLVMString*(s: string): PRope =
const MaxLineLength = 64
result = nil
var res = "c\""
for i in countup(0, len(s) - 1):
if (i + 1) mod MaxLineLength == 0:
for i in countup(0, len(s) - 1):
if (i + 1) mod MaxLineLength == 0:
app(result, toRope(res))
setLen(res, 0)
case s[i]
of '\0'..'\x1F', '\x80'..'\xFF', '\"', '\\':
of '\0'..'\x1F', '\x80'..'\xFF', '\"', '\\':
add(res, '\\')
add(res, toHex(ord(s[i]), 2))
else: add(res, s[i])

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -176,7 +176,7 @@ proc addInterfaceDeclAux(c: PContext, sym: PSym) =
if sfExported in sym.flags:
# add to interface:
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) =
addDeclAt(scope, sym)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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]
else: illFormedAst(n)
addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c))
of nkObjectTy, nkTupleTy:
of nkObjectTy, nkTupleTy, nkTupleClassTy:
discard
of nkFormalParams:
checkMinSonsLen(n, 1)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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
result.add(',' & typeStr)
of nkTupleTy:
assert len(n) > 0
result = "tuple["
for i in 0 .. <len(n): result.add(renderType(n[i]) & ',')
result[<len(result)] = ']'

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -60,7 +60,7 @@ type
nnkStmtListType, nnkBlockType,
nnkWith, nnkWithout,
nnkTypeOfExpr, nnkObjectTy,
nnkTupleTy, nnkTypeClassTy, nnkStaticTy,
nnkTupleTy, nnkTupleClassTy, nnkTypeClassTy, nnkStaticTy,
nnkRecList, nnkRecCase, nnkRecWhen,
nnkRefTy, nnkPtrTy, nnkVarTy,
nnkConstTy, nnkMutableTy,
@ -343,7 +343,7 @@ proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =
## checks that `n` is of kind `k`. If this is not the case,
## compilation aborts with an error message. This is useful for writing
## 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.} =
## 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,
nnkCallStrLit, nnkHiddenCallConv}
from strutils import cmpIgnoreStyle, format
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()];
body: PNimrodNode = newStmtList(), procType = nnkProcDef): PNimrodNode {.compileTime.} =
@ -654,7 +652,7 @@ proc `pragma=`*(someProc: PNimrodNode; val: PNimrodNode){.compileTime.}=
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.} =
case someProc.kind:
@ -776,6 +774,22 @@ proc copy*(node: PNimrodNode): PNimrodNode {.compileTime.} =
## An alias for 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
## Check if two idents are identical.

View file

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

View file

@ -8,16 +8,16 @@
#
## This module contains code for reading from `stdin`:idx:. On UNIX the GNU
## readline library is wrapped and set up to provide default key bindings
## readline library is wrapped and set up to provide default key bindings
## (e.g. you can navigate with the arrow keys). On Windows ``system.readLine``
## is used. This suffices because Windows' console already provides the
## is used. This suffices because Windows' console already provides the
## wanted functionality.
{.deadCodeElim: on.}
when defined(Windows):
proc readLineFromStdin*(prompt: string): TaintedString {.
tags: [ReadIOEffect, WriteIOEffect].} =
tags: [ReadIOEffect, WriteIOEffect].} =
## Reads a line from stdin.
stdout.write(prompt)
result = readLine(stdin)
@ -33,27 +33,71 @@ when defined(Windows):
stdout.write(prompt)
result = readLine(stdin, line)
import winlean
const
VK_SHIFT* = 16
VK_CONTROL* = 17
VK_MENU* = 18
KEY_EVENT* = 1
type
KEY_EVENT_RECORD = object
bKeyDown: WinBool
wRepeatCount: uint16
wVirtualKeyCode: uint16
wVirtualScanCode: uint16
unicodeChar: uint16
dwControlKeyState: uint32
INPUT_RECORD = object
eventType*: int16
reserved*: int16
event*: KEY_EVENT_RECORD
safetyBuffer: array[0..5, DWORD]
proc readConsoleInputW*(hConsoleInput: THANDLE, lpBuffer: var INPUTRECORD,
nLength: uint32,
lpNumberOfEventsRead: var uint32): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "ReadConsoleInputW".}
proc getch(): uint16 =
let hStdin = getStdHandle(STD_INPUT_HANDLE)
var
irInputRecord: INPUT_RECORD
dwEventsRead: uint32
while readConsoleInputW(hStdin, irInputRecord, 1, dwEventsRead) != 0:
if irInputRecord.eventType == KEY_EVENT and
irInputRecord.event.wVirtualKeyCode notin {VK_SHIFT, VK_MENU, VK_CONTROL}:
result = irInputRecord.event.unicodeChar
discard readConsoleInputW(hStdin, irInputRecord, 1, dwEventsRead)
return result
from unicode import toUTF8, Rune, runeLenAt
proc readPasswordFromStdin*(prompt: string, password: var TaintedString):
bool {.tags: [ReadIOEffect, WriteIOEffect].} =
## Reads a `password` from stdin without printing it. `password` must not
## be ``nil``! Returns ``false`` if the end of the file has been reached,
## ``true`` otherwise.
proc getch(): cint {.header: "<conio.h>", importc: "_getch".}
password.setLen(0)
var c: char
stdout.write(prompt)
while true:
c = getch().char
case c
let c = getch()
case c.char
of '\r', chr(0xA):
break
of '\b':
password.setLen(password.len - 1)
# ensure we delete the whole UTF-8 character:
var i = 0
var x = 1
while i < password.len:
x = runeLenAt(password, i)
inc i, x
password.setLen(password.len - x)
else:
password.add(c)
password.add(toUTF8(c.Rune))
stdout.write "\n"
# TODO: How to detect EOF on Windows?
else:
import readline, history, termios, unsigned

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -47,6 +47,24 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
result[i] = itm
inc(i)
proc repeat*[T](s: seq[T], n: Natural): seq[T] =
## Returns a new sequence with the items of `s` repeated `n` times.
##
## Example:
##
## .. code-block:
##
## let
## s = @[1, 2, 3]
## total = s.repeat(3)
## assert total == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
result = newSeq[T](n * s.len)
var o = 0
for x in 1..n:
for e in s:
result[o] = e
inc o
proc deduplicate*[T](seq1: seq[T]): seq[T] =
## Returns a new sequence without duplicates.
##
@ -86,7 +104,7 @@ proc zip*[S, T](seq1: seq[S], seq2: seq[T]): seq[tuple[a: S, b: T]] =
newSeq(result, m)
for i in 0 .. m-1: result[i] = (seq1[i], seq2[i])
proc distribute*[T](s: seq[T], num: int, spread = true): seq[seq[T]] =
proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
## Splits and distributes a sequence `s` into `num` sub sequences.
##
## Returns a sequence of `num` sequences. For some input values this is the
@ -113,11 +131,12 @@ proc distribute*[T](s: seq[T], num: int, spread = true): seq[seq[T]] =
## assert numbers.distribute(6)[0] == @[1, 2]
## assert numbers.distribute(6)[5] == @[7]
assert(not s.isNil, "`s` can't be nil")
assert(num > 0, "`num` has to be greater than zero")
if num < 2:
result = @[s]
return
let num = int(num) # XXX probably only needed because of .. bug
# Create the result and calculate the stride size and the remainder if any.
result = newSeq[seq[T]](num)
var
@ -587,4 +606,14 @@ when isMainModule:
seq2D[0][1] = true
doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]]
block: # repeat tests
let
a = @[1, 2, 3]
b: seq[int] = @[]
doAssert a.repeat(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
doAssert a.repeat(0) == @[]
#doAssert a.repeat(-1) == @[] # will not compile!
doAssert b.repeat(3) == @[]
echo "Finished doc tests"

View file

@ -196,7 +196,11 @@ proc mget*[A, B](t: var Table[A, B], key: A): var B =
var hc: THash
var index = rawGet(t, key, hc)
if index >= 0: result = t.data[index].val
else: raise newException(KeyError, "key not found: " & $key)
else:
when compiles($key):
raise newException(KeyError, "key not found: " & $key)
else:
raise newException(KeyError, "key not found")
iterator allValues*[A, B](t: Table[A, B]; key: A): B =
## iterates over any value in the table `t` that belongs to the given `key`.

View file

@ -365,9 +365,9 @@ proc polar*(z: Complex): tuple[r, phi: float] =
result.phi = phase(z)
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.im = sin(phi)
result.im = r * sin(phi)
proc `$`*(z: Complex): string =
@ -438,5 +438,6 @@ when isMainModule:
assert( arccoth(a) =~ arctanh(1/a) )
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) )

View file

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

View file

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

View file

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

View file

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

View file

@ -81,6 +81,23 @@ type
TReadLineResult: ReadLineResult, TSOBool: SOBool, PSocket: Socket,
TSocketImpl: SocketImpl].}
type
IpAddressFamily* {.pure.} = enum ## Describes the type of an IP address
IPv6, ## IPv6 address
IPv4 ## IPv4 address
TIpAddress* = object ## stores an arbitrary IP address
case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6)
of IpAddressFamily.IPv6:
address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in
## case of IPv6
of IpAddressFamily.IPv4:
address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in
## case of IPv4
proc isIpAddress*(address_str: string): bool {.tags: [].}
proc parseIpAddress*(address_str: string): TIpAddress
proc isDisconnectionError*(flags: set[SocketFlag],
lastError: OSErrorCode): bool =
## Determines whether ``lastError`` is a disconnection error. Only does this
@ -511,6 +528,12 @@ proc connect*(socket: Socket, address: string, port = Port(0),
when defined(ssl):
if socket.isSSL:
# RFC3546 for SNI specifies that IP addresses are not allowed.
if not isIpAddress(address):
# Discard result in case OpenSSL version doesn't support SNI, or we're
# not using TLSv1+
discard SSL_set_tlsext_host_name(socket.sslHandle, address)
let ret = SSLConnect(socket.sslHandle)
socketError(socket, ret)
@ -969,20 +992,6 @@ proc isSsl*(socket: Socket): bool =
proc getFd*(socket: Socket): SocketHandle = return socket.fd
## Returns the socket's file descriptor
type
IpAddressFamily* {.pure.} = enum ## Describes the type of an IP address
IPv6, ## IPv6 address
IPv4 ## IPv4 address
TIpAddress* = object ## stores an arbitrary IP address
case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6)
of IpAddressFamily.IPv6:
address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in
## case of IPv6
of IpAddressFamily.IPv4:
address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in
## case of IPv4
proc IPv4_any*(): TIpAddress =
## Returns the IPv4 any address, which can be used to listen on all available
## network adapters
@ -1241,7 +1250,7 @@ proc parseIPv6Address(address_str: string): TIpAddress =
raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups")
proc parseIpAddress*(address_str: string): TIpAddress =
proc parseIpAddress(address_str: string): TIpAddress =
## Parses an IP address
## Raises EInvalidValue on error
if address_str == nil:
@ -1250,3 +1259,13 @@ proc parseIpAddress*(address_str: string): TIpAddress =
return parseIPv6Address(address_str)
else:
return parseIPv4Address(address_str)
proc isIpAddress(address_str: string): bool =
## Checks if a string is an IP address
## Returns true if it is, false otherwise
try:
discard parseIpAddress(address_str)
except ValueError:
return false
return true

View file

@ -11,9 +11,12 @@
## It supports one convenience iterator over all command line options and some
## lower-level features.
##
## **Deprecated since version 0.9.3:** Use the `parseopt2 <parseopt2.html>`_
## module instead as this version has issues with spaces in arguments.
{.deprecated.}
## Supported syntax:
##
## 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.}
include "system/inclrtl"

View file

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

View file

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

View file

@ -798,6 +798,22 @@ proc zunionstore*(r: Redis, destination: string, numkeys: string,
return r.readInteger()
# HyperLogLog
proc pfadd*(r: Redis, key: string, elements: varargs[string]): RedisInteger =
## Add variable number of elements into special 'HyperLogLog' set type
r.sendCommand("PFADD", key, elements)
return r.readInteger()
proc pfcount*(r: Redis, key: string): RedisInteger =
## Count approximate number of elements in 'HyperLogLog'
r.sendCommand("PFCOUNT", key)
return r.readInteger()
proc pfmerge*(r: Redis, destination: string, sources: varargs[string]) =
## Merge several source HyperLogLog's into one specified by destKey
r.sendCommand("PFMERGE", destination, sources)
raiseNoOK(r.readStatus(), r.pipeline.enabled)
# Pub/Sub

View file

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

View file

@ -499,26 +499,47 @@ proc parseEnum*[T: enum](s: string, default: T): T =
return e
result = default
proc repeatChar*(count: int, c: char = ' '): string {.noSideEffect,
proc repeat*(c: char, count: int): string {.noSideEffect,
rtl, extern: "nsuRepeatChar".} =
## Returns a string of length `count` consisting only of
## the character `c`. You can use this proc to left align strings. Example:
##
## .. 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
## width = 15
## text1 = "Hello user!"
## text2 = "This is a very long string"
## echo text1 & repeatChar(max(0, width - text1.len)) & "|"
## echo text2 & repeatChar(max(0, width - text2.len)) & "|"
result = newString(count)
for i in 0..count-1: result[i] = c
## echo text1 & spaces(max(0, width - text1.len)) & "|"
## echo text2 & spaces(max(0, width - text2.len)) & "|"
proc repeatStr*(count: int, s: string): string {.noSideEffect,
rtl, extern: "nsuRepeatStr".} =
## Returns `s` concatenated `count` times.
result = newStringOfCap(count*s.len)
for i in 0..count-1: result.add(s)
proc repeatChar*(count: int, c: char = ' '): string {.deprecated.} = repeat(c, count)
## deprecated: use repeat() or spaces()
proc repeatStr*(count: int, s: string): string {.deprecated.} = repeat(s, count)
## deprecated: use repeat(string, count) or string.repeat(count)
proc align*(s: string, count: int, padding = ' '): string {.
noSideEffect, rtl, extern: "nsuAlignString".} =

View file

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

View file

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

View file

@ -118,21 +118,35 @@ proc toUTF8*(c: Rune): string {.rtl, extern: "nuc$1".} =
elif i <=% 0x07FF:
result = newString(2)
result[0] = chr((i shr 6) or 0b110_00000)
result[1] = chr((i and ones(6)) or 0b10_000000)
result[1] = chr((i and ones(6)) or 0b10_0000_00)
elif i <=% 0xFFFF:
result = newString(3)
result[0] = chr(i shr 12 or 0b1110_0000)
result[1] = chr(i shr 6 and ones(6) or 0b10_0000_00)
result[2] = chr(i and ones(6) or 0b10_0000_00)
elif i <=% 0x0010FFFF:
elif i <=% 0x001FFFFF:
result = newString(4)
result[0] = chr(i shr 18 or 0b1111_0000)
result[1] = chr(i shr 12 and ones(6) or 0b10_0000_00)
result[2] = chr(i shr 6 and ones(6) or 0b10_0000_00)
result[3] = chr(i and ones(6) or 0b10_0000_00)
elif i <=% 0x03FFFFFF:
result = newString(5)
result[0] = chr(i shr 24 or 0b111110_00)
result[1] = chr(i shr 18 and ones(6) or 0b10_0000_00)
result[2] = chr(i shr 12 and ones(6) or 0b10_0000_00)
result[3] = chr(i shr 6 and ones(6) or 0b10_0000_00)
result[4] = chr(i and ones(6) or 0b10_0000_00)
elif i <=% 0x7FFFFFFF:
result = newString(6)
result[0] = chr(i shr 30 or 0b1111110_0)
result[1] = chr(i shr 24 and ones(6) or 0b10_0000_00)
result[2] = chr(i shr 18 and ones(6) or 0b10_0000_00)
result[3] = chr(i shr 12 and ones(6) or 0b10_0000_00)
result[4] = chr(i shr 6 and ones(6) or 0b10_0000_00)
result[5] = chr(i and ones(6) or 0b10_0000_00)
else:
result = newString(1)
result[0] = chr(i)
discard # error, exception?
proc `$`*(rune: Rune): string =
## converts a rune to a string

View file

@ -285,6 +285,16 @@ proc `$`*(u: Uri): string =
result.add(u.anchor)
when isMainModule:
block:
let str = "http://localhost"
let test = parseUri(str)
doAssert test.path == ""
block:
let str = "http://localhost/"
let test = parseUri(str)
doAssert test.path == "/"
block:
let str = "http://localhost:8080/test"
let test = parseUri(str)

View file

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

View file

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

View file

@ -34,9 +34,9 @@ proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode) {.benign.} =
proc copyDeepString(src: NimString): NimString {.inline.} =
if src != nil:
result = rawNewString(src.space)
result = rawNewStringNoInit(src.len)
result.len = src.len
c_memcpy(result.data, src.data, (src.len + 1) * sizeof(char))
c_memcpy(result.data, src.data, src.len + 1)
proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) =
var

View file

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

View file

@ -593,7 +593,7 @@ proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} =
return
add(gch.zct, res)
proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap, rc1: bool): pointer =
proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap, rc1 = false): pointer =
# generates a new object and sets its reference counter to 0
acquire(gch)
sysAssert(allocInv(gch.region), "rawNewObj begin")

View file

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

View file

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

View file

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

View file

@ -145,8 +145,8 @@ proc readAllFile(file: File): string =
proc readAll(file: File): TaintedString =
# Separate handling needed because we need to buffer when we
# don't know the overall length of the File.
var len = rawFileSize(file)
if len >= 0:
let len = if file != stdin: rawFileSize(file) else: -1
if len > 0:
result = readAllFile(file, len).TaintedString
else:
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 == nil or b == nil: return false
return a.len == b.len and
c_memcmp(a.data, b.data, a.len * sizeof(char)) == 0'i32
c_memcmp(a.data, b.data, a.len) == 0'i32
when declared(allocAtomic):
template allocStr(size: expr): expr =
cast[NimString](allocAtomic(size))
template allocStrNoInit(size: expr): expr =
cast[NimString](boehmAllocAtomic(size))
else:
template allocStr(size: expr): expr =
cast[NimString](newObj(addr(strDesc), size))
template allocStrNoInit(size: expr): expr =
cast[NimString](newObjNoInit(addr(strDesc), size))
proc rawNewStringNoInit(space: int): NimString {.compilerProc.} =
var s = space
if s < 7: s = 7
result = allocStrNoInit(sizeof(TGenericSeq) + s + 1)
result.reserved = s
proc rawNewString(space: int): NimString {.compilerProc.} =
var s = space
if s < 8: s = 7
if s < 7: s = 7
result = allocStr(sizeof(TGenericSeq) + s + 1)
result.reserved = s
@ -53,10 +65,10 @@ proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.} =
var start = max(start, 0)
var len = min(last, s.len-1) - start + 1
if len > 0:
result = rawNewString(len)
result = rawNewStringNoInit(len)
result.len = len
c_memcpy(result.data, addr(s.data[start]), len * sizeof(char))
#result.data[len] = '\0'
c_memcpy(result.data, addr(s.data[start]), len)
result.data[len] = '\0'
else:
result = rawNewString(len)
@ -64,10 +76,9 @@ proc copyStr(s: NimString, start: int): NimString {.compilerProc.} =
result = copyStrLast(s, start, s.len-1)
proc toNimStr(str: cstring, len: int): NimString {.compilerProc.} =
result = rawNewString(len)
result = rawNewStringNoInit(len)
result.len = len
c_memcpy(result.data, str, (len+1) * sizeof(char))
#result.data[len] = '\0' # readline relies on this!
c_memcpy(result.data, str, len + 1)
proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} =
result = toNimStr(str, c_strlen(str))
@ -77,23 +88,24 @@ proc copyString(src: NimString): NimString {.compilerRtl.} =
if (src.reserved and seqShallowFlag) != 0:
result = src
else:
result = rawNewString(src.space)
result = rawNewStringNoInit(src.len)
result.len = src.len
c_memcpy(result.data, src.data, (src.len + 1) * sizeof(char))
c_memcpy(result.data, src.data, src.len + 1)
proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
if src != nil:
var s = src.space
if s < 8: s = 7
when declared(newObjRC1):
var s = src.len
if s < 7: s = 7
result = cast[NimString](newObjRC1(addr(strDesc), sizeof(TGenericSeq) +
s+1))
result.reserved = s
else:
result = allocStr(sizeof(TGenericSeq) + s + 1)
result.reserved = s
result = rawNewStringNoInit(src.len)
result.len = src.len
c_memcpy(result.data, src.data, src.len + 1)
proc hashString(s: string): int {.compilerproc.} =
# the compiler needs exactly the same hash function!
# this used to be used for efficient generation of string case statements
@ -113,7 +125,7 @@ proc addChar(s: NimString, c: char): NimString =
if result.len >= result.space:
result.reserved = resize(result.space)
result = cast[NimString](growObj(result,
sizeof(TGenericSeq) + (result.reserved+1) * sizeof(char)))
sizeof(TGenericSeq) + result.reserved + 1))
result.data[result.len] = c
result.data[result.len+1] = '\0'
inc(result.len)
@ -157,7 +169,7 @@ proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} =
result = cast[NimString](growObj(dest, sizeof(TGenericSeq) + sp + 1))
result.reserved = sp
#result = rawNewString(sp)
#copyMem(result, dest, dest.len * sizeof(char) + sizeof(TGenericSeq))
#copyMem(result, dest, dest.len + sizeof(TGenericSeq))
# DO NOT UPDATE LEN YET: dest.len = newLen
proc appendString(dest, src: NimString) {.compilerproc, inline.} =
@ -203,7 +215,7 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
GenericSeqSize))
elif newLen < result.len:
# we need to decref here, otherwise the GC leaks!
when not defined(boehmGC) and not defined(nogc) and
when not defined(boehmGC) and not defined(nogc) and
not defined(gcMarkAndSweep):
when compileOption("gc", "v2"):
for i in newLen..result.len-1:
@ -220,7 +232,7 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +%
GenericSeqSize +% (i*%elemSize)),
extGetCellType(result).base, waZctDecRef)
# XXX: zeroing out the memory can still result in crashes if a wiped-out
# cell is aliased by another pointer (ie proc parameter or a let variable).
# This is a tought problem, because even if we don't zeroMem here, in the
@ -258,13 +270,23 @@ proc nimFloatToStr(f: float): string {.compilerproc.} =
if buf[i] == ',':
buf[i] = '.'
hasDot = true
elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
hasDot = true
if not hasDot:
buf[n] = '.'
buf[n+1] = '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,
header: "<stdlib.h>", noSideEffect.}
@ -299,7 +321,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
template addToBuf(c) =
if ti < t.high:
t[ti] = c; inc(ti)
# Sign?
if s[i] == '+' or s[i] == '-':
if s[i] == '-':
@ -320,7 +342,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
if s[i] == 'I' or s[i] == 'i':
if s[i+1] == 'N' or s[i+1] == 'n':
if s[i+2] == 'F' or s[i+2] == 'f':
if s[i+3] notin IdentChars:
if s[i+3] notin IdentChars:
number = Inf*sign
return i+3 - start
return 0

View file

@ -50,7 +50,7 @@ when useWinVersion:
from winlean import SocketHandle
else:
const
versions = "(.10|.1.0.1|.1.0.0|.0.9.9|.0.9.8|.0.9.7|.0.9.6|.0.9.5|.0.9.4)"
versions = "(.10|.1.0.1|.1.0.0|.0.9.9|.0.9.8)"
when defined(macosx):
const
DLLSSLName = "libssl" & versions & ".dylib"
@ -141,6 +141,14 @@ const
SSL_CTRL_GET_MAX_CERT_LIST* = 50
SSL_CTRL_SET_MAX_CERT_LIST* = 51 #* Allow SSL_write(..., n) to return r with 0 < r < n (i.e. report success
# * when just a single record has been written): *
SSL_CTRL_SET_TLSEXT_SERVERNAME_CB = 53
SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG = 54
SSL_CTRL_SET_TLSEXT_HOSTNAME = 55
TLSEXT_NAMETYPE_host_name* = 0
SSL_TLSEXT_ERR_OK* = 0
SSL_TLSEXT_ERR_ALERT_WARNING* = 1
SSL_TLSEXT_ERR_ALERT_FATAL* = 2
SSL_TLSEXT_ERR_NOACK* = 3
SSL_MODE_ENABLE_PARTIAL_WRITE* = 1 #* Make it possible to retry SSL_write() with changed buffer location
# * (buffer contents must stay the same!); this is not the default to avoid
# * the misconception that non-blocking SSL_write() behaves like
@ -296,9 +304,41 @@ proc CRYPTO_malloc_init*() =
proc SSL_CTX_ctrl*(ctx: SslCtx, cmd: cInt, larg: int, parg: pointer): int{.
cdecl, dynlib: DLLSSLName, importc.}
proc SSL_CTX_callback_ctrl(ctx: SslCtx, typ: cInt, fp: PFunction): int{.
cdecl, dynlib: DLLSSLName, importc.}
proc SSLCTXSetMode*(ctx: SslCtx, mode: int): int =
result = SSL_CTX_ctrl(ctx, SSL_CTRL_MODE, mode, nil)
proc SSL_ctrl*(ssl: SslPtr, cmd: cInt, larg: int, parg: pointer): int{.
cdecl, dynlib: DLLSSLName, importc.}
proc SSL_set_tlsext_host_name*(ssl: SslPtr, name: cstring): int =
result = SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, name)
## Set the SNI server name extension to be used in a client hello.
## Returns 1 if SNI was set, 0 if current SSL configuration doesn't support SNI.
proc SSL_get_servername*(ssl: SslPtr, typ: cInt = TLSEXT_NAMETYPE_host_name): cstring {.cdecl, dynlib: DLLSSLName, importc.}
## Retrieve the server name requested in the client hello. This can be used
## in the callback set in `SSL_CTX_set_tlsext_servername_callback` to
## implement virtual hosting. May return `nil`.
proc SSL_CTX_set_tlsext_servername_callback*(ctx: SslCtx, cb: proc(ssl: SslPtr, cb_id: int, arg: pointer): int {.cdecl.}): int =
## Set the callback to be used on listening SSL connections when the client hello is received.
##
## The callback should return one of:
## * SSL_TLSEXT_ERR_OK
## * SSL_TLSEXT_ERR_ALERT_WARNING
## * SSL_TLSEXT_ERR_ALERT_FATAL
## * SSL_TLSEXT_ERR_NOACK
result = SSL_CTX_callback_ctrl(ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, cast[PFunction](cb))
proc SSL_CTX_set_tlsext_servername_arg*(ctx: SslCtx, arg: pointer): int =
## Set the pointer to be used in the callback registered to ``SSL_CTX_set_tlsext_servername_callback``.
result = SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG, 0, arg)
proc bioNew*(b: PBIO_METHOD): BIO{.cdecl, dynlib: DLLUtilName, importc: "BIO_new".}
proc bioFreeAll*(b: BIO){.cdecl, dynlib: DLLUtilName, importc: "BIO_free_all".}
proc bioSMem*(): PBIO_METHOD{.cdecl, dynlib: DLLUtilName, importc: "BIO_s_mem".}
@ -341,8 +381,6 @@ else:
dynlib: DLLSSLName, importc.}
proc SslSetFd*(s: PSSL, fd: cInt): cInt{.cdecl, dynlib: DLLSSLName, importc.}
proc SslCtrl*(ssl: PSSL, cmd: cInt, larg: int, parg: Pointer): int{.cdecl,
dynlib: DLLSSLName, importc.}
proc SslCTXCtrl*(ctx: PSSL_CTX, cmd: cInt, larg: int, parg: Pointer): int{.
cdecl, dynlib: DLLSSLName, importc.}

View file

@ -1,5 +1,5 @@
# Nim Compiler
This repo contains the Nim compiler, Nim's stdlib, tools and
This repo contains the Nim compiler, Nim's stdlib, tools and
documentation.
## Compiling
@ -8,17 +8,13 @@ the Nim compiler itself is written in the Nim programming language
the C source of an older version of the compiler are needed to bootstrap the
latest version. The C sources are available in a separate repo [here](http://github.com/nim-lang/csources).
Pre-compiled snapshots of the compiler are also available on
[Nimbuild](http://build.nim-lang.org/). Your platform however may not
currently be built for.
The compiler currently supports the following platform and architecture
The compiler currently supports the following platform and architecture
combinations:
* Windows (Windows XP or greater) - x86 and x86_64
* Linux (most, if not all, distributions) - x86, x86_64, ppc64 and armv6l
* Mac OS X 10.04 or higher - x86, x86_64 and ppc64
In reality a lot more are supported, however they are not tested regularly.
To build from source you will need:
@ -39,8 +35,7 @@ $ bin/nim c koch
$ ./koch boot -d:release
```
``koch install [dir]`` may then be used to install Nim, or you can simply
add it to your PATH. More ``koch`` related options are documented in
``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
[doc/koch.txt](doc/koch.txt).
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).
## License
The compiler and the standard library are licensed under the MIT license,
except for some modules where the documentation suggests otherwise. This means
that you can use any license for your own programs developed with Nim,
The compiler and the standard library are licensed under the MIT license,
except for some modules where the documentation suggests otherwise. This means
that you can use any license for your own programs developed with Nim,
allowing you to create commercial applications.
Read copying.txt for more details.

View file

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

View file

@ -3,14 +3,14 @@ discard """
WARNING: false first assertion from bar
ERROR: false second assertion from bar
-1
tests/assert/tfailedassert.nim:27 false assertion from foo
tfailedassert.nim:27 false assertion from foo
'''
"""
type
TLineInfo = tuple[filename: string, line: int]
TMyError = object of E_Base
TMyError = object of Exception
lineinfo: TLineInfo
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 """
cmd: "nim cpp $target"
cmd: "nim cpp $file"
"""
import rawsockets

View file

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

View file

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

View file

@ -1,6 +1,6 @@
discard """
line: 20
errormsg: " usage of a type with a destructor in a non destructible context"
line: 23
nimout: " usage of a type with a destructor in a non destructible context"
"""
{.experimental.}
@ -19,5 +19,9 @@ proc open: TMyObj =
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 """
line: 7
errormsg: "expression 'items' cannot be called"
errormsg: "undeclared identifier: 'items'"
"""
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
proc doTrace(n: PNode[T,D], level: int) =
var space = repeatChar(2 * level)
var space = spaces(2 * level)
traceln(space)
write stdout, "node: "
if n == nil:

View file

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

View file

@ -1,6 +1,6 @@
discard """
file: "toop1.nim"
output: "in globalaux2: 10\ntotal globals: 2\nint value: 100\nstring value: second"
disabled: "true"
"""
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
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] = @[],

View file

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

View file

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

View file

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

View file

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

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